@zythum02/fello-server 0.1.39 → 0.1.42
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/out/renderer/assets/{_baseUniq-BonfEMiF.js → _baseUniq-CNYzw1DB.js} +1 -1
- package/out/renderer/assets/_baseUniq-CNYzw1DB.js.br +0 -0
- package/out/renderer/assets/_baseUniq-CNYzw1DB.js.gz +0 -0
- package/out/renderer/assets/{arc-Do8LbeMR.js → arc-vyANDg9_.js} +1 -1
- package/out/renderer/assets/arc-vyANDg9_.js.br +0 -0
- package/out/renderer/assets/arc-vyANDg9_.js.gz +0 -0
- package/out/renderer/assets/{architectureDiagram-Q4EWVU46-B8pyh3AH.js → architectureDiagram-Q4EWVU46-BzcUqnqp.js} +5 -5
- package/out/renderer/assets/architectureDiagram-Q4EWVU46-BzcUqnqp.js.br +0 -0
- package/out/renderer/assets/architectureDiagram-Q4EWVU46-BzcUqnqp.js.gz +0 -0
- package/out/renderer/assets/{blockDiagram-DXYQGD6D-DxehDaBQ.js → blockDiagram-DXYQGD6D-CTEZWa8S.js} +6 -6
- package/out/renderer/assets/blockDiagram-DXYQGD6D-CTEZWa8S.js.br +0 -0
- package/out/renderer/assets/blockDiagram-DXYQGD6D-CTEZWa8S.js.gz +0 -0
- package/out/renderer/assets/{c4Diagram-AHTNJAMY-BwVB2HtH.js → c4Diagram-AHTNJAMY-BuFQdjjc.js} +2 -2
- package/out/renderer/assets/c4Diagram-AHTNJAMY-BuFQdjjc.js.br +0 -0
- package/out/renderer/assets/{c4Diagram-AHTNJAMY-BwVB2HtH.js.gz → c4Diagram-AHTNJAMY-BuFQdjjc.js.gz} +0 -0
- package/out/renderer/assets/{channel-DoK5PQCo.js → channel-DyKDQZ32.js} +1 -1
- package/out/renderer/assets/{chunk-4BX2VUAB-B5UbKm2i.js → chunk-4BX2VUAB-BKj1YSOw.js} +1 -1
- package/out/renderer/assets/{chunk-4TB4RGXK-Mp3JyPop.js → chunk-4TB4RGXK-CUfY3XSR.js} +5 -5
- package/out/renderer/assets/chunk-4TB4RGXK-CUfY3XSR.js.br +0 -0
- package/out/renderer/assets/chunk-4TB4RGXK-CUfY3XSR.js.gz +0 -0
- package/out/renderer/assets/{chunk-55IACEB6-BpQS-lsN.js → chunk-55IACEB6-BPafcNL-.js} +1 -1
- package/out/renderer/assets/{chunk-EDXVE4YY-BQEQxqzJ.js → chunk-EDXVE4YY-DlWjafzT.js} +1 -1
- package/out/renderer/assets/chunk-EDXVE4YY-DlWjafzT.js.br +0 -0
- package/out/renderer/assets/chunk-EDXVE4YY-DlWjafzT.js.gz +0 -0
- package/out/renderer/assets/{chunk-FMBD7UC4-dCqprN1e.js → chunk-FMBD7UC4-CI2WhJKs.js} +1 -1
- package/out/renderer/assets/{chunk-OYMX7WX6-D19mZGkc.js → chunk-OYMX7WX6-1XAYgymx.js} +3 -3
- package/out/renderer/assets/chunk-OYMX7WX6-1XAYgymx.js.br +0 -0
- package/out/renderer/assets/chunk-OYMX7WX6-1XAYgymx.js.gz +0 -0
- package/out/renderer/assets/{chunk-QZHKN3VN-DzWTtEnO.js → chunk-QZHKN3VN-B2Kh1qhq.js} +1 -1
- package/out/renderer/assets/{chunk-YZCP3GAM-BEBfdXkA.js → chunk-YZCP3GAM-DKTOTV26.js} +1 -1
- package/out/renderer/assets/chunk-YZCP3GAM-DKTOTV26.js.br +0 -0
- package/out/renderer/assets/chunk-YZCP3GAM-DKTOTV26.js.gz +0 -0
- package/out/renderer/assets/{classDiagram-6PBFFD2Q-BosKSo4l.js → classDiagram-6PBFFD2Q-BSxgcC66.js} +6 -6
- package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-BosKSo4l.js → classDiagram-v2-HSJHXN6E-BSxgcC66.js} +6 -6
- package/out/renderer/assets/{clone-C281XDWZ.js → clone-B-NXg17n.js} +1 -1
- package/out/renderer/assets/{cose-bilkent-S5V4N54A-6SeriN8Z.js → cose-bilkent-S5V4N54A-Dr92qdgA.js} +1 -1
- package/out/renderer/assets/cose-bilkent-S5V4N54A-Dr92qdgA.js.br +0 -0
- package/out/renderer/assets/cose-bilkent-S5V4N54A-Dr92qdgA.js.gz +0 -0
- package/out/renderer/assets/{dagre-KV5264BT-gXq994RD.js → dagre-KV5264BT-7l16BI52.js} +6 -6
- package/out/renderer/assets/dagre-KV5264BT-7l16BI52.js.br +0 -0
- package/out/renderer/assets/dagre-KV5264BT-7l16BI52.js.gz +0 -0
- package/out/renderer/assets/{diagram-5BDNPKRD-GOebOMWt.js → diagram-5BDNPKRD-7J0KHPTw.js} +6 -6
- package/out/renderer/assets/diagram-5BDNPKRD-7J0KHPTw.js.br +0 -0
- package/out/renderer/assets/diagram-5BDNPKRD-7J0KHPTw.js.gz +0 -0
- package/out/renderer/assets/{diagram-G4DWMVQ6-BkQmAuCz.js → diagram-G4DWMVQ6-Dgk1T3Ng.js} +6 -6
- package/out/renderer/assets/diagram-G4DWMVQ6-Dgk1T3Ng.js.br +0 -0
- package/out/renderer/assets/diagram-G4DWMVQ6-Dgk1T3Ng.js.gz +0 -0
- package/out/renderer/assets/{diagram-MMDJMWI5-Q9B0Tkq3.js → diagram-MMDJMWI5-DerRkmYs.js} +5 -5
- package/out/renderer/assets/diagram-MMDJMWI5-DerRkmYs.js.br +0 -0
- package/out/renderer/assets/diagram-MMDJMWI5-DerRkmYs.js.gz +0 -0
- package/out/renderer/assets/{diagram-TYMM5635-5wNVFKxe.js → diagram-TYMM5635-DR9dcwKl.js} +5 -5
- package/out/renderer/assets/diagram-TYMM5635-DR9dcwKl.js.br +0 -0
- package/out/renderer/assets/diagram-TYMM5635-DR9dcwKl.js.gz +0 -0
- package/out/renderer/assets/docx_parser_bg-Dwb1lJCk.wasm +0 -0
- package/out/renderer/assets/{erDiagram-SMLLAGMA-DRxQnVgW.js → erDiagram-SMLLAGMA-CZ2vNtP6.js} +4 -4
- package/out/renderer/assets/erDiagram-SMLLAGMA-CZ2vNtP6.js.br +0 -0
- package/out/renderer/assets/erDiagram-SMLLAGMA-CZ2vNtP6.js.gz +0 -0
- package/out/renderer/assets/{flowDiagram-DWJPFMVM-3uIpRmli.js → flowDiagram-DWJPFMVM-DOL_mdve.js} +6 -6
- package/out/renderer/assets/flowDiagram-DWJPFMVM-DOL_mdve.js.br +0 -0
- package/out/renderer/assets/flowDiagram-DWJPFMVM-DOL_mdve.js.gz +0 -0
- package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-CFxauQFp.js → ganttDiagram-T4ZO3ILL-DXfCf0sW.js} +2 -2
- package/out/renderer/assets/ganttDiagram-T4ZO3ILL-DXfCf0sW.js.br +0 -0
- package/out/renderer/assets/ganttDiagram-T4ZO3ILL-DXfCf0sW.js.gz +0 -0
- package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-CDBCF75U.js → gitGraphDiagram-UUTBAWPF-Bi19RzUn.js} +6 -6
- package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-Bi19RzUn.js.br +0 -0
- package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-Bi19RzUn.js.gz +0 -0
- package/out/renderer/assets/{graph-B77t0Iut.js → graph-BX6gbV3Q.js} +2 -2
- package/out/renderer/assets/graph-BX6gbV3Q.js.br +0 -0
- package/out/renderer/assets/graph-BX6gbV3Q.js.gz +0 -0
- package/out/renderer/assets/{highlighted-body-OFNGDK62-BOhs9DFu.js → highlighted-body-OFNGDK62-CpZorxCd.js} +1 -1
- package/out/renderer/assets/index-BihXRHbW.css +6729 -0
- package/out/renderer/assets/index-BihXRHbW.css.br +0 -0
- package/out/renderer/assets/index-BihXRHbW.css.gz +0 -0
- package/out/renderer/assets/{index-l_WeGi7a.js → index-Ckb2j9e5.js} +49891 -24876
- package/out/renderer/assets/index-Ckb2j9e5.js.br +0 -0
- package/out/renderer/assets/index-Ckb2j9e5.js.gz +0 -0
- package/out/renderer/assets/{infoDiagram-42DDH7IO-C3TJqapp.js → infoDiagram-42DDH7IO-BB7FT1Ls.js} +4 -4
- package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-C59I-VqG.js → ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js} +1 -1
- package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js.br +0 -0
- package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js.gz +0 -0
- package/out/renderer/assets/{journeyDiagram-VCZTEJTY-B6jawzfM.js → journeyDiagram-VCZTEJTY-bhcMvreZ.js} +4 -4
- package/out/renderer/assets/journeyDiagram-VCZTEJTY-bhcMvreZ.js.br +0 -0
- package/out/renderer/assets/journeyDiagram-VCZTEJTY-bhcMvreZ.js.gz +0 -0
- package/out/renderer/assets/{kanban-definition-6JOO6SKY-BCXhVxbs.js → kanban-definition-6JOO6SKY-CRg0O3h_.js} +2 -2
- package/out/renderer/assets/kanban-definition-6JOO6SKY-CRg0O3h_.js.br +0 -0
- package/out/renderer/assets/kanban-definition-6JOO6SKY-CRg0O3h_.js.gz +0 -0
- package/out/renderer/assets/{layout-zx9noErc.js → layout-Gz3MMSfR.js} +4 -4
- package/out/renderer/assets/layout-Gz3MMSfR.js.br +0 -0
- package/out/renderer/assets/layout-Gz3MMSfR.js.gz +0 -0
- package/out/renderer/assets/{linear-Bo25ELwI.js → linear-B-XoCpT2.js} +1 -1
- package/out/renderer/assets/linear-B-XoCpT2.js.br +0 -0
- package/out/renderer/assets/linear-B-XoCpT2.js.gz +0 -0
- package/out/renderer/assets/{min-eGRLXD6u.js → min-pbiT8SH0.js} +2 -2
- package/out/renderer/assets/min-pbiT8SH0.js.br +0 -0
- package/out/renderer/assets/min-pbiT8SH0.js.gz +0 -0
- package/out/renderer/assets/{mindmap-definition-QFDTVHPH-PUpW_gKO.js → mindmap-definition-QFDTVHPH-QEcTW8Sk.js} +3 -3
- package/out/renderer/assets/mindmap-definition-QFDTVHPH-QEcTW8Sk.js.br +0 -0
- package/out/renderer/assets/mindmap-definition-QFDTVHPH-QEcTW8Sk.js.gz +0 -0
- package/out/renderer/assets/{pdf-worker-wrapper-Do-ikGHl.js → pdf-worker-wrapper-D9W0fDhT.js} +6514 -5934
- package/out/renderer/assets/pdf-worker-wrapper-D9W0fDhT.js.br +0 -0
- package/out/renderer/assets/pdf-worker-wrapper-D9W0fDhT.js.gz +0 -0
- package/out/renderer/assets/{pieDiagram-DEJITSTG-BrSaX9YD.js → pieDiagram-DEJITSTG-BdyIMG3r.js} +6 -6
- package/out/renderer/assets/pieDiagram-DEJITSTG-BdyIMG3r.js.br +0 -0
- package/out/renderer/assets/pieDiagram-DEJITSTG-BdyIMG3r.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-BL-l9r39.js +4 -0
- package/out/renderer/assets/pierre-dark-BL-l9r39.js.br +0 -0
- package/out/renderer/assets/pierre-dark-BL-l9r39.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js +4 -0
- package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js.br +0 -0
- package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js +4 -0
- package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js.br +0 -0
- package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js +4 -0
- package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js.br +0 -0
- package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js +4 -0
- package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js.br +0 -0
- package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js.gz +0 -0
- package/out/renderer/assets/pierre-light-BUzWTcjO.js +4 -0
- package/out/renderer/assets/pierre-light-BUzWTcjO.js.br +0 -0
- package/out/renderer/assets/pierre-light-BUzWTcjO.js.gz +0 -0
- package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js +4 -0
- package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js.br +0 -0
- package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js.gz +0 -0
- package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js +4 -0
- package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js.br +0 -0
- package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js.gz +0 -0
- package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js +4 -0
- package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js.br +0 -0
- package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js.gz +0 -0
- package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js +4 -0
- package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js.br +0 -0
- package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js.gz +0 -0
- package/out/renderer/assets/pptx_parser_bg-N_v237u9.wasm +0 -0
- package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-B_oUGzKN.js → quadrantDiagram-34T5L4WZ-CO63F50c.js} +2 -2
- package/out/renderer/assets/quadrantDiagram-34T5L4WZ-CO63F50c.js.br +0 -0
- package/out/renderer/assets/quadrantDiagram-34T5L4WZ-CO63F50c.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js +25 -0
- package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js.br +0 -0
- package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js +25 -0
- package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js.br +0 -0
- package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js +25 -0
- package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js.br +0 -0
- package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js.gz +0 -0
- package/out/renderer/assets/{requirementDiagram-MS252O5E-LQY3T05J.js → requirementDiagram-MS252O5E-Ch2CVgfZ.js} +3 -3
- package/out/renderer/assets/requirementDiagram-MS252O5E-Ch2CVgfZ.js.br +0 -0
- package/out/renderer/assets/requirementDiagram-MS252O5E-Ch2CVgfZ.js.gz +0 -0
- package/out/renderer/assets/{sankeyDiagram-XADWPNL6-BZrdYvkl.js → sankeyDiagram-XADWPNL6--b1mWkl5.js} +1 -1
- package/out/renderer/assets/sankeyDiagram-XADWPNL6--b1mWkl5.js.br +0 -0
- package/out/renderer/assets/sankeyDiagram-XADWPNL6--b1mWkl5.js.gz +0 -0
- package/out/renderer/assets/{sequenceDiagram-FGHM5R23-DT03DGl3.js → sequenceDiagram-FGHM5R23-CNautoFU.js} +3 -3
- package/out/renderer/assets/sequenceDiagram-FGHM5R23-CNautoFU.js.br +0 -0
- package/out/renderer/assets/sequenceDiagram-FGHM5R23-CNautoFU.js.gz +0 -0
- package/out/renderer/assets/{stateDiagram-FHFEXIEX-XxvRQ2fw.js → stateDiagram-FHFEXIEX-CKsv5tYk.js} +8 -8
- package/out/renderer/assets/stateDiagram-FHFEXIEX-CKsv5tYk.js.br +0 -0
- package/out/renderer/assets/stateDiagram-FHFEXIEX-CKsv5tYk.js.gz +0 -0
- package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-C8lZQVxS.js → stateDiagram-v2-QKLJ7IA2-CkdzkP8w.js} +4 -4
- package/out/renderer/assets/{timeline-definition-GMOUNBTQ-BThN9RD2.js → timeline-definition-GMOUNBTQ-CF_zqktL.js} +2 -2
- package/out/renderer/assets/timeline-definition-GMOUNBTQ-CF_zqktL.js.br +0 -0
- package/out/renderer/assets/timeline-definition-GMOUNBTQ-CF_zqktL.js.gz +0 -0
- package/out/renderer/assets/{vennDiagram-DHZGUBPP-DvL9CApP.js → vennDiagram-DHZGUBPP-C7q9SHIx.js} +1 -1
- package/out/renderer/assets/vennDiagram-DHZGUBPP-C7q9SHIx.js.br +0 -0
- package/out/renderer/assets/vennDiagram-DHZGUBPP-C7q9SHIx.js.gz +0 -0
- package/out/renderer/assets/{wardley-RL74JXVD-B4vPxA-A.js → wardley-RL74JXVD-DmW7d79y.js} +3 -3
- package/out/renderer/assets/wardley-RL74JXVD-DmW7d79y.js.br +0 -0
- package/out/renderer/assets/wardley-RL74JXVD-DmW7d79y.js.gz +0 -0
- package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-A3F8zuNA.js → wardleyDiagram-NUSXRM2D-1VzGmAot.js} +5 -5
- package/out/renderer/assets/wardleyDiagram-NUSXRM2D-1VzGmAot.js.br +0 -0
- package/out/renderer/assets/wardleyDiagram-NUSXRM2D-1VzGmAot.js.gz +0 -0
- package/out/renderer/assets/xlsx_parser_bg-CQ5Lc7LS.wasm +0 -0
- package/out/renderer/assets/{xychartDiagram-5P7HB3ND-BxjT7l5Q.js → xychartDiagram-5P7HB3ND-DznKXnsr.js} +2 -2
- package/out/renderer/assets/xychartDiagram-5P7HB3ND-DznKXnsr.js.br +0 -0
- package/out/renderer/assets/xychartDiagram-5P7HB3ND-DznKXnsr.js.gz +0 -0
- package/out/renderer/index.html +2 -2
- package/out/renderer/index.html.br +0 -0
- package/out/renderer/index.html.gz +0 -0
- package/out/scripts/electron-preload/preload.mjs +21 -0
- package/out/scripts/mcp-memo/server.mjs +173 -0
- package/out/scripts/mcp-memory/server.mjs +167 -0
- package/out/scripts/mcp-search/server.mjs +1 -1
- package/out/scripts/worker-file-outline/worker.mjs +237 -28
- package/out/server/main.js +2208 -1146
- package/package.json +14 -13
- package/resources/tree-sitter-wasm/tree-sitter-dart.wasm +0 -0
- package/out/renderer/assets/_baseUniq-BonfEMiF.js.br +0 -0
- package/out/renderer/assets/_baseUniq-BonfEMiF.js.gz +0 -0
- package/out/renderer/assets/arc-Do8LbeMR.js.br +0 -0
- package/out/renderer/assets/arc-Do8LbeMR.js.gz +0 -0
- package/out/renderer/assets/architectureDiagram-Q4EWVU46-B8pyh3AH.js.br +0 -0
- package/out/renderer/assets/architectureDiagram-Q4EWVU46-B8pyh3AH.js.gz +0 -0
- package/out/renderer/assets/blockDiagram-DXYQGD6D-DxehDaBQ.js.br +0 -0
- package/out/renderer/assets/blockDiagram-DXYQGD6D-DxehDaBQ.js.gz +0 -0
- package/out/renderer/assets/c4Diagram-AHTNJAMY-BwVB2HtH.js.br +0 -0
- package/out/renderer/assets/chunk-4TB4RGXK-Mp3JyPop.js.br +0 -0
- package/out/renderer/assets/chunk-4TB4RGXK-Mp3JyPop.js.gz +0 -0
- package/out/renderer/assets/chunk-EDXVE4YY-BQEQxqzJ.js.br +0 -0
- package/out/renderer/assets/chunk-EDXVE4YY-BQEQxqzJ.js.gz +0 -0
- package/out/renderer/assets/chunk-OYMX7WX6-D19mZGkc.js.br +0 -0
- package/out/renderer/assets/chunk-OYMX7WX6-D19mZGkc.js.gz +0 -0
- package/out/renderer/assets/chunk-YZCP3GAM-BEBfdXkA.js.br +0 -0
- package/out/renderer/assets/chunk-YZCP3GAM-BEBfdXkA.js.gz +0 -0
- package/out/renderer/assets/cose-bilkent-S5V4N54A-6SeriN8Z.js.br +0 -0
- package/out/renderer/assets/cose-bilkent-S5V4N54A-6SeriN8Z.js.gz +0 -0
- package/out/renderer/assets/dagre-KV5264BT-gXq994RD.js.br +0 -0
- package/out/renderer/assets/dagre-KV5264BT-gXq994RD.js.gz +0 -0
- package/out/renderer/assets/diagram-5BDNPKRD-GOebOMWt.js.br +0 -0
- package/out/renderer/assets/diagram-5BDNPKRD-GOebOMWt.js.gz +0 -0
- package/out/renderer/assets/diagram-G4DWMVQ6-BkQmAuCz.js.br +0 -0
- package/out/renderer/assets/diagram-G4DWMVQ6-BkQmAuCz.js.gz +0 -0
- package/out/renderer/assets/diagram-MMDJMWI5-Q9B0Tkq3.js.br +0 -0
- package/out/renderer/assets/diagram-MMDJMWI5-Q9B0Tkq3.js.gz +0 -0
- package/out/renderer/assets/diagram-TYMM5635-5wNVFKxe.js.br +0 -0
- package/out/renderer/assets/diagram-TYMM5635-5wNVFKxe.js.gz +0 -0
- package/out/renderer/assets/erDiagram-SMLLAGMA-DRxQnVgW.js.br +0 -0
- package/out/renderer/assets/erDiagram-SMLLAGMA-DRxQnVgW.js.gz +0 -0
- package/out/renderer/assets/flowDiagram-DWJPFMVM-3uIpRmli.js.br +0 -0
- package/out/renderer/assets/flowDiagram-DWJPFMVM-3uIpRmli.js.gz +0 -0
- package/out/renderer/assets/ganttDiagram-T4ZO3ILL-CFxauQFp.js.br +0 -0
- package/out/renderer/assets/ganttDiagram-T4ZO3ILL-CFxauQFp.js.gz +0 -0
- package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CDBCF75U.js.br +0 -0
- package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CDBCF75U.js.gz +0 -0
- package/out/renderer/assets/graph-B77t0Iut.js.br +0 -0
- package/out/renderer/assets/graph-B77t0Iut.js.gz +0 -0
- package/out/renderer/assets/index-CTSkyk5N.css +0 -7619
- package/out/renderer/assets/index-CTSkyk5N.css.br +0 -0
- package/out/renderer/assets/index-CTSkyk5N.css.gz +0 -0
- package/out/renderer/assets/index-l_WeGi7a.js.br +0 -0
- package/out/renderer/assets/index-l_WeGi7a.js.gz +0 -0
- package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-C59I-VqG.js.br +0 -0
- package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-C59I-VqG.js.gz +0 -0
- package/out/renderer/assets/journeyDiagram-VCZTEJTY-B6jawzfM.js.br +0 -0
- package/out/renderer/assets/journeyDiagram-VCZTEJTY-B6jawzfM.js.gz +0 -0
- package/out/renderer/assets/kanban-definition-6JOO6SKY-BCXhVxbs.js.br +0 -0
- package/out/renderer/assets/kanban-definition-6JOO6SKY-BCXhVxbs.js.gz +0 -0
- package/out/renderer/assets/layout-zx9noErc.js.br +0 -0
- package/out/renderer/assets/layout-zx9noErc.js.gz +0 -0
- package/out/renderer/assets/linear-Bo25ELwI.js.br +0 -0
- package/out/renderer/assets/linear-Bo25ELwI.js.gz +0 -0
- package/out/renderer/assets/min-eGRLXD6u.js.br +0 -0
- package/out/renderer/assets/min-eGRLXD6u.js.gz +0 -0
- package/out/renderer/assets/mindmap-definition-QFDTVHPH-PUpW_gKO.js.br +0 -0
- package/out/renderer/assets/mindmap-definition-QFDTVHPH-PUpW_gKO.js.gz +0 -0
- package/out/renderer/assets/pdf-worker-wrapper-Do-ikGHl.js.br +0 -0
- package/out/renderer/assets/pdf-worker-wrapper-Do-ikGHl.js.gz +0 -0
- package/out/renderer/assets/pieDiagram-DEJITSTG-BrSaX9YD.js.br +0 -0
- package/out/renderer/assets/pieDiagram-DEJITSTG-BrSaX9YD.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-C-Drv5KU.js +0 -4
- package/out/renderer/assets/pierre-dark-C-Drv5KU.js.br +0 -0
- package/out/renderer/assets/pierre-dark-C-Drv5KU.js.gz +0 -0
- package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js +0 -4
- package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js.br +0 -0
- package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js.gz +0 -0
- package/out/renderer/assets/pierre-light-CfQnmt6J.js +0 -4
- package/out/renderer/assets/pierre-light-CfQnmt6J.js.br +0 -0
- package/out/renderer/assets/pierre-light-CfQnmt6J.js.gz +0 -0
- package/out/renderer/assets/pierre-light-soft-DMVdU74b.js +0 -4
- package/out/renderer/assets/pierre-light-soft-DMVdU74b.js.br +0 -0
- package/out/renderer/assets/pierre-light-soft-DMVdU74b.js.gz +0 -0
- package/out/renderer/assets/quadrantDiagram-34T5L4WZ-B_oUGzKN.js.br +0 -0
- package/out/renderer/assets/quadrantDiagram-34T5L4WZ-B_oUGzKN.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js +0 -25
- package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js.br +0 -0
- package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js +0 -25
- package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js.br +0 -0
- package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js.gz +0 -0
- package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js +0 -25
- package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js.br +0 -0
- package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js.gz +0 -0
- package/out/renderer/assets/requirementDiagram-MS252O5E-LQY3T05J.js.br +0 -0
- package/out/renderer/assets/requirementDiagram-MS252O5E-LQY3T05J.js.gz +0 -0
- package/out/renderer/assets/sankeyDiagram-XADWPNL6-BZrdYvkl.js.br +0 -0
- package/out/renderer/assets/sankeyDiagram-XADWPNL6-BZrdYvkl.js.gz +0 -0
- package/out/renderer/assets/sequenceDiagram-FGHM5R23-DT03DGl3.js.br +0 -0
- package/out/renderer/assets/sequenceDiagram-FGHM5R23-DT03DGl3.js.gz +0 -0
- package/out/renderer/assets/stateDiagram-FHFEXIEX-XxvRQ2fw.js.br +0 -0
- package/out/renderer/assets/stateDiagram-FHFEXIEX-XxvRQ2fw.js.gz +0 -0
- package/out/renderer/assets/timeline-definition-GMOUNBTQ-BThN9RD2.js.br +0 -0
- package/out/renderer/assets/timeline-definition-GMOUNBTQ-BThN9RD2.js.gz +0 -0
- package/out/renderer/assets/vennDiagram-DHZGUBPP-DvL9CApP.js.br +0 -0
- package/out/renderer/assets/vennDiagram-DHZGUBPP-DvL9CApP.js.gz +0 -0
- package/out/renderer/assets/wardley-RL74JXVD-B4vPxA-A.js.br +0 -0
- package/out/renderer/assets/wardley-RL74JXVD-B4vPxA-A.js.gz +0 -0
- package/out/renderer/assets/wardleyDiagram-NUSXRM2D-A3F8zuNA.js.br +0 -0
- package/out/renderer/assets/wardleyDiagram-NUSXRM2D-A3F8zuNA.js.gz +0 -0
- package/out/renderer/assets/xychartDiagram-5P7HB3ND-BxjT7l5Q.js.br +0 -0
- package/out/renderer/assets/xychartDiagram-5P7HB3ND-BxjT7l5Q.js.gz +0 -0
package/out/server/main.js
CHANGED
|
@@ -11,44 +11,32 @@ process.treeSitterWasmPath = join(resourcesPath, "tree-sitter-wasm");
|
|
|
11
11
|
|
|
12
12
|
// src/backend/backend.ts
|
|
13
13
|
import { readdir as readdir2, rm as rm3, readFile as readFile9 } from "fs/promises";
|
|
14
|
-
import { join as
|
|
14
|
+
import { join as join22 } from "path";
|
|
15
15
|
|
|
16
|
-
// src/backend/storage.ts
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
mkdirSync,
|
|
21
|
-
writeFileSync,
|
|
22
|
-
readFileSync,
|
|
23
|
-
readdirSync,
|
|
24
|
-
rmSync,
|
|
25
|
-
existsSync,
|
|
26
|
-
appendFileSync
|
|
27
|
-
} from "fs";
|
|
28
|
-
import { createHash } from "crypto";
|
|
16
|
+
// src/backend/storage/index.ts
|
|
17
|
+
import { join as join5 } from "path";
|
|
18
|
+
import { mkdirSync as mkdirSync3, appendFileSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
29
19
|
|
|
30
|
-
// src/
|
|
31
|
-
|
|
32
|
-
"image/jpeg",
|
|
33
|
-
"image/png",
|
|
34
|
-
"image/gif",
|
|
35
|
-
"image/webp",
|
|
36
|
-
"image/svg+xml",
|
|
37
|
-
"image/bmp",
|
|
38
|
-
"image/avif"
|
|
39
|
-
]);
|
|
40
|
-
function isImageMimeType(mimeType) {
|
|
41
|
-
return !!mimeType && IMAGE_MIME_TYPES.has(mimeType);
|
|
42
|
-
}
|
|
43
|
-
var ALL_FEATURES = ["search", "skills", "ask_user", "share_to_user"];
|
|
20
|
+
// src/backend/storage/prepare.ts
|
|
21
|
+
import { mkdirSync } from "fs";
|
|
44
22
|
|
|
45
|
-
// src/backend/storage.ts
|
|
23
|
+
// src/backend/storage/constant.ts
|
|
24
|
+
import { join as join2 } from "path";
|
|
25
|
+
import { homedir } from "os";
|
|
46
26
|
var FELLO_DIR = join2(homedir(), ".fello");
|
|
47
27
|
var SOCKETS_DIR = join2(FELLO_DIR, "sockets");
|
|
48
28
|
var PROJECTS_DIR = join2(FELLO_DIR, "projects");
|
|
49
29
|
var TEMP_DIR = join2(FELLO_DIR, "temp");
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
|
|
31
|
+
// src/backend/storage/prepare.ts
|
|
32
|
+
mkdirSync(FELLO_DIR, { recursive: true });
|
|
33
|
+
mkdirSync(PROJECTS_DIR, { recursive: true });
|
|
34
|
+
mkdirSync(SOCKETS_DIR, { recursive: true });
|
|
35
|
+
mkdirSync(TEMP_DIR, { recursive: true });
|
|
36
|
+
|
|
37
|
+
// src/backend/storage/settings.ts
|
|
38
|
+
import { join as join3 } from "path";
|
|
39
|
+
import { writeFileSync, readFileSync, existsSync } from "fs";
|
|
52
40
|
var DEFAULT_SETTINGS = {
|
|
53
41
|
agents: {},
|
|
54
42
|
theme: { theme_mode: "system" },
|
|
@@ -71,14 +59,8 @@ var DEFAULT_SETTINGS = {
|
|
|
71
59
|
theme: "soft"
|
|
72
60
|
}
|
|
73
61
|
};
|
|
74
|
-
mkdirSync(PROJECTS_DIR, { recursive: true });
|
|
75
|
-
mkdirSync(SOCKETS_DIR, { recursive: true });
|
|
76
|
-
mkdirSync(TEMP_DIR, { recursive: true });
|
|
77
|
-
mkdirSync(WORKSPACES_DIR, { recursive: true });
|
|
78
|
-
mkdirSync(WORKSPACE_TEMP_DIR, { recursive: true });
|
|
79
|
-
var streamingSessions = /* @__PURE__ */ new Set();
|
|
80
62
|
function settingsPath() {
|
|
81
|
-
return
|
|
63
|
+
return join3(FELLO_DIR, "settings.json");
|
|
82
64
|
}
|
|
83
65
|
function readSettings() {
|
|
84
66
|
try {
|
|
@@ -222,579 +204,600 @@ function readSettings() {
|
|
|
222
204
|
function writeSettings(meta) {
|
|
223
205
|
writeFileSync(settingsPath(), JSON.stringify(meta, null, 2));
|
|
224
206
|
}
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
207
|
+
function getSettings() {
|
|
208
|
+
const meta = readSettings();
|
|
209
|
+
return {
|
|
210
|
+
agents: Object.entries(meta.agents).map(([id, agentMeta]) => {
|
|
211
|
+
if (agentMeta.type === "stdio") {
|
|
212
|
+
return {
|
|
213
|
+
id,
|
|
214
|
+
type: agentMeta.type,
|
|
215
|
+
command: agentMeta.command,
|
|
216
|
+
args: agentMeta.args.slice(),
|
|
217
|
+
env: Object.assign({}, agentMeta.env),
|
|
218
|
+
disabled: agentMeta.disabled
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (agentMeta.type === "api") {
|
|
222
|
+
return {
|
|
223
|
+
id,
|
|
224
|
+
type: agentMeta.type,
|
|
225
|
+
provider: agentMeta.provider,
|
|
226
|
+
baseUrl: agentMeta.baseUrl,
|
|
227
|
+
apiKey: agentMeta.apiKey,
|
|
228
|
+
headers: Object.assign({}, agentMeta.headers),
|
|
229
|
+
disabled: agentMeta.disabled,
|
|
230
|
+
contextWindowTokens: agentMeta.contextWindowTokens,
|
|
231
|
+
modelIdTemplate: agentMeta.modelIdTemplate
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
throw new Error("Invalid agent type.");
|
|
235
|
+
}).sort((a, b) => meta.agents[a.id].order - meta.agents[b.id].order),
|
|
236
|
+
mcpServers: Object.entries(meta.mcpServers).map(([id, srvMeta]) => {
|
|
237
|
+
if (srvMeta.type === "stdio") {
|
|
238
|
+
return {
|
|
239
|
+
id,
|
|
240
|
+
type: srvMeta.type,
|
|
241
|
+
command: srvMeta.command,
|
|
242
|
+
args: srvMeta.args.slice(),
|
|
243
|
+
env: Object.assign({}, srvMeta.env),
|
|
244
|
+
disabled: srvMeta.disabled
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (srvMeta.type === "http") {
|
|
248
|
+
return {
|
|
249
|
+
id,
|
|
250
|
+
type: srvMeta.type,
|
|
251
|
+
url: srvMeta.url,
|
|
252
|
+
headers: Object.assign({}, srvMeta.headers),
|
|
253
|
+
disabled: srvMeta.disabled
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (srvMeta.type === "sse") {
|
|
257
|
+
return {
|
|
258
|
+
id,
|
|
259
|
+
type: srvMeta.type,
|
|
260
|
+
url: srvMeta.url,
|
|
261
|
+
headers: Object.assign({}, srvMeta.headers),
|
|
262
|
+
disabled: srvMeta.disabled
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
throw new Error(`Invalid mcpServer type ${srvMeta.type}.`);
|
|
266
|
+
}).sort((a, b) => meta.mcpServers[a.id].order - meta.mcpServers[b.id].order),
|
|
267
|
+
i18n: {
|
|
268
|
+
language: meta.i18n.language
|
|
269
|
+
},
|
|
270
|
+
theme: {
|
|
271
|
+
themeMode: meta.theme.theme_mode
|
|
272
|
+
},
|
|
273
|
+
fileWatcher: {
|
|
274
|
+
enabled: meta.fileWatcher.enabled
|
|
275
|
+
},
|
|
276
|
+
ilink: {
|
|
277
|
+
useOriginalImage: meta.ilink.useOriginalImage
|
|
278
|
+
},
|
|
279
|
+
editor: {
|
|
280
|
+
name: meta.editor.name
|
|
281
|
+
},
|
|
282
|
+
sound: {
|
|
283
|
+
volume: meta.sound.volume,
|
|
284
|
+
muted: meta.sound.muted,
|
|
285
|
+
theme: meta.sound.theme
|
|
286
|
+
},
|
|
287
|
+
snippets: meta.snippets ?? []
|
|
288
|
+
};
|
|
230
289
|
}
|
|
231
|
-
function
|
|
232
|
-
|
|
290
|
+
function updateSettings(settings) {
|
|
291
|
+
const prevMeta = readSettings();
|
|
292
|
+
const meta = {
|
|
293
|
+
agents: (() => {
|
|
294
|
+
if (!settings.agents) {
|
|
295
|
+
return prevMeta.agents;
|
|
296
|
+
}
|
|
297
|
+
const nextAgents = {};
|
|
298
|
+
settings.agents.forEach((agent2, idx) => {
|
|
299
|
+
if (agent2.type === "stdio") {
|
|
300
|
+
nextAgents[agent2.id] = {
|
|
301
|
+
type: agent2.type,
|
|
302
|
+
command: agent2.command,
|
|
303
|
+
args: agent2.args.slice(),
|
|
304
|
+
env: Object.assign({}, agent2.env),
|
|
305
|
+
disabled: agent2.disabled,
|
|
306
|
+
order: idx
|
|
307
|
+
};
|
|
308
|
+
} else if (agent2.type === "api") {
|
|
309
|
+
nextAgents[agent2.id] = {
|
|
310
|
+
type: agent2.type,
|
|
311
|
+
provider: agent2.provider,
|
|
312
|
+
baseUrl: agent2.baseUrl,
|
|
313
|
+
apiKey: agent2.apiKey,
|
|
314
|
+
headers: Object.assign({}, agent2.headers || {}),
|
|
315
|
+
disabled: agent2.disabled,
|
|
316
|
+
order: idx,
|
|
317
|
+
contextWindowTokens: agent2.contextWindowTokens !== void 0 ? Number.isInteger(agent2.contextWindowTokens) && agent2.contextWindowTokens > 0 ? agent2.contextWindowTokens : void 0 : void 0,
|
|
318
|
+
modelIdTemplate: typeof agent2.modelIdTemplate === "string" && agent2.modelIdTemplate.trim() ? agent2.modelIdTemplate.trim() : void 0
|
|
319
|
+
};
|
|
320
|
+
} else {
|
|
321
|
+
throw new Error("Invalid agent type.");
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
return nextAgents;
|
|
325
|
+
})(),
|
|
326
|
+
i18n: (() => {
|
|
327
|
+
if (!settings.i18n) {
|
|
328
|
+
return prevMeta.i18n;
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
language: settings.i18n.language
|
|
332
|
+
};
|
|
333
|
+
})(),
|
|
334
|
+
theme: (() => {
|
|
335
|
+
if (!settings.theme) {
|
|
336
|
+
return prevMeta.theme;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
theme_mode: settings.theme.themeMode
|
|
340
|
+
};
|
|
341
|
+
})(),
|
|
342
|
+
mcpServers: (() => {
|
|
343
|
+
if (!settings.mcpServers) {
|
|
344
|
+
return prevMeta.mcpServers;
|
|
345
|
+
}
|
|
346
|
+
const nextMcpServers = {};
|
|
347
|
+
settings.mcpServers.forEach((srv, idx) => {
|
|
348
|
+
if (srv.type === "stdio") {
|
|
349
|
+
nextMcpServers[srv.id] = {
|
|
350
|
+
type: srv.type,
|
|
351
|
+
command: srv.command,
|
|
352
|
+
args: srv.args.slice(),
|
|
353
|
+
env: Object.assign({}, srv.env),
|
|
354
|
+
disabled: srv.disabled,
|
|
355
|
+
order: idx
|
|
356
|
+
};
|
|
357
|
+
} else if (srv.type === "http") {
|
|
358
|
+
nextMcpServers[srv.id] = {
|
|
359
|
+
type: srv.type,
|
|
360
|
+
url: srv.url,
|
|
361
|
+
headers: Object.assign({}, srv.headers),
|
|
362
|
+
disabled: srv.disabled,
|
|
363
|
+
order: idx
|
|
364
|
+
};
|
|
365
|
+
} else if (srv.type === "sse") {
|
|
366
|
+
nextMcpServers[srv.id] = {
|
|
367
|
+
type: srv.type,
|
|
368
|
+
url: srv.url,
|
|
369
|
+
headers: Object.assign({}, srv.headers),
|
|
370
|
+
disabled: srv.disabled,
|
|
371
|
+
order: idx
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
return nextMcpServers;
|
|
376
|
+
})(),
|
|
377
|
+
fileWatcher: (() => {
|
|
378
|
+
if (!settings.fileWatcher) {
|
|
379
|
+
return prevMeta.fileWatcher;
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
enabled: settings.fileWatcher.enabled
|
|
383
|
+
};
|
|
384
|
+
})(),
|
|
385
|
+
ilink: (() => {
|
|
386
|
+
if (!settings.ilink) {
|
|
387
|
+
return prevMeta.ilink;
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
useOriginalImage: settings.ilink.useOriginalImage
|
|
391
|
+
};
|
|
392
|
+
})(),
|
|
393
|
+
editor: (() => {
|
|
394
|
+
if (!settings.editor) {
|
|
395
|
+
return prevMeta.editor;
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
name: settings.editor.name
|
|
399
|
+
};
|
|
400
|
+
})(),
|
|
401
|
+
sound: (() => {
|
|
402
|
+
if (!settings.sound) {
|
|
403
|
+
return prevMeta.sound;
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
volume: typeof settings.sound.volume === "number" && settings.sound.volume >= 0 && settings.sound.volume <= 100 ? settings.sound.volume : prevMeta.sound.volume,
|
|
407
|
+
muted: typeof settings.sound.muted === "boolean" ? settings.sound.muted : prevMeta.sound.muted,
|
|
408
|
+
theme: settings.sound.theme === "soft" || settings.sound.theme === "crisp" ? settings.sound.theme : prevMeta.sound.theme
|
|
409
|
+
};
|
|
410
|
+
})(),
|
|
411
|
+
snippets: settings.snippets ?? prevMeta.snippets
|
|
412
|
+
};
|
|
413
|
+
writeSettings(meta);
|
|
233
414
|
}
|
|
234
|
-
|
|
235
|
-
|
|
415
|
+
|
|
416
|
+
// src/backend/storage/project-session.ts
|
|
417
|
+
import { basename, dirname as dirname2, join as join4 } from "path";
|
|
418
|
+
import { omit } from "es-toolkit";
|
|
419
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, readdirSync, rmSync, existsSync as existsSync2 } from "fs";
|
|
420
|
+
import { createHash } from "crypto";
|
|
421
|
+
|
|
422
|
+
// src/shared/constants.ts
|
|
423
|
+
var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
424
|
+
"image/jpeg",
|
|
425
|
+
"image/png",
|
|
426
|
+
"image/gif",
|
|
427
|
+
"image/webp",
|
|
428
|
+
"image/svg+xml",
|
|
429
|
+
"image/bmp",
|
|
430
|
+
"image/avif"
|
|
431
|
+
]);
|
|
432
|
+
function isImageMimeType(mimeType) {
|
|
433
|
+
return !!mimeType && IMAGE_MIME_TYPES.has(mimeType);
|
|
236
434
|
}
|
|
435
|
+
var ALL_FEATURES = ["search", "skills", "ask_user", "share_to_user", "memory"];
|
|
436
|
+
|
|
437
|
+
// src/backend/storage/project-session.ts
|
|
438
|
+
var projectMetaMemo = (() => {
|
|
439
|
+
const memo = /* @__PURE__ */ new Map();
|
|
440
|
+
const scopes2 = readdirSync(PROJECTS_DIR);
|
|
441
|
+
for (const scope of scopes2) {
|
|
442
|
+
if (scope.startsWith(".")) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const filename = join4(PROJECTS_DIR, scope, "project.json");
|
|
446
|
+
try {
|
|
447
|
+
const raw = JSON.parse(readFileSync2(filename, "utf-8"));
|
|
448
|
+
if (!raw) continue;
|
|
449
|
+
const id = String(raw.id || "");
|
|
450
|
+
const title = String(raw.title || "");
|
|
451
|
+
const cwd = String(raw.cwd || "");
|
|
452
|
+
const created_at = typeof raw.created_at === "number" ? raw.created_at : Date.now();
|
|
453
|
+
if (!id || !title || !cwd) continue;
|
|
454
|
+
const meta = {
|
|
455
|
+
id,
|
|
456
|
+
title,
|
|
457
|
+
cwd,
|
|
458
|
+
created_at,
|
|
459
|
+
filename
|
|
460
|
+
};
|
|
461
|
+
memo.set(id, meta);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
console.error(`load project error ${filename}`, error);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return memo;
|
|
467
|
+
})();
|
|
468
|
+
var sessionMetaMemo = (() => {
|
|
469
|
+
const memo = /* @__PURE__ */ new Map();
|
|
470
|
+
for (const [_, project] of Array.from(projectMetaMemo)) {
|
|
471
|
+
const dir = join4(dirname2(project.filename), "sessions");
|
|
472
|
+
if (!existsSync2(dir)) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
const scopes2 = readdirSync(dir);
|
|
477
|
+
for (const scope of scopes2) {
|
|
478
|
+
if (scope.startsWith(".")) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const filename = join4(dir, scope, "session.json");
|
|
482
|
+
try {
|
|
483
|
+
const raw = JSON.parse(readFileSync2(filename, "utf-8"));
|
|
484
|
+
if (!raw) continue;
|
|
485
|
+
const id = String(raw.id);
|
|
486
|
+
const title = String(raw.title || "");
|
|
487
|
+
const agent_id = String(raw.agent_id);
|
|
488
|
+
const resume_id = String(raw.resume_id);
|
|
489
|
+
const project_id = String(raw.project_id);
|
|
490
|
+
const created_at = typeof raw.created_at === "number" ? raw.created_at : Date.now();
|
|
491
|
+
const updated_at = typeof raw.updated_at === "number" ? raw.updated_at : created_at;
|
|
492
|
+
const mcp_servers = Array.isArray(raw.mcp_servers) ? raw.mcp_servers.filter((v) => typeof v === "string") : void 0;
|
|
493
|
+
const permission_mode = raw.permission_mode === "allow-all" || raw.permission_mode === "ask" ? raw.permission_mode : "ask";
|
|
494
|
+
const features = Array.isArray(raw.features) ? raw.features.filter((v) => ALL_FEATURES.includes(v)) : ALL_FEATURES;
|
|
495
|
+
const models = raw.models ?? null;
|
|
496
|
+
const modes = raw.modes ?? null;
|
|
497
|
+
const initialize_info = raw.initialize_info ?? null;
|
|
498
|
+
if (!id || !agent_id || !resume_id || !project_id) continue;
|
|
499
|
+
const meta = {
|
|
500
|
+
filename,
|
|
501
|
+
isStreaming: false,
|
|
502
|
+
connectionStatus: "disconnected",
|
|
503
|
+
id,
|
|
504
|
+
title,
|
|
505
|
+
agent_id,
|
|
506
|
+
resume_id,
|
|
507
|
+
project_id,
|
|
508
|
+
created_at,
|
|
509
|
+
updated_at,
|
|
510
|
+
mcp_servers,
|
|
511
|
+
features,
|
|
512
|
+
permission_mode,
|
|
513
|
+
models,
|
|
514
|
+
modes,
|
|
515
|
+
initialize_info
|
|
516
|
+
};
|
|
517
|
+
memo.set(id, meta);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error(`[storage] load session error ${filename}`, error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(`[storage] load session error ${dir}`, error);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return memo;
|
|
527
|
+
})();
|
|
237
528
|
function safeSessionDirName(sessionId) {
|
|
238
529
|
return sessionId.replace(/[\\/:<>"|?*]/g, "_");
|
|
239
530
|
}
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const legacyDir = join2(projectSessionsDir(projectId), sessionId);
|
|
245
|
-
if (existsSync(legacyDir)) return legacyDir;
|
|
246
|
-
}
|
|
247
|
-
return safeDir;
|
|
248
|
-
}
|
|
249
|
-
function sessionMetaPath(projectId, sessionId) {
|
|
250
|
-
return join2(sessionDir(projectId, sessionId), "session.json");
|
|
531
|
+
function writeProjectMeta(meta) {
|
|
532
|
+
mkdirSync2(dirname2(meta.filename), { recursive: true });
|
|
533
|
+
writeFileSync2(meta.filename, JSON.stringify(omit(meta, ["filename"]), null, 2));
|
|
534
|
+
projectMetaMemo.set(meta.id, meta);
|
|
251
535
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
536
|
+
function writeSessionMeta(meta) {
|
|
537
|
+
mkdirSync2(dirname2(meta.filename), { recursive: true });
|
|
538
|
+
writeFileSync2(
|
|
539
|
+
meta.filename,
|
|
540
|
+
JSON.stringify(omit(meta, ["filename", "isStreaming", "connectionStatus"]), null, 2)
|
|
541
|
+
);
|
|
542
|
+
sessionMetaMemo.set(meta.id, meta);
|
|
543
|
+
}
|
|
544
|
+
function listProjects() {
|
|
545
|
+
return Array.from(projectMetaMemo.values()).sort((a, b) => b.created_at - a.created_at).map((p) => ({
|
|
546
|
+
id: p.id,
|
|
547
|
+
title: p.title,
|
|
548
|
+
cwd: p.cwd,
|
|
549
|
+
createdAt: p.created_at
|
|
550
|
+
}));
|
|
551
|
+
}
|
|
552
|
+
function addProject(cwd) {
|
|
553
|
+
cwd = cwd.trim();
|
|
554
|
+
const projectId = createHash("sha1").update(cwd).digest("hex");
|
|
555
|
+
const existing = projectMetaMemo.get(projectId);
|
|
556
|
+
if (existing) {
|
|
557
|
+
return {
|
|
558
|
+
id: existing.id,
|
|
559
|
+
title: existing.title,
|
|
560
|
+
cwd: existing.cwd,
|
|
561
|
+
createdAt: existing.created_at
|
|
562
|
+
};
|
|
270
563
|
}
|
|
564
|
+
const now = Date.now();
|
|
565
|
+
const title = basename(cwd) || cwd;
|
|
566
|
+
const filename = join4(PROJECTS_DIR, projectId, "project.json");
|
|
567
|
+
const meta = { filename, id: projectId, title, cwd, created_at: now };
|
|
568
|
+
writeProjectMeta(meta);
|
|
569
|
+
return {
|
|
570
|
+
id: meta.id,
|
|
571
|
+
title: meta.title,
|
|
572
|
+
cwd: meta.cwd,
|
|
573
|
+
createdAt: meta.created_at
|
|
574
|
+
};
|
|
271
575
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
576
|
+
function updateProjectTitle(projectId, title) {
|
|
577
|
+
const project = projectMetaMemo.get(projectId);
|
|
578
|
+
if (!project) return;
|
|
579
|
+
const nextTitle = title.trim();
|
|
580
|
+
if (!nextTitle) return;
|
|
581
|
+
project.title = nextTitle;
|
|
582
|
+
writeProjectMeta(project);
|
|
583
|
+
}
|
|
584
|
+
function deleteProject(projectId) {
|
|
585
|
+
const projectMeta = projectMetaMemo.get(projectId);
|
|
586
|
+
if (projectMeta) rmSync(dirname2(projectMeta.filename), { recursive: true, force: true });
|
|
587
|
+
projectMetaMemo.delete(projectId);
|
|
588
|
+
for (const [id, meta] of sessionMetaMemo.entries()) {
|
|
283
589
|
if (meta.project_id === projectId) {
|
|
284
|
-
|
|
590
|
+
sessionMetaMemo.delete(id);
|
|
285
591
|
}
|
|
286
592
|
}
|
|
287
593
|
}
|
|
288
|
-
function
|
|
289
|
-
|
|
594
|
+
function getProject(projectId) {
|
|
595
|
+
const project = projectMetaMemo.get(projectId);
|
|
596
|
+
if (!project) return null;
|
|
597
|
+
return {
|
|
598
|
+
id: project.id,
|
|
599
|
+
title: project.title,
|
|
600
|
+
cwd: project.cwd,
|
|
601
|
+
createdAt: project.created_at
|
|
602
|
+
};
|
|
290
603
|
}
|
|
291
|
-
function
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
604
|
+
function createSession(projectId, resumeId, agentId, updates) {
|
|
605
|
+
const projectMeta = projectMetaMemo.get(projectId);
|
|
606
|
+
if (!projectMeta) throw new Error("Project does not exist");
|
|
607
|
+
const now = Date.now();
|
|
608
|
+
const id = `${agentId}:${resumeId}`;
|
|
609
|
+
const sessionMeta = {
|
|
610
|
+
filename: join4(
|
|
611
|
+
dirname2(projectMeta.filename),
|
|
612
|
+
"sessions",
|
|
613
|
+
safeSessionDirName(id),
|
|
614
|
+
"session.json"
|
|
615
|
+
),
|
|
616
|
+
isStreaming: false,
|
|
617
|
+
connectionStatus: "disconnected",
|
|
618
|
+
id,
|
|
619
|
+
title: updates?.title ?? "",
|
|
620
|
+
agent_id: agentId,
|
|
621
|
+
resume_id: resumeId,
|
|
622
|
+
project_id: projectId,
|
|
623
|
+
created_at: now,
|
|
624
|
+
updated_at: now,
|
|
625
|
+
mcp_servers: updates?.mcpServers ?? [],
|
|
626
|
+
features: updates?.features ?? ALL_FEATURES,
|
|
627
|
+
permission_mode: updates?.permissionMode ?? "ask",
|
|
628
|
+
models: updates?.models ?? null,
|
|
629
|
+
modes: updates?.modes ?? null,
|
|
630
|
+
initialize_info: updates?.initializeInfo ?? null
|
|
631
|
+
};
|
|
632
|
+
writeSessionMeta(sessionMeta);
|
|
633
|
+
return getSession(id);
|
|
634
|
+
}
|
|
635
|
+
function updateSession(sessionId, updates, updateTime = true) {
|
|
636
|
+
const meta = sessionMetaMemo.get(sessionId);
|
|
637
|
+
if (!meta) return;
|
|
638
|
+
if (updates.isStreaming !== void 0) meta.isStreaming = updates.isStreaming;
|
|
639
|
+
if (updates.connectionStatus !== void 0) meta.connectionStatus = updates.connectionStatus;
|
|
640
|
+
if (updates.title !== void 0) meta.title = updates.title;
|
|
641
|
+
if (updates.mcpServers !== void 0) meta.mcp_servers = updates.mcpServers;
|
|
642
|
+
if (updates.features !== void 0) meta.features = updates.features;
|
|
643
|
+
if (updates.permissionMode !== void 0) meta.permission_mode = updates.permissionMode;
|
|
644
|
+
if (updates.models !== void 0) meta.models = updates.models;
|
|
645
|
+
if (updates.modes !== void 0) meta.modes = updates.modes;
|
|
646
|
+
if (updates.initializeInfo !== void 0) meta.initialize_info = updates.initializeInfo;
|
|
647
|
+
if (updateTime) {
|
|
648
|
+
meta.updated_at = Date.now();
|
|
649
|
+
}
|
|
650
|
+
writeSessionMeta(meta);
|
|
295
651
|
}
|
|
296
|
-
function
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
initialize_info
|
|
332
|
-
};
|
|
333
|
-
sessionMetaCache.set(id, meta);
|
|
334
|
-
return meta;
|
|
335
|
-
} catch {
|
|
336
|
-
return null;
|
|
652
|
+
function deleteSession(sessionId) {
|
|
653
|
+
const sessionMeta = sessionMetaMemo.get(sessionId);
|
|
654
|
+
if (!sessionMeta) return;
|
|
655
|
+
const dir = dirname2(sessionMeta.filename);
|
|
656
|
+
if (existsSync2(dir)) rmSync(dir, { recursive: true, force: true });
|
|
657
|
+
sessionMetaMemo.delete(sessionId);
|
|
658
|
+
}
|
|
659
|
+
function listSessions() {
|
|
660
|
+
const sessionMetas = Array.from(sessionMetaMemo.values()).sort(
|
|
661
|
+
(a, b) => b.updated_at - a.updated_at
|
|
662
|
+
);
|
|
663
|
+
const result = [];
|
|
664
|
+
for (const meta of sessionMetas) {
|
|
665
|
+
const project = projectMetaMemo.get(meta.project_id);
|
|
666
|
+
if (project) {
|
|
667
|
+
result.push({
|
|
668
|
+
id: meta.id,
|
|
669
|
+
title: meta.title,
|
|
670
|
+
cwd: project.cwd,
|
|
671
|
+
projectId: meta.project_id,
|
|
672
|
+
projectTitle: project.title,
|
|
673
|
+
agentId: meta.agent_id,
|
|
674
|
+
resumeId: meta.resume_id,
|
|
675
|
+
createdAt: meta.created_at,
|
|
676
|
+
updatedAt: meta.updated_at,
|
|
677
|
+
mcpServers: meta.mcp_servers ?? [],
|
|
678
|
+
features: meta.features ?? ALL_FEATURES,
|
|
679
|
+
permissionMode: meta.permission_mode ?? "ask",
|
|
680
|
+
models: meta.models ?? null,
|
|
681
|
+
modes: meta.modes ?? null,
|
|
682
|
+
initializeInfo: meta.initialize_info ?? null,
|
|
683
|
+
isStreaming: meta.isStreaming,
|
|
684
|
+
connectionStatus: meta.connectionStatus
|
|
685
|
+
});
|
|
686
|
+
}
|
|
337
687
|
}
|
|
688
|
+
return result;
|
|
338
689
|
}
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
690
|
+
function getSession(sessionId) {
|
|
691
|
+
const meta = sessionMetaMemo.get(sessionId);
|
|
692
|
+
if (!meta) return null;
|
|
693
|
+
const project = projectMetaMemo.get(meta.project_id);
|
|
694
|
+
if (!project) return null;
|
|
695
|
+
return {
|
|
696
|
+
id: meta.id,
|
|
697
|
+
title: meta.title,
|
|
698
|
+
cwd: project.cwd,
|
|
699
|
+
projectId: meta.project_id,
|
|
700
|
+
projectTitle: project.title,
|
|
701
|
+
agentId: meta.agent_id,
|
|
702
|
+
resumeId: meta.resume_id,
|
|
703
|
+
createdAt: meta.created_at,
|
|
704
|
+
updatedAt: meta.updated_at,
|
|
705
|
+
mcpServers: meta.mcp_servers ?? [],
|
|
706
|
+
features: meta.features ?? ALL_FEATURES,
|
|
707
|
+
permissionMode: meta.permission_mode ?? "ask",
|
|
708
|
+
models: meta.models ?? null,
|
|
709
|
+
modes: meta.modes ?? null,
|
|
710
|
+
initializeInfo: meta.initialize_info ?? null,
|
|
711
|
+
isStreaming: meta.isStreaming,
|
|
712
|
+
connectionStatus: meta.connectionStatus
|
|
713
|
+
};
|
|
343
714
|
}
|
|
344
|
-
function
|
|
345
|
-
const
|
|
346
|
-
if (!
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
sessionMetaCache.delete(sessionId);
|
|
715
|
+
function touchSession(sessionId) {
|
|
716
|
+
const meta = sessionMetaMemo.get(sessionId);
|
|
717
|
+
if (!meta) return;
|
|
718
|
+
meta.updated_at = Date.now();
|
|
719
|
+
writeSessionMeta(meta);
|
|
350
720
|
}
|
|
351
|
-
function
|
|
352
|
-
|
|
721
|
+
function sessionDir(sessionId) {
|
|
722
|
+
const meta = sessionMetaMemo.get(sessionId);
|
|
723
|
+
if (!meta) return null;
|
|
724
|
+
return dirname2(meta.filename);
|
|
353
725
|
}
|
|
354
|
-
function
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
return
|
|
726
|
+
function sessionTerminalDir(sessionId) {
|
|
727
|
+
const dir = sessionDir(sessionId);
|
|
728
|
+
if (!dir) return null;
|
|
729
|
+
return join4(dir, "terminals");
|
|
358
730
|
}
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
731
|
+
function sessionShareDir(sessionId) {
|
|
732
|
+
const dir = sessionDir(sessionId);
|
|
733
|
+
if (!dir) return null;
|
|
734
|
+
return join4(dir, "share");
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// src/backend/storage/index.ts
|
|
738
|
+
function appendSessionMessage(sessionIdOrSubSessionId, notification) {
|
|
739
|
+
const dir = sessionDir(sessionIdOrSubSessionId);
|
|
740
|
+
if (!dir) return;
|
|
741
|
+
mkdirSync3(dir, { recursive: true });
|
|
742
|
+
const filePath = join5(dir, "messages.jsonl");
|
|
743
|
+
appendFileSync(filePath, JSON.stringify(notification) + "\n");
|
|
744
|
+
}
|
|
745
|
+
function readSessionMessages(sessionIdOrSubSessionId) {
|
|
746
|
+
const dir = sessionDir(sessionIdOrSubSessionId);
|
|
747
|
+
if (!dir) return [];
|
|
748
|
+
const filePath = join5(dir, "messages.jsonl");
|
|
749
|
+
if (!existsSync3(filePath)) return [];
|
|
750
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
751
|
+
return content.split("\n").filter((line) => line.trim()).map((line) => {
|
|
752
|
+
try {
|
|
753
|
+
return JSON.parse(line);
|
|
754
|
+
} catch {
|
|
755
|
+
return null;
|
|
375
756
|
}
|
|
757
|
+
}).filter(Boolean);
|
|
758
|
+
}
|
|
759
|
+
function appendTerminalOutput(sessionIdOrSubSessionId, terminalId, data) {
|
|
760
|
+
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(terminalId)) {
|
|
761
|
+
throw new Error("Invalid terminalId");
|
|
762
|
+
}
|
|
763
|
+
const dir = sessionTerminalDir(sessionIdOrSubSessionId);
|
|
764
|
+
if (!dir) return;
|
|
765
|
+
if (!existsSync3(dir)) {
|
|
766
|
+
mkdirSync3(dir, { recursive: true });
|
|
376
767
|
}
|
|
768
|
+
const filePath = join5(dir, `${terminalId}.log`);
|
|
769
|
+
appendFileSync(filePath, data);
|
|
377
770
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const meta = readSettings();
|
|
382
|
-
return {
|
|
383
|
-
agents: Object.entries(meta.agents).map(([id, agentMeta]) => {
|
|
384
|
-
if (agentMeta.type === "stdio") {
|
|
385
|
-
return {
|
|
386
|
-
id,
|
|
387
|
-
type: agentMeta.type,
|
|
388
|
-
command: agentMeta.command,
|
|
389
|
-
args: agentMeta.args.slice(),
|
|
390
|
-
env: Object.assign({}, agentMeta.env),
|
|
391
|
-
disabled: agentMeta.disabled
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
if (agentMeta.type === "api") {
|
|
395
|
-
return {
|
|
396
|
-
id,
|
|
397
|
-
type: agentMeta.type,
|
|
398
|
-
provider: agentMeta.provider,
|
|
399
|
-
baseUrl: agentMeta.baseUrl,
|
|
400
|
-
apiKey: agentMeta.apiKey,
|
|
401
|
-
headers: Object.assign({}, agentMeta.headers),
|
|
402
|
-
disabled: agentMeta.disabled,
|
|
403
|
-
contextWindowTokens: agentMeta.contextWindowTokens,
|
|
404
|
-
modelIdTemplate: agentMeta.modelIdTemplate
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
throw new Error("Invalid agent type.");
|
|
408
|
-
}).sort((a, b) => meta.agents[a.id].order - meta.agents[b.id].order),
|
|
409
|
-
mcpServers: Object.entries(meta.mcpServers).map(([id, srvMeta]) => {
|
|
410
|
-
if (srvMeta.type === "stdio") {
|
|
411
|
-
return {
|
|
412
|
-
id,
|
|
413
|
-
type: srvMeta.type,
|
|
414
|
-
command: srvMeta.command,
|
|
415
|
-
args: srvMeta.args.slice(),
|
|
416
|
-
env: Object.assign({}, srvMeta.env),
|
|
417
|
-
disabled: srvMeta.disabled
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
if (srvMeta.type === "http") {
|
|
421
|
-
return {
|
|
422
|
-
id,
|
|
423
|
-
type: srvMeta.type,
|
|
424
|
-
url: srvMeta.url,
|
|
425
|
-
headers: Object.assign({}, srvMeta.headers),
|
|
426
|
-
disabled: srvMeta.disabled
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
if (srvMeta.type === "sse") {
|
|
430
|
-
return {
|
|
431
|
-
id,
|
|
432
|
-
type: srvMeta.type,
|
|
433
|
-
url: srvMeta.url,
|
|
434
|
-
headers: Object.assign({}, srvMeta.headers),
|
|
435
|
-
disabled: srvMeta.disabled
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
throw new Error(`Invalid mcpServer type ${srvMeta.type}.`);
|
|
439
|
-
}).sort((a, b) => meta.mcpServers[a.id].order - meta.mcpServers[b.id].order),
|
|
440
|
-
i18n: {
|
|
441
|
-
language: meta.i18n.language
|
|
442
|
-
},
|
|
443
|
-
theme: {
|
|
444
|
-
themeMode: meta.theme.theme_mode
|
|
445
|
-
},
|
|
446
|
-
fileWatcher: {
|
|
447
|
-
enabled: meta.fileWatcher.enabled
|
|
448
|
-
},
|
|
449
|
-
ilink: {
|
|
450
|
-
useOriginalImage: meta.ilink.useOriginalImage
|
|
451
|
-
},
|
|
452
|
-
editor: {
|
|
453
|
-
name: meta.editor.name
|
|
454
|
-
},
|
|
455
|
-
sound: {
|
|
456
|
-
volume: meta.sound.volume,
|
|
457
|
-
muted: meta.sound.muted,
|
|
458
|
-
theme: meta.sound.theme
|
|
459
|
-
},
|
|
460
|
-
snippets: meta.snippets ?? []
|
|
461
|
-
};
|
|
462
|
-
},
|
|
463
|
-
updateSettings(settings) {
|
|
464
|
-
const prevMeta = readSettings();
|
|
465
|
-
const meta = {
|
|
466
|
-
agents: (() => {
|
|
467
|
-
if (!settings.agents) {
|
|
468
|
-
return prevMeta.agents;
|
|
469
|
-
}
|
|
470
|
-
const nextAgents = {};
|
|
471
|
-
settings.agents.forEach((agent, idx) => {
|
|
472
|
-
if (agent.type === "stdio") {
|
|
473
|
-
nextAgents[agent.id] = {
|
|
474
|
-
type: agent.type,
|
|
475
|
-
command: agent.command,
|
|
476
|
-
args: agent.args.slice(),
|
|
477
|
-
env: Object.assign({}, agent.env),
|
|
478
|
-
disabled: agent.disabled,
|
|
479
|
-
order: idx
|
|
480
|
-
};
|
|
481
|
-
} else if (agent.type === "api") {
|
|
482
|
-
nextAgents[agent.id] = {
|
|
483
|
-
type: agent.type,
|
|
484
|
-
provider: agent.provider,
|
|
485
|
-
baseUrl: agent.baseUrl,
|
|
486
|
-
apiKey: agent.apiKey,
|
|
487
|
-
headers: Object.assign({}, agent.headers || {}),
|
|
488
|
-
disabled: agent.disabled,
|
|
489
|
-
order: idx,
|
|
490
|
-
contextWindowTokens: agent.contextWindowTokens !== void 0 ? Number.isInteger(agent.contextWindowTokens) && agent.contextWindowTokens > 0 ? agent.contextWindowTokens : void 0 : void 0,
|
|
491
|
-
modelIdTemplate: typeof agent.modelIdTemplate === "string" && agent.modelIdTemplate.trim() ? agent.modelIdTemplate.trim() : void 0
|
|
492
|
-
};
|
|
493
|
-
} else {
|
|
494
|
-
throw new Error("Invalid agent type.");
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
return nextAgents;
|
|
498
|
-
})(),
|
|
499
|
-
i18n: (() => {
|
|
500
|
-
if (!settings.i18n) {
|
|
501
|
-
return prevMeta.i18n;
|
|
502
|
-
}
|
|
503
|
-
return {
|
|
504
|
-
language: settings.i18n.language
|
|
505
|
-
};
|
|
506
|
-
})(),
|
|
507
|
-
theme: (() => {
|
|
508
|
-
if (!settings.theme) {
|
|
509
|
-
return prevMeta.theme;
|
|
510
|
-
}
|
|
511
|
-
return {
|
|
512
|
-
theme_mode: settings.theme.themeMode
|
|
513
|
-
};
|
|
514
|
-
})(),
|
|
515
|
-
mcpServers: (() => {
|
|
516
|
-
if (!settings.mcpServers) {
|
|
517
|
-
return prevMeta.mcpServers;
|
|
518
|
-
}
|
|
519
|
-
const nextMcpServers = {};
|
|
520
|
-
settings.mcpServers.forEach((srv, idx) => {
|
|
521
|
-
if (srv.type === "stdio") {
|
|
522
|
-
nextMcpServers[srv.id] = {
|
|
523
|
-
type: srv.type,
|
|
524
|
-
command: srv.command,
|
|
525
|
-
args: srv.args.slice(),
|
|
526
|
-
env: Object.assign({}, srv.env),
|
|
527
|
-
disabled: srv.disabled,
|
|
528
|
-
order: idx
|
|
529
|
-
};
|
|
530
|
-
} else if (srv.type === "http") {
|
|
531
|
-
nextMcpServers[srv.id] = {
|
|
532
|
-
type: srv.type,
|
|
533
|
-
url: srv.url,
|
|
534
|
-
headers: Object.assign({}, srv.headers),
|
|
535
|
-
disabled: srv.disabled,
|
|
536
|
-
order: idx
|
|
537
|
-
};
|
|
538
|
-
} else if (srv.type === "sse") {
|
|
539
|
-
nextMcpServers[srv.id] = {
|
|
540
|
-
type: srv.type,
|
|
541
|
-
url: srv.url,
|
|
542
|
-
headers: Object.assign({}, srv.headers),
|
|
543
|
-
disabled: srv.disabled,
|
|
544
|
-
order: idx
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
return nextMcpServers;
|
|
549
|
-
})(),
|
|
550
|
-
fileWatcher: (() => {
|
|
551
|
-
if (!settings.fileWatcher) {
|
|
552
|
-
return prevMeta.fileWatcher;
|
|
553
|
-
}
|
|
554
|
-
return {
|
|
555
|
-
enabled: settings.fileWatcher.enabled
|
|
556
|
-
};
|
|
557
|
-
})(),
|
|
558
|
-
ilink: (() => {
|
|
559
|
-
if (!settings.ilink) {
|
|
560
|
-
return prevMeta.ilink;
|
|
561
|
-
}
|
|
562
|
-
return {
|
|
563
|
-
useOriginalImage: settings.ilink.useOriginalImage
|
|
564
|
-
};
|
|
565
|
-
})(),
|
|
566
|
-
editor: (() => {
|
|
567
|
-
if (!settings.editor) {
|
|
568
|
-
return prevMeta.editor;
|
|
569
|
-
}
|
|
570
|
-
return {
|
|
571
|
-
name: settings.editor.name
|
|
572
|
-
};
|
|
573
|
-
})(),
|
|
574
|
-
sound: (() => {
|
|
575
|
-
if (!settings.sound) {
|
|
576
|
-
return prevMeta.sound;
|
|
577
|
-
}
|
|
578
|
-
return {
|
|
579
|
-
volume: typeof settings.sound.volume === "number" && settings.sound.volume >= 0 && settings.sound.volume <= 100 ? settings.sound.volume : prevMeta.sound.volume,
|
|
580
|
-
muted: typeof settings.sound.muted === "boolean" ? settings.sound.muted : prevMeta.sound.muted,
|
|
581
|
-
theme: settings.sound.theme === "soft" || settings.sound.theme === "crisp" ? settings.sound.theme : prevMeta.sound.theme
|
|
582
|
-
};
|
|
583
|
-
})(),
|
|
584
|
-
snippets: settings.snippets ?? prevMeta.snippets
|
|
585
|
-
};
|
|
586
|
-
writeSettings(meta);
|
|
587
|
-
},
|
|
588
|
-
listProjects() {
|
|
589
|
-
return listProjectMetas().map((p) => ({
|
|
590
|
-
id: p.id,
|
|
591
|
-
title: p.title,
|
|
592
|
-
cwd: p.cwd,
|
|
593
|
-
createdAt: p.created_at
|
|
594
|
-
}));
|
|
595
|
-
},
|
|
596
|
-
addProject(cwd) {
|
|
597
|
-
const projectId = hashCwd(cwd);
|
|
598
|
-
const existing = getProjectMeta(projectId);
|
|
599
|
-
if (existing) {
|
|
600
|
-
return {
|
|
601
|
-
id: existing.id,
|
|
602
|
-
title: existing.title,
|
|
603
|
-
cwd: existing.cwd,
|
|
604
|
-
createdAt: existing.created_at
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
const now = Date.now();
|
|
608
|
-
const title = basename(cwd) || cwd;
|
|
609
|
-
const meta = { id: projectId, title, cwd, created_at: now };
|
|
610
|
-
writeProjectMeta(meta);
|
|
611
|
-
return {
|
|
612
|
-
id: meta.id,
|
|
613
|
-
title: meta.title,
|
|
614
|
-
cwd: meta.cwd,
|
|
615
|
-
createdAt: meta.created_at
|
|
616
|
-
};
|
|
617
|
-
},
|
|
618
|
-
updateProjectTitle(projectId, title) {
|
|
619
|
-
const project = getProjectMeta(projectId);
|
|
620
|
-
if (!project) return;
|
|
621
|
-
const nextTitle = title.trim();
|
|
622
|
-
if (!nextTitle) return;
|
|
623
|
-
project.title = nextTitle;
|
|
624
|
-
writeProjectMeta(project);
|
|
625
|
-
},
|
|
626
|
-
deleteProject(projectId) {
|
|
627
|
-
deleteProjectMeta(projectId);
|
|
628
|
-
},
|
|
629
|
-
getProject(projectId) {
|
|
630
|
-
const project = getProjectMeta(projectId);
|
|
631
|
-
if (!project) return null;
|
|
632
|
-
return {
|
|
633
|
-
id: project.id,
|
|
634
|
-
title: project.title,
|
|
635
|
-
cwd: project.cwd,
|
|
636
|
-
createdAt: project.created_at
|
|
637
|
-
};
|
|
638
|
-
},
|
|
639
|
-
createSession(projectId, resumeId, agentId, updates) {
|
|
640
|
-
const project = readProjectMeta(projectId);
|
|
641
|
-
if (!project) throw new Error("Project does not exist");
|
|
642
|
-
const now = Date.now();
|
|
643
|
-
const id = `${agentId}:${resumeId}`;
|
|
644
|
-
const meta = {
|
|
645
|
-
id,
|
|
646
|
-
title: updates?.title ?? "",
|
|
647
|
-
agent_id: agentId,
|
|
648
|
-
resume_id: resumeId,
|
|
649
|
-
project_id: projectId,
|
|
650
|
-
created_at: now,
|
|
651
|
-
updated_at: now,
|
|
652
|
-
mcp_servers: updates?.mcpServers ?? [],
|
|
653
|
-
features: updates?.features ?? ALL_FEATURES,
|
|
654
|
-
permission_mode: updates?.permissionMode ?? "ask",
|
|
655
|
-
models: updates?.models ?? null,
|
|
656
|
-
modes: updates?.modes ?? null,
|
|
657
|
-
initialize_info: updates?.initializeInfo ?? null
|
|
658
|
-
};
|
|
659
|
-
writeSessionMeta(meta);
|
|
660
|
-
return this.getSession(id);
|
|
661
|
-
},
|
|
662
|
-
updateSession(sessionId, updates, updateTime = true) {
|
|
663
|
-
const meta = getSessionMeta(sessionId);
|
|
664
|
-
if (!meta) return;
|
|
665
|
-
if (updates.title !== void 0) meta.title = updates.title;
|
|
666
|
-
if (updates.mcpServers !== void 0) meta.mcp_servers = updates.mcpServers;
|
|
667
|
-
if (updates.features !== void 0) meta.features = updates.features;
|
|
668
|
-
if (updates.permissionMode !== void 0) meta.permission_mode = updates.permissionMode;
|
|
669
|
-
if (updates.models !== void 0) meta.models = updates.models;
|
|
670
|
-
if (updates.modes !== void 0) meta.modes = updates.modes;
|
|
671
|
-
if (updates.initializeInfo !== void 0) meta.initialize_info = updates.initializeInfo;
|
|
672
|
-
if (updateTime) {
|
|
673
|
-
meta.updated_at = Date.now();
|
|
674
|
-
}
|
|
675
|
-
writeSessionMeta(meta);
|
|
676
|
-
if (updates.isStreaming !== void 0) {
|
|
677
|
-
if (updates.isStreaming) {
|
|
678
|
-
streamingSessions.add(sessionId);
|
|
679
|
-
} else {
|
|
680
|
-
streamingSessions.delete(sessionId);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
},
|
|
684
|
-
deleteSession(sessionId) {
|
|
685
|
-
deleteSessionMeta(sessionId);
|
|
686
|
-
streamingSessions.delete(sessionId);
|
|
687
|
-
},
|
|
688
|
-
listSessions() {
|
|
689
|
-
const metas = listSessionMetas();
|
|
690
|
-
const result = [];
|
|
691
|
-
for (const meta of metas) {
|
|
692
|
-
const project = readProjectMeta(meta.project_id);
|
|
693
|
-
if (project) {
|
|
694
|
-
result.push({
|
|
695
|
-
id: meta.id,
|
|
696
|
-
title: meta.title,
|
|
697
|
-
cwd: project.cwd,
|
|
698
|
-
projectId: meta.project_id,
|
|
699
|
-
projectTitle: project.title,
|
|
700
|
-
agentId: meta.agent_id,
|
|
701
|
-
resumeId: meta.resume_id,
|
|
702
|
-
createdAt: meta.created_at,
|
|
703
|
-
updatedAt: meta.updated_at,
|
|
704
|
-
mcpServers: meta.mcp_servers ?? [],
|
|
705
|
-
features: meta.features ?? ALL_FEATURES,
|
|
706
|
-
permissionMode: meta.permission_mode ?? "ask",
|
|
707
|
-
models: meta.models ?? null,
|
|
708
|
-
modes: meta.modes ?? null,
|
|
709
|
-
initializeInfo: meta.initialize_info ?? null,
|
|
710
|
-
isStreaming: streamingSessions.has(meta.id)
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
return result;
|
|
715
|
-
},
|
|
716
|
-
getSession(sessionId) {
|
|
717
|
-
const meta = getSessionMeta(sessionId);
|
|
718
|
-
if (!meta) return null;
|
|
719
|
-
const project = readProjectMeta(meta.project_id);
|
|
720
|
-
if (!project) return null;
|
|
721
|
-
return {
|
|
722
|
-
id: meta.id,
|
|
723
|
-
title: meta.title,
|
|
724
|
-
cwd: project.cwd,
|
|
725
|
-
projectId: meta.project_id,
|
|
726
|
-
projectTitle: project.title,
|
|
727
|
-
agentId: meta.agent_id,
|
|
728
|
-
resumeId: meta.resume_id,
|
|
729
|
-
createdAt: meta.created_at,
|
|
730
|
-
updatedAt: meta.updated_at,
|
|
731
|
-
mcpServers: meta.mcp_servers ?? [],
|
|
732
|
-
features: meta.features ?? ALL_FEATURES,
|
|
733
|
-
permissionMode: meta.permission_mode ?? "ask",
|
|
734
|
-
models: meta.models ?? null,
|
|
735
|
-
modes: meta.modes ?? null,
|
|
736
|
-
initializeInfo: meta.initialize_info ?? null,
|
|
737
|
-
isStreaming: streamingSessions.has(meta.id)
|
|
738
|
-
};
|
|
739
|
-
},
|
|
740
|
-
touchSession(sessionId) {
|
|
741
|
-
const session = this.getSession(sessionId);
|
|
742
|
-
if (!session) return;
|
|
743
|
-
const meta = readSessionMeta(session.projectId, session.id);
|
|
744
|
-
if (!meta) return;
|
|
745
|
-
meta.updated_at = Date.now();
|
|
746
|
-
writeSessionMeta(meta);
|
|
747
|
-
},
|
|
748
|
-
appendSessionMessage(sessionId, notification) {
|
|
749
|
-
const session = this.getSession(sessionId);
|
|
750
|
-
if (!session) return;
|
|
751
|
-
const filePath = join2(sessionDir(session.projectId, sessionId), "messages.jsonl");
|
|
752
|
-
appendFileSync(filePath, JSON.stringify(notification) + "\n");
|
|
753
|
-
},
|
|
754
|
-
readSessionMessages(sessionId) {
|
|
755
|
-
const session = this.getSession(sessionId);
|
|
756
|
-
if (!session) return [];
|
|
757
|
-
const filePath = join2(sessionDir(session.projectId, sessionId), "messages.jsonl");
|
|
758
|
-
if (!existsSync(filePath)) return [];
|
|
759
|
-
const content = readFileSync(filePath, "utf-8");
|
|
760
|
-
return content.split("\n").filter((line) => line.trim()).map((line) => {
|
|
761
|
-
try {
|
|
762
|
-
return JSON.parse(line);
|
|
763
|
-
} catch {
|
|
764
|
-
return null;
|
|
765
|
-
}
|
|
766
|
-
}).filter(Boolean);
|
|
767
|
-
},
|
|
768
|
-
appendTerminalOutput(sessionId, terminalId, data) {
|
|
769
|
-
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(terminalId)) {
|
|
770
|
-
throw new Error("Invalid terminalId");
|
|
771
|
-
}
|
|
772
|
-
const session = this.getSession(sessionId);
|
|
773
|
-
if (!session) return;
|
|
774
|
-
const sessionTerminalsDir = join2(sessionDir(session.projectId, sessionId), "terminals");
|
|
775
|
-
if (!existsSync(sessionTerminalsDir)) {
|
|
776
|
-
mkdirSync(sessionTerminalsDir, { recursive: true });
|
|
777
|
-
}
|
|
778
|
-
const filePath = join2(sessionTerminalsDir, `${terminalId}.log`);
|
|
779
|
-
appendFileSync(filePath, data);
|
|
780
|
-
},
|
|
781
|
-
/** 获取 session 的 share 目录路径 */
|
|
782
|
-
sessionShareDir(sessionId) {
|
|
783
|
-
const session = this.getSession(sessionId);
|
|
784
|
-
if (!session) return null;
|
|
785
|
-
return join2(sessionDir(session.projectId, sessionId), "share");
|
|
786
|
-
},
|
|
787
|
-
readTerminalOutput(sessionId, terminalId) {
|
|
788
|
-
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(terminalId)) {
|
|
789
|
-
throw new Error("Invalid terminalId");
|
|
790
|
-
}
|
|
791
|
-
const session = this.getSession(sessionId);
|
|
792
|
-
if (!session) return null;
|
|
793
|
-
const sessionTerminalsDir = join2(sessionDir(session.projectId, sessionId), "terminals");
|
|
794
|
-
const filePath = join2(sessionTerminalsDir, `${terminalId}.log`);
|
|
795
|
-
if (!existsSync(filePath)) return null;
|
|
796
|
-
return readFileSync(filePath, "utf-8");
|
|
771
|
+
function readTerminalOutput(sessionIdOrSubSessionId, terminalId) {
|
|
772
|
+
if (!/^[a-zA-Z0-9_-]{1,64}$/.test(terminalId)) {
|
|
773
|
+
throw new Error("Invalid terminalId");
|
|
797
774
|
}
|
|
775
|
+
const dir = sessionTerminalDir(sessionIdOrSubSessionId);
|
|
776
|
+
if (!dir) return null;
|
|
777
|
+
const filePath = join5(dir, `${terminalId}.log`);
|
|
778
|
+
if (!existsSync3(filePath)) return null;
|
|
779
|
+
return readFileSync3(filePath, "utf-8");
|
|
780
|
+
}
|
|
781
|
+
var storageOps = {
|
|
782
|
+
getSettings,
|
|
783
|
+
updateSettings,
|
|
784
|
+
addProject,
|
|
785
|
+
createSession,
|
|
786
|
+
deleteProject,
|
|
787
|
+
deleteSession,
|
|
788
|
+
getProject,
|
|
789
|
+
getSession,
|
|
790
|
+
listProjects,
|
|
791
|
+
listSessions,
|
|
792
|
+
getSessionDataSystemPath: sessionDir,
|
|
793
|
+
sessionShareDir,
|
|
794
|
+
touchSession,
|
|
795
|
+
updateProjectTitle,
|
|
796
|
+
updateSession,
|
|
797
|
+
appendSessionMessage,
|
|
798
|
+
readSessionMessages,
|
|
799
|
+
appendTerminalOutput,
|
|
800
|
+
readTerminalOutput
|
|
798
801
|
};
|
|
799
802
|
|
|
800
803
|
// src/backend/i18n.ts
|
|
@@ -959,15 +962,15 @@ function setLanguage(lang) {
|
|
|
959
962
|
|
|
960
963
|
// src/agents/storage.ts
|
|
961
964
|
import { readFile } from "fs/promises";
|
|
962
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
963
|
-
import { join as
|
|
965
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
966
|
+
import { join as join6 } from "path";
|
|
964
967
|
function toSafePathSegment(value) {
|
|
965
968
|
const normalized = value.trim();
|
|
966
969
|
if (!normalized) return "unknown";
|
|
967
970
|
return normalized.replace(/[\\/:<>"|?*]/g, "_");
|
|
968
971
|
}
|
|
969
972
|
function apiAgentSessionDir(agentId, sessionId) {
|
|
970
|
-
return
|
|
973
|
+
return join6(
|
|
971
974
|
FELLO_DIR,
|
|
972
975
|
"api-agents",
|
|
973
976
|
toSafePathSegment(agentId),
|
|
@@ -976,10 +979,10 @@ function apiAgentSessionDir(agentId, sessionId) {
|
|
|
976
979
|
);
|
|
977
980
|
}
|
|
978
981
|
function sessionJsonPath(agentId, sessionId) {
|
|
979
|
-
return
|
|
982
|
+
return join6(apiAgentSessionDir(agentId, sessionId), "session.json");
|
|
980
983
|
}
|
|
981
984
|
function historyJsonlPath(agentId, sessionId) {
|
|
982
|
-
return
|
|
985
|
+
return join6(apiAgentSessionDir(agentId, sessionId), "history.jsonl");
|
|
983
986
|
}
|
|
984
987
|
function isModelMessage(value) {
|
|
985
988
|
if (!value || typeof value !== "object") return false;
|
|
@@ -1025,7 +1028,7 @@ async function loadPersistedSessionHistory(params) {
|
|
|
1025
1028
|
}).filter((value) => isModelMessage(value));
|
|
1026
1029
|
}
|
|
1027
1030
|
function ensureSessionDirSync(agentId, sessionId) {
|
|
1028
|
-
|
|
1031
|
+
mkdirSync4(apiAgentSessionDir(agentId, sessionId), { recursive: true });
|
|
1029
1032
|
}
|
|
1030
1033
|
async function savePersistedSessionState(params) {
|
|
1031
1034
|
ensureSessionDirSync(params.agentId, params.sessionId);
|
|
@@ -1034,7 +1037,7 @@ async function savePersistedSessionState(params) {
|
|
|
1034
1037
|
allowedToolKinds: params.allowedToolKinds,
|
|
1035
1038
|
contextUsedTokens: params.contextUsedTokens
|
|
1036
1039
|
};
|
|
1037
|
-
|
|
1040
|
+
writeFileSync3(
|
|
1038
1041
|
sessionJsonPath(params.agentId, params.sessionId),
|
|
1039
1042
|
JSON.stringify(sessionJson, null, 2),
|
|
1040
1043
|
"utf8"
|
|
@@ -1043,7 +1046,7 @@ async function savePersistedSessionState(params) {
|
|
|
1043
1046
|
async function savePersistedSessionHistory(params) {
|
|
1044
1047
|
ensureSessionDirSync(params.agentId, params.sessionId);
|
|
1045
1048
|
const lines = params.messages.map((message) => JSON.stringify(message)).join("\n");
|
|
1046
|
-
|
|
1049
|
+
writeFileSync3(
|
|
1047
1050
|
historyJsonlPath(params.agentId, params.sessionId),
|
|
1048
1051
|
lines ? `${lines}
|
|
1049
1052
|
` : "",
|
|
@@ -1064,11 +1067,11 @@ function deletePersistedSessionDirectory(params) {
|
|
|
1064
1067
|
});
|
|
1065
1068
|
}
|
|
1066
1069
|
function deleteAgentPersistedStorage(agentId) {
|
|
1067
|
-
const dir =
|
|
1070
|
+
const dir = join6(FELLO_DIR, "api-agents", toSafePathSegment(agentId));
|
|
1068
1071
|
rmSync2(dir, { recursive: true, force: true });
|
|
1069
1072
|
}
|
|
1070
1073
|
function deleteOrphanedAgentSessionDirectories(agentId, knownSessionIds) {
|
|
1071
|
-
const sessionsDir =
|
|
1074
|
+
const sessionsDir = join6(FELLO_DIR, "api-agents", toSafePathSegment(agentId), "sessions");
|
|
1072
1075
|
let entries;
|
|
1073
1076
|
try {
|
|
1074
1077
|
entries = readdirSync2(sessionsDir);
|
|
@@ -1076,7 +1079,7 @@ function deleteOrphanedAgentSessionDirectories(agentId, knownSessionIds) {
|
|
|
1076
1079
|
return;
|
|
1077
1080
|
}
|
|
1078
1081
|
for (const entry of entries) {
|
|
1079
|
-
const fullPath =
|
|
1082
|
+
const fullPath = join6(sessionsDir, entry);
|
|
1080
1083
|
try {
|
|
1081
1084
|
if (statSync(fullPath).isDirectory() && !knownSessionIds.has(entry)) {
|
|
1082
1085
|
rmSync2(fullPath, { recursive: true, force: true });
|
|
@@ -1091,7 +1094,7 @@ import { createServer } from "http";
|
|
|
1091
1094
|
import { WebSocketServer } from "ws";
|
|
1092
1095
|
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
1093
1096
|
import { networkInterfaces } from "os";
|
|
1094
|
-
import { join as
|
|
1097
|
+
import { join as join8 } from "path";
|
|
1095
1098
|
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
1096
1099
|
|
|
1097
1100
|
// src/backend/serve-file.ts
|
|
@@ -1161,42 +1164,42 @@ async function serveFile(filename, cwd) {
|
|
|
1161
1164
|
}
|
|
1162
1165
|
|
|
1163
1166
|
// src/backend/automation/store.ts
|
|
1164
|
-
import { join as
|
|
1167
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
1165
1168
|
import { homedir as homedir2 } from "os";
|
|
1166
1169
|
import {
|
|
1167
|
-
mkdirSync as
|
|
1168
|
-
writeFileSync as
|
|
1169
|
-
readFileSync as
|
|
1170
|
+
mkdirSync as mkdirSync5,
|
|
1171
|
+
writeFileSync as writeFileSync4,
|
|
1172
|
+
readFileSync as readFileSync4,
|
|
1170
1173
|
readdirSync as readdirSync3,
|
|
1171
1174
|
rmSync as rmSync3,
|
|
1172
|
-
existsSync as
|
|
1175
|
+
existsSync as existsSync4,
|
|
1173
1176
|
statSync as statSync2
|
|
1174
1177
|
} from "fs";
|
|
1175
|
-
var AUTOMATIONS_DIR =
|
|
1176
|
-
|
|
1178
|
+
var AUTOMATIONS_DIR = join7(homedir2(), ".fello", "automations");
|
|
1179
|
+
mkdirSync5(AUTOMATIONS_DIR, { recursive: true });
|
|
1177
1180
|
var store = {
|
|
1178
1181
|
scheduleDir(scheduleId) {
|
|
1179
|
-
return
|
|
1182
|
+
return join7(AUTOMATIONS_DIR, scheduleId);
|
|
1180
1183
|
},
|
|
1181
1184
|
scheduleConfigPath(scheduleId) {
|
|
1182
|
-
return
|
|
1185
|
+
return join7(this.scheduleDir(scheduleId), "schedule.json");
|
|
1183
1186
|
},
|
|
1184
1187
|
tasksDir(scheduleId) {
|
|
1185
|
-
return
|
|
1188
|
+
return join7(this.scheduleDir(scheduleId), "tasks");
|
|
1186
1189
|
},
|
|
1187
1190
|
taskDir(scheduleId, taskId) {
|
|
1188
|
-
return
|
|
1191
|
+
return join7(this.tasksDir(scheduleId), taskId);
|
|
1189
1192
|
},
|
|
1190
1193
|
taskMetaPath(scheduleId, taskId) {
|
|
1191
|
-
return
|
|
1194
|
+
return join7(this.taskDir(scheduleId, taskId), "task.json");
|
|
1192
1195
|
},
|
|
1193
1196
|
listSchedules() {
|
|
1194
|
-
if (!
|
|
1197
|
+
if (!existsSync4(AUTOMATIONS_DIR)) return [];
|
|
1195
1198
|
const dirs = readdirSync3(AUTOMATIONS_DIR);
|
|
1196
1199
|
const list = [];
|
|
1197
1200
|
for (const dir of dirs) {
|
|
1198
1201
|
try {
|
|
1199
|
-
const raw = JSON.parse(
|
|
1202
|
+
const raw = JSON.parse(readFileSync4(this.scheduleConfigPath(dir), "utf-8"));
|
|
1200
1203
|
list.push(raw);
|
|
1201
1204
|
} catch {
|
|
1202
1205
|
}
|
|
@@ -1206,16 +1209,16 @@ var store = {
|
|
|
1206
1209
|
},
|
|
1207
1210
|
getSchedule(scheduleId) {
|
|
1208
1211
|
try {
|
|
1209
|
-
return JSON.parse(
|
|
1212
|
+
return JSON.parse(readFileSync4(this.scheduleConfigPath(scheduleId), "utf-8"));
|
|
1210
1213
|
} catch {
|
|
1211
1214
|
return null;
|
|
1212
1215
|
}
|
|
1213
1216
|
},
|
|
1214
1217
|
saveSchedule(schedule) {
|
|
1215
1218
|
const dir = this.scheduleDir(schedule.id);
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
+
mkdirSync5(dir, { recursive: true });
|
|
1220
|
+
mkdirSync5(this.tasksDir(schedule.id), { recursive: true });
|
|
1221
|
+
writeFileSync4(this.scheduleConfigPath(schedule.id), JSON.stringify(schedule, null, 2));
|
|
1219
1222
|
},
|
|
1220
1223
|
createSchedule(params) {
|
|
1221
1224
|
const now = Date.now();
|
|
@@ -1237,18 +1240,18 @@ var store = {
|
|
|
1237
1240
|
},
|
|
1238
1241
|
deleteSchedule(scheduleId) {
|
|
1239
1242
|
const dir = this.scheduleDir(scheduleId);
|
|
1240
|
-
if (
|
|
1243
|
+
if (existsSync4(dir)) {
|
|
1241
1244
|
rmSync3(dir, { recursive: true, force: true });
|
|
1242
1245
|
}
|
|
1243
1246
|
},
|
|
1244
1247
|
listTasks(scheduleId) {
|
|
1245
1248
|
const dir = this.tasksDir(scheduleId);
|
|
1246
|
-
if (!
|
|
1249
|
+
if (!existsSync4(dir)) return [];
|
|
1247
1250
|
const dirs = readdirSync3(dir);
|
|
1248
1251
|
const list = [];
|
|
1249
1252
|
for (const d of dirs) {
|
|
1250
1253
|
try {
|
|
1251
|
-
const raw = JSON.parse(
|
|
1254
|
+
const raw = JSON.parse(readFileSync4(this.taskMetaPath(scheduleId, d), "utf-8"));
|
|
1252
1255
|
list.push(raw);
|
|
1253
1256
|
} catch {
|
|
1254
1257
|
}
|
|
@@ -1258,28 +1261,28 @@ var store = {
|
|
|
1258
1261
|
},
|
|
1259
1262
|
getTask(scheduleId, taskId) {
|
|
1260
1263
|
try {
|
|
1261
|
-
return JSON.parse(
|
|
1264
|
+
return JSON.parse(readFileSync4(this.taskMetaPath(scheduleId, taskId), "utf-8"));
|
|
1262
1265
|
} catch {
|
|
1263
1266
|
return null;
|
|
1264
1267
|
}
|
|
1265
1268
|
},
|
|
1266
1269
|
saveTask(scheduleId, task) {
|
|
1267
1270
|
const dir = this.taskDir(scheduleId, task.id);
|
|
1268
|
-
|
|
1269
|
-
|
|
1271
|
+
mkdirSync5(dir, { recursive: true });
|
|
1272
|
+
writeFileSync4(this.taskMetaPath(scheduleId, task.id), JSON.stringify(task, null, 2));
|
|
1270
1273
|
},
|
|
1271
1274
|
deleteTask(scheduleId, taskId) {
|
|
1272
1275
|
const dir = this.taskDir(scheduleId, taskId);
|
|
1273
|
-
if (
|
|
1276
|
+
if (existsSync4(dir)) {
|
|
1274
1277
|
rmSync3(dir, { recursive: true, force: true });
|
|
1275
1278
|
}
|
|
1276
1279
|
},
|
|
1277
1280
|
listTaskFiles(scheduleId, taskId) {
|
|
1278
1281
|
const dir = this.taskDir(scheduleId, taskId);
|
|
1279
|
-
if (!
|
|
1282
|
+
if (!existsSync4(dir)) return [];
|
|
1280
1283
|
const entries = readdirSync3(dir);
|
|
1281
1284
|
return entries.filter((f) => f !== "task.json").map((f) => {
|
|
1282
|
-
const fullPath =
|
|
1285
|
+
const fullPath = join7(dir, f);
|
|
1283
1286
|
const s = statSync2(fullPath);
|
|
1284
1287
|
if (s.isDirectory()) {
|
|
1285
1288
|
return this._listDirRecursive(fullPath, f);
|
|
@@ -1291,30 +1294,30 @@ var store = {
|
|
|
1291
1294
|
const result = [];
|
|
1292
1295
|
const entries = readdirSync3(dir);
|
|
1293
1296
|
for (const entry of entries) {
|
|
1294
|
-
const full =
|
|
1297
|
+
const full = join7(dir, entry);
|
|
1295
1298
|
const s = statSync2(full);
|
|
1296
1299
|
if (s.isDirectory()) {
|
|
1297
|
-
result.push(...this._listDirRecursive(full,
|
|
1300
|
+
result.push(...this._listDirRecursive(full, join7(prefix, entry)));
|
|
1298
1301
|
} else {
|
|
1299
|
-
result.push(
|
|
1302
|
+
result.push(join7(prefix, entry));
|
|
1300
1303
|
}
|
|
1301
1304
|
}
|
|
1302
1305
|
return result;
|
|
1303
1306
|
},
|
|
1304
1307
|
readTaskFile(scheduleId, taskId, filePath, encoding) {
|
|
1305
1308
|
const base = this.taskDir(scheduleId, taskId);
|
|
1306
|
-
const fullPath =
|
|
1309
|
+
const fullPath = join7(base, filePath);
|
|
1307
1310
|
if (!fullPath.startsWith(base + "/") && fullPath !== base) throw new Error("Invalid file path");
|
|
1308
|
-
if (!
|
|
1309
|
-
if (encoding === "base64") return
|
|
1310
|
-
return
|
|
1311
|
+
if (!existsSync4(fullPath)) throw new Error("File not found");
|
|
1312
|
+
if (encoding === "base64") return readFileSync4(fullPath).toString("base64");
|
|
1313
|
+
return readFileSync4(fullPath, "utf-8");
|
|
1311
1314
|
},
|
|
1312
1315
|
writeTaskFile(scheduleId, taskId, filePath, content) {
|
|
1313
1316
|
const base = this.taskDir(scheduleId, taskId);
|
|
1314
|
-
const fullPath =
|
|
1317
|
+
const fullPath = join7(base, filePath);
|
|
1315
1318
|
if (!fullPath.startsWith(base + "/") && fullPath !== base) throw new Error("Invalid file path");
|
|
1316
|
-
|
|
1317
|
-
|
|
1319
|
+
mkdirSync5(dirname3(fullPath), { recursive: true });
|
|
1320
|
+
writeFileSync4(fullPath, content);
|
|
1318
1321
|
}
|
|
1319
1322
|
};
|
|
1320
1323
|
|
|
@@ -1513,17 +1516,17 @@ function createWebUIModule() {
|
|
|
1513
1516
|
function broadcastEvent(channel, payload) {
|
|
1514
1517
|
if (!isEnabled || !wss) return false;
|
|
1515
1518
|
let hasClients = false;
|
|
1516
|
-
for (const
|
|
1517
|
-
if (
|
|
1519
|
+
for (const client2 of connectedClients) {
|
|
1520
|
+
if (client2.readyState === 1) {
|
|
1518
1521
|
hasClients = true;
|
|
1519
1522
|
break;
|
|
1520
1523
|
}
|
|
1521
1524
|
}
|
|
1522
1525
|
if (!hasClients) return false;
|
|
1523
1526
|
const message = JSON.stringify({ type: "event", channel, payload });
|
|
1524
|
-
for (const
|
|
1525
|
-
if (
|
|
1526
|
-
|
|
1527
|
+
for (const client2 of connectedClients) {
|
|
1528
|
+
if (client2.readyState === 1) {
|
|
1529
|
+
client2.send(message);
|
|
1527
1530
|
}
|
|
1528
1531
|
}
|
|
1529
1532
|
return true;
|
|
@@ -1584,10 +1587,10 @@ function createWebUIModule() {
|
|
|
1584
1587
|
reqPath = "/index.html";
|
|
1585
1588
|
}
|
|
1586
1589
|
const baseDir = process.rendererPath;
|
|
1587
|
-
let filePath =
|
|
1590
|
+
let filePath = join8(baseDir, reqPath);
|
|
1588
1591
|
let s = await stat2(filePath).catch(() => null);
|
|
1589
1592
|
if (!s || s.isDirectory()) {
|
|
1590
|
-
filePath =
|
|
1593
|
+
filePath = join8(baseDir, "index.html");
|
|
1591
1594
|
}
|
|
1592
1595
|
const mime = mimeTypes2.lookup(filePath) || "application/octet-stream";
|
|
1593
1596
|
const acceptEncoding = req.headers["accept-encoding"] || "";
|
|
@@ -1683,8 +1686,8 @@ function createWebUIModule() {
|
|
|
1683
1686
|
function stop() {
|
|
1684
1687
|
isEnabled = false;
|
|
1685
1688
|
currentToken = null;
|
|
1686
|
-
for (const
|
|
1687
|
-
|
|
1689
|
+
for (const client2 of connectedClients) {
|
|
1690
|
+
client2.close();
|
|
1688
1691
|
}
|
|
1689
1692
|
connectedClients.clear();
|
|
1690
1693
|
if (wss) {
|
|
@@ -2056,7 +2059,7 @@ function buildSkillsMcpServer(options) {
|
|
|
2056
2059
|
}
|
|
2057
2060
|
|
|
2058
2061
|
// src/backend/search/index.ts
|
|
2059
|
-
import { join as
|
|
2062
|
+
import { join as join12 } from "path";
|
|
2060
2063
|
|
|
2061
2064
|
// src/shared/zod/mcp-search-schema.ts
|
|
2062
2065
|
import { z } from "zod";
|
|
@@ -2099,7 +2102,7 @@ var fileOutlineRespondSchema = z.object({
|
|
|
2099
2102
|
});
|
|
2100
2103
|
|
|
2101
2104
|
// src/backend/search/file-outline.ts
|
|
2102
|
-
import { extname, join as
|
|
2105
|
+
import { extname, join as join10, isAbsolute as isAbsolute4, resolve as resolve3, relative as relative5 } from "path";
|
|
2103
2106
|
import { readFile as readFile4 } from "fs/promises";
|
|
2104
2107
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2105
2108
|
import { fork } from "child_process";
|
|
@@ -2116,7 +2119,7 @@ var LANGUAGES = [
|
|
|
2116
2119
|
{
|
|
2117
2120
|
name: "JavaScript",
|
|
2118
2121
|
extensions: [".js", ".jsx", ".mjs", ".cjs"],
|
|
2119
|
-
wasmFile:
|
|
2122
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-javascript.wasm"),
|
|
2120
2123
|
symbols: [
|
|
2121
2124
|
{
|
|
2122
2125
|
types: ["function_declaration", "generator_function_declaration"],
|
|
@@ -2126,11 +2129,13 @@ var LANGUAGES = [
|
|
|
2126
2129
|
{ types: ["method_definition"], label: "method", hasName: true },
|
|
2127
2130
|
{ types: ["class_declaration"], label: "class", hasName: true },
|
|
2128
2131
|
{
|
|
2129
|
-
types: ["lexical_declaration"
|
|
2130
|
-
label: "
|
|
2132
|
+
types: ["lexical_declaration"],
|
|
2133
|
+
label: "variable",
|
|
2131
2134
|
hasName: true,
|
|
2132
|
-
maxDepth: 0
|
|
2135
|
+
maxDepth: 0,
|
|
2136
|
+
labelByChild: { const: "const", let: "let" }
|
|
2133
2137
|
},
|
|
2138
|
+
{ types: ["variable_declaration"], label: "var", hasName: true, maxDepth: 0 },
|
|
2134
2139
|
{ types: ["import_statement"], label: "import", hasName: true }
|
|
2135
2140
|
],
|
|
2136
2141
|
wrappers: [{ node: "export_statement", prefix: "export ", createStandaloneLabel: "export" }],
|
|
@@ -2144,36 +2149,60 @@ var LANGUAGES = [
|
|
|
2144
2149
|
"export_specifier"
|
|
2145
2150
|
],
|
|
2146
2151
|
identifierTypes: ["identifier", "type_identifier"]
|
|
2152
|
+
},
|
|
2153
|
+
statementDetail: {
|
|
2154
|
+
sourceFields: ["source"],
|
|
2155
|
+
sourceTypes: ["string"],
|
|
2156
|
+
clauseTypes: [
|
|
2157
|
+
"import_clause",
|
|
2158
|
+
"named_imports",
|
|
2159
|
+
"named_exports",
|
|
2160
|
+
"export_clause",
|
|
2161
|
+
"namespace_import"
|
|
2162
|
+
],
|
|
2163
|
+
typeKeywordTypes: ["type"],
|
|
2164
|
+
detailAsNameWhenNoSource: true
|
|
2147
2165
|
}
|
|
2148
2166
|
},
|
|
2149
2167
|
{
|
|
2150
2168
|
name: "TypeScript",
|
|
2151
2169
|
extensions: [".ts"],
|
|
2152
|
-
wasmFile:
|
|
2170
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-typescript.wasm"),
|
|
2153
2171
|
symbols: [
|
|
2154
2172
|
{
|
|
2155
|
-
types: ["function_declaration", "generator_function_declaration"],
|
|
2173
|
+
types: ["function_declaration", "generator_function_declaration", "function_signature"],
|
|
2156
2174
|
label: "function",
|
|
2157
2175
|
hasName: true
|
|
2158
2176
|
},
|
|
2159
|
-
{ types: ["method_definition"], label: "method", hasName: true },
|
|
2177
|
+
{ types: ["method_definition", "abstract_method_signature"], label: "method", hasName: true },
|
|
2160
2178
|
{ types: ["class_declaration", "abstract_class_declaration"], label: "class", hasName: true },
|
|
2161
2179
|
{ types: ["interface_declaration"], label: "interface", hasName: true },
|
|
2162
2180
|
{ types: ["type_alias_declaration"], label: "type", hasName: true },
|
|
2163
2181
|
{ types: ["enum_declaration"], label: "enum", hasName: true },
|
|
2164
2182
|
{ types: ["internal_module"], label: "namespace", hasName: true },
|
|
2165
|
-
{ types: ["property_signature"], label: "property", hasName: true },
|
|
2183
|
+
{ types: ["property_signature"], label: "property", hasName: true, maxDepth: 1 },
|
|
2184
|
+
// object_type acts as a depth barrier to prevent property_signature inside
|
|
2185
|
+
// generic constraints or type arguments from being misidentified as real properties
|
|
2186
|
+
{ types: ["object_type"], label: "_", hasName: false, maxDepth: -1 },
|
|
2166
2187
|
{
|
|
2167
|
-
types: ["lexical_declaration"
|
|
2168
|
-
label: "
|
|
2188
|
+
types: ["lexical_declaration"],
|
|
2189
|
+
label: "variable",
|
|
2169
2190
|
hasName: true,
|
|
2170
|
-
maxDepth: 0
|
|
2191
|
+
maxDepth: 0,
|
|
2192
|
+
labelByChild: { const: "const", let: "let" }
|
|
2171
2193
|
},
|
|
2194
|
+
{ types: ["variable_declaration"], label: "var", hasName: true, maxDepth: 0 },
|
|
2172
2195
|
{ types: ["import_statement"], label: "import", hasName: true }
|
|
2173
2196
|
],
|
|
2174
2197
|
wrappers: [
|
|
2175
2198
|
{ node: "export_statement", prefix: "export ", createStandaloneLabel: "export" },
|
|
2176
|
-
{
|
|
2199
|
+
{
|
|
2200
|
+
node: "ambient_declaration",
|
|
2201
|
+
prefix: "declare ",
|
|
2202
|
+
maxDepth: 0,
|
|
2203
|
+
createStandaloneLabel: "declare",
|
|
2204
|
+
createContainerWhenCovered: true
|
|
2205
|
+
}
|
|
2177
2206
|
],
|
|
2178
2207
|
nameOf: {
|
|
2179
2208
|
fieldPriority: ["name", "source"],
|
|
@@ -2185,15 +2214,30 @@ var LANGUAGES = [
|
|
|
2185
2214
|
"export_specifier"
|
|
2186
2215
|
],
|
|
2187
2216
|
identifierTypes: ["identifier", "type_identifier"]
|
|
2217
|
+
},
|
|
2218
|
+
statementDetail: {
|
|
2219
|
+
labels: ["import", "export", "declare"],
|
|
2220
|
+
sourceFields: ["source"],
|
|
2221
|
+
sourceTypes: ["string"],
|
|
2222
|
+
clauseTypes: [
|
|
2223
|
+
"import_clause",
|
|
2224
|
+
"named_imports",
|
|
2225
|
+
"named_exports",
|
|
2226
|
+
"export_clause",
|
|
2227
|
+
"namespace_import"
|
|
2228
|
+
],
|
|
2229
|
+
typeKeywordTypes: ["type"],
|
|
2230
|
+
trimPatterns: ["\\s*\\{\\s*$"],
|
|
2231
|
+
detailAsNameWhenNoSource: true
|
|
2188
2232
|
}
|
|
2189
2233
|
},
|
|
2190
2234
|
{
|
|
2191
2235
|
name: "TSX",
|
|
2192
2236
|
extensions: [".tsx"],
|
|
2193
|
-
wasmFile:
|
|
2237
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-tsx.wasm"),
|
|
2194
2238
|
symbols: [
|
|
2195
2239
|
{
|
|
2196
|
-
types: ["function_declaration", "generator_function_declaration"],
|
|
2240
|
+
types: ["function_declaration", "generator_function_declaration", "function_signature"],
|
|
2197
2241
|
label: "function",
|
|
2198
2242
|
hasName: true
|
|
2199
2243
|
},
|
|
@@ -2204,11 +2248,13 @@ var LANGUAGES = [
|
|
|
2204
2248
|
{ types: ["enum_declaration"], label: "enum", hasName: true },
|
|
2205
2249
|
{ types: ["internal_module"], label: "namespace", hasName: true },
|
|
2206
2250
|
{
|
|
2207
|
-
types: ["lexical_declaration"
|
|
2208
|
-
label: "
|
|
2251
|
+
types: ["lexical_declaration"],
|
|
2252
|
+
label: "variable",
|
|
2209
2253
|
hasName: true,
|
|
2210
|
-
maxDepth: 0
|
|
2254
|
+
maxDepth: 0,
|
|
2255
|
+
labelByChild: { const: "const", let: "let" }
|
|
2211
2256
|
},
|
|
2257
|
+
{ types: ["variable_declaration"], label: "var", hasName: true, maxDepth: 0 },
|
|
2212
2258
|
{ types: ["import_statement"], label: "import", hasName: true }
|
|
2213
2259
|
],
|
|
2214
2260
|
wrappers: [{ node: "export_statement", prefix: "export ", createStandaloneLabel: "export" }],
|
|
@@ -2222,12 +2268,25 @@ var LANGUAGES = [
|
|
|
2222
2268
|
"export_specifier"
|
|
2223
2269
|
],
|
|
2224
2270
|
identifierTypes: ["identifier", "type_identifier"]
|
|
2271
|
+
},
|
|
2272
|
+
statementDetail: {
|
|
2273
|
+
sourceFields: ["source"],
|
|
2274
|
+
sourceTypes: ["string"],
|
|
2275
|
+
clauseTypes: [
|
|
2276
|
+
"import_clause",
|
|
2277
|
+
"named_imports",
|
|
2278
|
+
"named_exports",
|
|
2279
|
+
"export_clause",
|
|
2280
|
+
"namespace_import"
|
|
2281
|
+
],
|
|
2282
|
+
typeKeywordTypes: ["type"],
|
|
2283
|
+
detailAsNameWhenNoSource: true
|
|
2225
2284
|
}
|
|
2226
2285
|
},
|
|
2227
2286
|
{
|
|
2228
2287
|
name: "Python",
|
|
2229
2288
|
extensions: [".py"],
|
|
2230
|
-
wasmFile:
|
|
2289
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-python.wasm"),
|
|
2231
2290
|
symbols: [
|
|
2232
2291
|
{ types: ["function_definition"], label: "function", hasName: true },
|
|
2233
2292
|
{ types: ["class_definition"], label: "class", hasName: true },
|
|
@@ -2239,36 +2298,46 @@ var LANGUAGES = [
|
|
|
2239
2298
|
fieldPriority: ["name", "module_name"],
|
|
2240
2299
|
recurseTypes: [],
|
|
2241
2300
|
identifierTypes: ["identifier"]
|
|
2242
|
-
}
|
|
2301
|
+
},
|
|
2302
|
+
statementDetail: {
|
|
2303
|
+
sourceFields: ["module_name"],
|
|
2304
|
+
clauseTypes: ["dotted_name", "aliased_import", "wildcard_import"],
|
|
2305
|
+
clausesAsSource: true,
|
|
2306
|
+
textFallbackPattern: "^from\\s+(\\S+)\\s+import\\s+(.+)"
|
|
2307
|
+
},
|
|
2308
|
+
docstrings: [{ nodeType: "expression_statement", prefixes: ['"""', "'''"] }]
|
|
2243
2309
|
},
|
|
2244
2310
|
{
|
|
2245
2311
|
name: "Go",
|
|
2246
2312
|
extensions: [".go"],
|
|
2247
|
-
wasmFile:
|
|
2313
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-go.wasm"),
|
|
2248
2314
|
symbols: [
|
|
2315
|
+
{ types: ["package_clause"], label: "package", hasName: true },
|
|
2249
2316
|
{ types: ["function_declaration"], label: "function", hasName: true },
|
|
2250
2317
|
{ types: ["method_declaration"], label: "method", hasName: true },
|
|
2251
2318
|
{ types: ["type_spec"], label: "type", hasName: true },
|
|
2252
2319
|
{ types: ["field_declaration"], label: "field", hasName: true },
|
|
2320
|
+
{ types: ["const_spec"], label: "const", hasName: true },
|
|
2253
2321
|
{ types: ["import_declaration"], label: "import", hasName: false }
|
|
2254
2322
|
],
|
|
2255
2323
|
wrappers: [],
|
|
2256
2324
|
nameOf: {
|
|
2257
2325
|
fieldPriority: ["name"],
|
|
2258
2326
|
recurseTypes: [],
|
|
2259
|
-
identifierTypes: ["identifier", "type_identifier", "field_identifier"]
|
|
2327
|
+
identifierTypes: ["identifier", "type_identifier", "field_identifier", "package_identifier"]
|
|
2260
2328
|
}
|
|
2261
2329
|
},
|
|
2262
2330
|
{
|
|
2263
2331
|
name: "C",
|
|
2264
2332
|
extensions: [".c", ".h"],
|
|
2265
|
-
wasmFile:
|
|
2333
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-c.wasm"),
|
|
2266
2334
|
symbols: [
|
|
2267
2335
|
{ types: ["function_definition"], label: "function", hasName: true },
|
|
2268
2336
|
{ types: ["struct_specifier"], label: "struct", hasName: true, maxDepth: 0 },
|
|
2269
2337
|
{ types: ["union_specifier"], label: "union", hasName: true, maxDepth: 0 },
|
|
2270
2338
|
{ types: ["enum_specifier"], label: "enum", hasName: true, maxDepth: 0 },
|
|
2271
|
-
{ types: ["type_definition"], label: "typedef", hasName: true }
|
|
2339
|
+
{ types: ["type_definition"], label: "typedef", hasName: true },
|
|
2340
|
+
{ types: ["declaration"], label: "var", hasName: true, maxDepth: 0 }
|
|
2272
2341
|
],
|
|
2273
2342
|
wrappers: [],
|
|
2274
2343
|
nameOf: {
|
|
@@ -2278,6 +2347,7 @@ var LANGUAGES = [
|
|
|
2278
2347
|
"function_declarator",
|
|
2279
2348
|
"array_declarator",
|
|
2280
2349
|
"pointer_declarator",
|
|
2350
|
+
"parenthesized_declarator",
|
|
2281
2351
|
"variable_declarator"
|
|
2282
2352
|
],
|
|
2283
2353
|
identifierTypes: ["identifier", "type_identifier", "field_identifier"]
|
|
@@ -2286,7 +2356,7 @@ var LANGUAGES = [
|
|
|
2286
2356
|
{
|
|
2287
2357
|
name: "C++",
|
|
2288
2358
|
extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".hh"],
|
|
2289
|
-
wasmFile:
|
|
2359
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-cpp.wasm"),
|
|
2290
2360
|
symbols: [
|
|
2291
2361
|
{ types: ["function_definition"], label: "function", hasName: true },
|
|
2292
2362
|
{ types: ["class_specifier"], label: "class", hasName: true },
|
|
@@ -2294,6 +2364,8 @@ var LANGUAGES = [
|
|
|
2294
2364
|
{ types: ["union_specifier"], label: "union", hasName: true, maxDepth: 0 },
|
|
2295
2365
|
{ types: ["enum_specifier"], label: "enum", hasName: true, maxDepth: 0 },
|
|
2296
2366
|
{ types: ["type_definition"], label: "typedef", hasName: true },
|
|
2367
|
+
{ types: ["alias_declaration"], label: "using", hasName: true },
|
|
2368
|
+
{ types: ["declaration"], label: "var", hasName: true, maxDepth: 0 },
|
|
2297
2369
|
{ types: ["namespace_definition"], label: "namespace", hasName: true }
|
|
2298
2370
|
],
|
|
2299
2371
|
wrappers: [],
|
|
@@ -2304,8 +2376,10 @@ var LANGUAGES = [
|
|
|
2304
2376
|
"function_declarator",
|
|
2305
2377
|
"array_declarator",
|
|
2306
2378
|
"pointer_declarator",
|
|
2379
|
+
"parenthesized_declarator",
|
|
2307
2380
|
"reference_declarator",
|
|
2308
|
-
"variable_declarator"
|
|
2381
|
+
"variable_declarator",
|
|
2382
|
+
"init_declarator"
|
|
2309
2383
|
],
|
|
2310
2384
|
identifierTypes: ["identifier", "type_identifier", "field_identifier"],
|
|
2311
2385
|
rawTextTypes: ["destructor_name", "operator_name"]
|
|
@@ -2314,13 +2388,21 @@ var LANGUAGES = [
|
|
|
2314
2388
|
{
|
|
2315
2389
|
name: "Swift",
|
|
2316
2390
|
extensions: [".swift"],
|
|
2317
|
-
wasmFile:
|
|
2391
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-swift.wasm"),
|
|
2318
2392
|
symbols: [
|
|
2319
2393
|
{ types: ["class_declaration"], label: "declaration", hasName: true },
|
|
2320
2394
|
{ types: ["protocol_declaration"], label: "protocol", hasName: true },
|
|
2321
2395
|
{ types: ["function_declaration"], label: "function", hasName: true },
|
|
2322
2396
|
{ types: ["method_declaration"], label: "method", hasName: true },
|
|
2397
|
+
{ types: ["init_declaration"], label: "init", hasName: false },
|
|
2398
|
+
{ types: ["subscript_declaration"], label: "subscript", hasName: false },
|
|
2323
2399
|
{ types: ["typealias_declaration"], label: "typealias", hasName: true },
|
|
2400
|
+
{
|
|
2401
|
+
types: ["property_declaration"],
|
|
2402
|
+
label: "property",
|
|
2403
|
+
hasName: true,
|
|
2404
|
+
labelByChild: { let: "let", var: "var" }
|
|
2405
|
+
},
|
|
2324
2406
|
{ types: ["import_declaration"], label: "import", hasName: true }
|
|
2325
2407
|
],
|
|
2326
2408
|
wrappers: [],
|
|
@@ -2328,31 +2410,93 @@ var LANGUAGES = [
|
|
|
2328
2410
|
fieldPriority: ["name"],
|
|
2329
2411
|
recurseTypes: [],
|
|
2330
2412
|
identifierTypes: ["identifier", "type_identifier"]
|
|
2331
|
-
}
|
|
2413
|
+
},
|
|
2414
|
+
statementDetail: {}
|
|
2332
2415
|
},
|
|
2333
2416
|
{
|
|
2334
2417
|
name: "Kotlin",
|
|
2335
2418
|
extensions: [".kt", ".kts"],
|
|
2336
|
-
wasmFile:
|
|
2419
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-kotlin.wasm"),
|
|
2337
2420
|
symbols: [
|
|
2338
2421
|
{ types: ["class_declaration"], label: "declaration", hasName: true },
|
|
2339
2422
|
{ types: ["function_declaration"], label: "function", hasName: true },
|
|
2340
2423
|
{ types: ["object_declaration"], label: "object", hasName: true },
|
|
2341
2424
|
{ types: ["type_alias"], label: "typealias", hasName: true },
|
|
2425
|
+
{
|
|
2426
|
+
types: ["property_declaration"],
|
|
2427
|
+
label: "property",
|
|
2428
|
+
hasName: true,
|
|
2429
|
+
labelByChild: { val: "val", var: "var" }
|
|
2430
|
+
},
|
|
2342
2431
|
{ types: ["import_header"], label: "import", hasName: true },
|
|
2343
2432
|
{ types: ["package_header"], label: "package", hasName: true }
|
|
2344
2433
|
],
|
|
2345
2434
|
wrappers: [],
|
|
2346
2435
|
nameOf: {
|
|
2347
2436
|
fieldPriority: ["name"],
|
|
2348
|
-
recurseTypes: [],
|
|
2437
|
+
recurseTypes: ["variable_declaration"],
|
|
2349
2438
|
identifierTypes: ["simple_identifier", "type_identifier"]
|
|
2439
|
+
},
|
|
2440
|
+
statementDetail: {
|
|
2441
|
+
labels: ["import", "package"]
|
|
2442
|
+
}
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
name: "Dart",
|
|
2446
|
+
extensions: [".dart"],
|
|
2447
|
+
wasmFile: join10(process.treeSitterWasmPath, "tree-sitter-dart.wasm"),
|
|
2448
|
+
symbols: [
|
|
2449
|
+
{ types: ["class_definition"], label: "class", hasName: true },
|
|
2450
|
+
{ types: ["enum_declaration"], label: "enum", hasName: true },
|
|
2451
|
+
{ types: ["mixin_declaration"], label: "mixin", hasName: true },
|
|
2452
|
+
{ types: ["extension_declaration"], label: "extension", hasName: true },
|
|
2453
|
+
{ types: ["extension_type_declaration"], label: "extension type", hasName: true },
|
|
2454
|
+
{ types: ["function_signature"], label: "function", hasName: true, maxDepth: 0 },
|
|
2455
|
+
{
|
|
2456
|
+
types: ["method_signature"],
|
|
2457
|
+
label: "method",
|
|
2458
|
+
hasName: true,
|
|
2459
|
+
labelByChild: { factory_constructor_signature: "factory" }
|
|
2460
|
+
},
|
|
2461
|
+
{ types: ["getter_signature"], label: "getter", hasName: true },
|
|
2462
|
+
{ types: ["setter_signature"], label: "setter", hasName: true },
|
|
2463
|
+
{
|
|
2464
|
+
types: ["constructor_signature", "constant_constructor_signature"],
|
|
2465
|
+
label: "constructor",
|
|
2466
|
+
hasName: true
|
|
2467
|
+
},
|
|
2468
|
+
{ types: ["type_alias"], label: "typedef", hasName: true },
|
|
2469
|
+
{ types: ["library_import"], label: "import", hasName: true },
|
|
2470
|
+
{ types: ["library_export"], label: "export", hasName: true }
|
|
2471
|
+
],
|
|
2472
|
+
wrappers: [],
|
|
2473
|
+
nameOf: {
|
|
2474
|
+
fieldPriority: ["name"],
|
|
2475
|
+
recurseTypes: [
|
|
2476
|
+
"function_signature",
|
|
2477
|
+
"factory_constructor_signature",
|
|
2478
|
+
"getter_signature",
|
|
2479
|
+
"setter_signature",
|
|
2480
|
+
"library_import",
|
|
2481
|
+
"library_export",
|
|
2482
|
+
"import_specification",
|
|
2483
|
+
"configurable_uri",
|
|
2484
|
+
"uri"
|
|
2485
|
+
],
|
|
2486
|
+
identifierTypes: ["identifier", "type_identifier"],
|
|
2487
|
+
rawTextTypes: ["string_literal"]
|
|
2488
|
+
},
|
|
2489
|
+
statementDetail: {
|
|
2490
|
+
labels: ["import", "export", "factory"],
|
|
2491
|
+
sourceTypes: ["configurable_uri", "uri", "import_specification", "string_literal"],
|
|
2492
|
+
clauseTypes: ["combinator"],
|
|
2493
|
+
trimPatterns: ["\\(.*$"]
|
|
2350
2494
|
}
|
|
2351
2495
|
}
|
|
2352
2496
|
];
|
|
2353
2497
|
async function parseInChild(filePath, config, cwd, timeout = 3e4) {
|
|
2354
2498
|
return new Promise((resolve6, reject) => {
|
|
2355
|
-
const modulePath =
|
|
2499
|
+
const modulePath = join10(process.scriptsPath, "/worker-file-outline/worker.mjs");
|
|
2356
2500
|
const child = fork(modulePath, [], { execArgv: [], cwd });
|
|
2357
2501
|
const timer = setTimeout(() => {
|
|
2358
2502
|
child.kill();
|
|
@@ -2376,14 +2520,16 @@ async function parseInChild(filePath, config, cwd, timeout = 3e4) {
|
|
|
2376
2520
|
wasmFile: config.wasmFile,
|
|
2377
2521
|
symbols: config.symbols,
|
|
2378
2522
|
wrappers: config.wrappers,
|
|
2379
|
-
nameOf: config.nameOf
|
|
2523
|
+
nameOf: config.nameOf,
|
|
2524
|
+
statementDetail: config.statementDetail,
|
|
2525
|
+
docstrings: config.docstrings
|
|
2380
2526
|
};
|
|
2381
2527
|
child.send(request);
|
|
2382
2528
|
});
|
|
2383
2529
|
}
|
|
2384
2530
|
function parseMarkdown(content) {
|
|
2385
2531
|
const lines = content.split("\n");
|
|
2386
|
-
const totalLines = lines.length;
|
|
2532
|
+
const totalLines = content.endsWith("\n") ? lines.length - 1 : lines.length;
|
|
2387
2533
|
const symbols = [];
|
|
2388
2534
|
let inFence = false;
|
|
2389
2535
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -2498,9 +2644,12 @@ function outlineToSummary(outline, maxSymbols = 128) {
|
|
|
2498
2644
|
for (const sym of shown) {
|
|
2499
2645
|
const indent = " ".repeat(sym.depth);
|
|
2500
2646
|
const lr = sym.startLine === sym.endLine ? `line ${sym.startLine}` : `lines ${sym.startLine}-${sym.endLine}`;
|
|
2501
|
-
const cmt = sym.comment ? ` // ${sym.comment}` : "";
|
|
2502
2647
|
const namePart = sym.name ? ` ${sym.name}` : "";
|
|
2503
|
-
|
|
2648
|
+
const detailPart = sym.detail ? `: ${sym.detail}` : "";
|
|
2649
|
+
if (sym.comment) {
|
|
2650
|
+
l.push(`${indent}# ${sym.comment}`);
|
|
2651
|
+
}
|
|
2652
|
+
l.push(`${indent}${sym.kind}${namePart}${detailPart} (${lr})`);
|
|
2504
2653
|
}
|
|
2505
2654
|
if (outline.symbols.length > maxSymbols)
|
|
2506
2655
|
l.push(` ... and ${outline.symbols.length - maxSymbols} more symbols`);
|
|
@@ -2513,12 +2662,12 @@ async function fileOutline(options) {
|
|
|
2513
2662
|
}
|
|
2514
2663
|
|
|
2515
2664
|
// src/backend/search/ripgrep.ts
|
|
2516
|
-
import { join as
|
|
2665
|
+
import { join as join11, isAbsolute as isAbsolute5, resolve as resolve4, relative as relative6 } from "path";
|
|
2517
2666
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2518
2667
|
import { fork as fork2 } from "child_process";
|
|
2519
2668
|
function ripgrepInChild(args2, cwd, timeout = 3e4) {
|
|
2520
2669
|
return new Promise((resolve6, reject) => {
|
|
2521
|
-
const child = fork2(
|
|
2670
|
+
const child = fork2(join11(process.scriptsPath, "worker-ripgrep/worker.mjs"), [], { cwd });
|
|
2522
2671
|
const timer = setTimeout(() => {
|
|
2523
2672
|
child.kill();
|
|
2524
2673
|
reject(new Error(`ripgrep timed out (${timeout}ms)`));
|
|
@@ -2588,20 +2737,20 @@ async function rg(options) {
|
|
|
2588
2737
|
function createSearchModule(_ctx) {
|
|
2589
2738
|
return { registerSearchRoute, buildSearchMcpServer };
|
|
2590
2739
|
}
|
|
2591
|
-
function registerSearchRoute(server,
|
|
2740
|
+
function registerSearchRoute(server, projectDir) {
|
|
2592
2741
|
server.registry("search/search", async (payload) => {
|
|
2593
2742
|
const params = searchRequestSchema.parse(payload);
|
|
2594
|
-
const result = await search({ projectDir
|
|
2743
|
+
const result = await search({ projectDir, ...params });
|
|
2595
2744
|
return searchRespondSchema.parse(result);
|
|
2596
2745
|
});
|
|
2597
2746
|
server.registry("search/rg", async (payload) => {
|
|
2598
2747
|
const params = rgRequestSchema.parse(payload);
|
|
2599
|
-
const result = await rg({ projectDir
|
|
2748
|
+
const result = await rg({ projectDir, ...params });
|
|
2600
2749
|
return rgRespondSchema.parse(result);
|
|
2601
2750
|
});
|
|
2602
2751
|
server.registry("search/file_outline", async (payload) => {
|
|
2603
2752
|
const params = fileOutlineRequestSchema.parse(payload);
|
|
2604
|
-
const result = await fileOutline({ projectDir
|
|
2753
|
+
const result = await fileOutline({ projectDir, ...params });
|
|
2605
2754
|
return fileOutlineRespondSchema.parse(result);
|
|
2606
2755
|
});
|
|
2607
2756
|
}
|
|
@@ -2610,7 +2759,7 @@ function buildSearchMcpServer(options) {
|
|
|
2610
2759
|
name: "search",
|
|
2611
2760
|
command: process.execPath,
|
|
2612
2761
|
args: [
|
|
2613
|
-
|
|
2762
|
+
join12(process.scriptsPath, "mcp-search/server.mjs"),
|
|
2614
2763
|
"--project-dir",
|
|
2615
2764
|
options.projectDir,
|
|
2616
2765
|
"--socket-path",
|
|
@@ -2622,7 +2771,7 @@ function buildSearchMcpServer(options) {
|
|
|
2622
2771
|
|
|
2623
2772
|
// src/backend/ask-user.ts
|
|
2624
2773
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2625
|
-
import { join as
|
|
2774
|
+
import { join as join13 } from "path";
|
|
2626
2775
|
|
|
2627
2776
|
// src/shared/zod/mcp-ask-user-schema.ts
|
|
2628
2777
|
import { z as z2 } from "zod";
|
|
@@ -2746,7 +2895,7 @@ function createAskUserModule(ctx, deps) {
|
|
|
2746
2895
|
name: "ask-user",
|
|
2747
2896
|
command: process.execPath,
|
|
2748
2897
|
args: [
|
|
2749
|
-
|
|
2898
|
+
join13(process.scriptsPath, "mcp-ask-user/server.mjs"),
|
|
2750
2899
|
"--project-dir",
|
|
2751
2900
|
options.projectDir,
|
|
2752
2901
|
"--socket-path",
|
|
@@ -2766,10 +2915,10 @@ function createAskUserModule(ctx, deps) {
|
|
|
2766
2915
|
|
|
2767
2916
|
// src/backend/share-to-user.ts
|
|
2768
2917
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2769
|
-
import { join as
|
|
2918
|
+
import { join as join14 } from "path";
|
|
2770
2919
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
2771
2920
|
import { readFile as readFile5, writeFile } from "fs/promises";
|
|
2772
|
-
import { existsSync as
|
|
2921
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
2773
2922
|
import * as mimeTypes3 from "mime-types";
|
|
2774
2923
|
|
|
2775
2924
|
// src/shared/zod/mcp-share-to-user-schema.ts
|
|
@@ -2824,7 +2973,7 @@ function createShareToUserModule(ctx, deps) {
|
|
|
2824
2973
|
const session = storage.getSession(sessionId);
|
|
2825
2974
|
if (session) {
|
|
2826
2975
|
deps.ilink.appendMediaBuffer({
|
|
2827
|
-
filePath:
|
|
2976
|
+
filePath: join14(session.cwd, uri),
|
|
2828
2977
|
name,
|
|
2829
2978
|
toUserId,
|
|
2830
2979
|
mimeType: resolvedMimeType
|
|
@@ -2837,10 +2986,10 @@ function createShareToUserModule(ctx, deps) {
|
|
|
2837
2986
|
const shareId = randomUUID3();
|
|
2838
2987
|
const shareDir = storage.sessionShareDir(sessionId);
|
|
2839
2988
|
if (!shareDir) throw new Error(`Session not found: ${sessionId}`);
|
|
2840
|
-
const targetDir =
|
|
2841
|
-
const targetPath =
|
|
2842
|
-
if (!
|
|
2843
|
-
|
|
2989
|
+
const targetDir = join14(shareDir, shareId);
|
|
2990
|
+
const targetPath = join14(targetDir, name);
|
|
2991
|
+
if (!existsSync6(targetDir)) {
|
|
2992
|
+
mkdirSync6(targetDir, { recursive: true });
|
|
2844
2993
|
}
|
|
2845
2994
|
let buffer;
|
|
2846
2995
|
if (type === "link" && uri) {
|
|
@@ -2893,7 +3042,7 @@ function createShareToUserModule(ctx, deps) {
|
|
|
2893
3042
|
name: "share-to-user",
|
|
2894
3043
|
command: process.execPath,
|
|
2895
3044
|
args: [
|
|
2896
|
-
|
|
3045
|
+
join14(process.scriptsPath, "mcp-share-to-user/server.mjs"),
|
|
2897
3046
|
"--project-dir",
|
|
2898
3047
|
options.projectDir,
|
|
2899
3048
|
"--socket-path",
|
|
@@ -2909,7 +3058,8 @@ function createShareToUserModule(ctx, deps) {
|
|
|
2909
3058
|
import { writeFile as writeFile2, readFile as readFile6 } from "fs/promises";
|
|
2910
3059
|
import {
|
|
2911
3060
|
ndJsonStream as ndJsonStream2,
|
|
2912
|
-
|
|
3061
|
+
client,
|
|
3062
|
+
methods as methods3,
|
|
2913
3063
|
PROTOCOL_VERSION as PROTOCOL_VERSION2
|
|
2914
3064
|
} from "@agentclientprotocol/sdk";
|
|
2915
3065
|
|
|
@@ -2919,14 +3069,18 @@ import { Writable, Readable } from "stream";
|
|
|
2919
3069
|
function spawnStdioAgent(options) {
|
|
2920
3070
|
const shouldDetach = process.platform !== "win32";
|
|
2921
3071
|
const proc = spawn(options.command, options.args, {
|
|
2922
|
-
stdio: ["pipe", "pipe", "
|
|
2923
|
-
cwd:
|
|
3072
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3073
|
+
cwd: options.cwd,
|
|
2924
3074
|
env: { ...process.env, ...options.env },
|
|
2925
|
-
detached: shouldDetach
|
|
3075
|
+
detached: shouldDetach,
|
|
3076
|
+
windowsHide: true,
|
|
3077
|
+
shell: !shouldDetach && process.platform === "win32"
|
|
3078
|
+
// Windows 上用 shell 解析命令
|
|
2926
3079
|
});
|
|
2927
3080
|
if (shouldDetach) {
|
|
2928
3081
|
proc.unref();
|
|
2929
3082
|
}
|
|
3083
|
+
proc.stderr?.resume();
|
|
2930
3084
|
let closed = false;
|
|
2931
3085
|
async function close() {
|
|
2932
3086
|
if (closed) {
|
|
@@ -2994,16 +3148,80 @@ function spawnStdioAgent(options) {
|
|
|
2994
3148
|
}
|
|
2995
3149
|
|
|
2996
3150
|
// src/backend/agent/openai-compatible-api-agent.ts
|
|
2997
|
-
import {
|
|
3151
|
+
import { agent, ndJsonStream, methods as methods2 } from "@agentclientprotocol/sdk";
|
|
2998
3152
|
|
|
2999
3153
|
// src/agents/openai-compatible-agent.ts
|
|
3000
|
-
import { randomUUID as
|
|
3001
|
-
import { readFileSync as
|
|
3002
|
-
import { join as
|
|
3154
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
3155
|
+
import { readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
3156
|
+
import { join as join15 } from "path";
|
|
3003
3157
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
3004
|
-
import {
|
|
3158
|
+
import {
|
|
3159
|
+
isStepCount as isStepCount2,
|
|
3160
|
+
streamText as streamText2,
|
|
3161
|
+
generateText
|
|
3162
|
+
} from "ai";
|
|
3005
3163
|
import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
3006
3164
|
|
|
3165
|
+
// src/agents/agent-client-proxy.ts
|
|
3166
|
+
import {
|
|
3167
|
+
methods
|
|
3168
|
+
} from "@agentclientprotocol/sdk";
|
|
3169
|
+
var TerminalHandle = class {
|
|
3170
|
+
constructor(id, sessionId, ctx) {
|
|
3171
|
+
this.id = id;
|
|
3172
|
+
this.sessionId = sessionId;
|
|
3173
|
+
this.ctx = ctx;
|
|
3174
|
+
}
|
|
3175
|
+
async currentOutput() {
|
|
3176
|
+
return this.ctx.request(methods.client.terminal.output, {
|
|
3177
|
+
sessionId: this.sessionId,
|
|
3178
|
+
terminalId: this.id
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
async waitForExit() {
|
|
3182
|
+
return this.ctx.request(methods.client.terminal.waitForExit, {
|
|
3183
|
+
sessionId: this.sessionId,
|
|
3184
|
+
terminalId: this.id
|
|
3185
|
+
});
|
|
3186
|
+
}
|
|
3187
|
+
async kill() {
|
|
3188
|
+
return this.ctx.request(methods.client.terminal.kill, {
|
|
3189
|
+
sessionId: this.sessionId,
|
|
3190
|
+
terminalId: this.id
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
async release() {
|
|
3194
|
+
return this.ctx.request(methods.client.terminal.release, {
|
|
3195
|
+
sessionId: this.sessionId,
|
|
3196
|
+
terminalId: this.id
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
};
|
|
3200
|
+
var AgentClientProxy = class {
|
|
3201
|
+
constructor(ctx) {
|
|
3202
|
+
this.ctx = ctx;
|
|
3203
|
+
}
|
|
3204
|
+
async sessionUpdate(params) {
|
|
3205
|
+
await this.ctx.notify(methods.client.session.update, params);
|
|
3206
|
+
}
|
|
3207
|
+
async requestPermission(params) {
|
|
3208
|
+
return this.ctx.request(methods.client.session.requestPermission, params);
|
|
3209
|
+
}
|
|
3210
|
+
async readTextFile(params) {
|
|
3211
|
+
return this.ctx.request(methods.client.fs.readTextFile, params);
|
|
3212
|
+
}
|
|
3213
|
+
async writeTextFile(params) {
|
|
3214
|
+
return this.ctx.request(methods.client.fs.writeTextFile, params);
|
|
3215
|
+
}
|
|
3216
|
+
async createTerminal(params) {
|
|
3217
|
+
const response = await this.ctx.request(
|
|
3218
|
+
methods.client.terminal.create,
|
|
3219
|
+
params
|
|
3220
|
+
);
|
|
3221
|
+
return new TerminalHandle(response.terminalId, params.sessionId, this.ctx);
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
|
|
3007
3225
|
// src/agents/acp-client-tools.ts
|
|
3008
3226
|
import { resolve as resolve5 } from "path";
|
|
3009
3227
|
import { tool } from "ai";
|
|
@@ -3120,14 +3338,39 @@ function resourceLinkToFilePart(resourceLink) {
|
|
|
3120
3338
|
}
|
|
3121
3339
|
function filePartToEmbeddedResourceResource(filePart) {
|
|
3122
3340
|
let blob;
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3341
|
+
const data = filePart.data;
|
|
3342
|
+
if (typeof data === "string") {
|
|
3343
|
+
blob = data;
|
|
3344
|
+
} else if (data instanceof URL) {
|
|
3345
|
+
blob = data.toString();
|
|
3346
|
+
} else if (data instanceof ArrayBuffer) {
|
|
3347
|
+
blob = Buffer.from(new Uint8Array(data)).toString("base64");
|
|
3348
|
+
} else if (data instanceof Uint8Array) {
|
|
3349
|
+
blob = Buffer.from(data).toString("base64");
|
|
3350
|
+
} else if (typeof data === "object" && data !== null && "type" in data) {
|
|
3351
|
+
const tagged = data;
|
|
3352
|
+
switch (tagged.type) {
|
|
3353
|
+
case "data":
|
|
3354
|
+
if (typeof tagged.data === "string") {
|
|
3355
|
+
blob = tagged.data;
|
|
3356
|
+
} else if (tagged.data instanceof Uint8Array) {
|
|
3357
|
+
blob = Buffer.from(tagged.data).toString("base64");
|
|
3358
|
+
} else {
|
|
3359
|
+
blob = String(tagged.data ?? "");
|
|
3360
|
+
}
|
|
3361
|
+
break;
|
|
3362
|
+
case "url":
|
|
3363
|
+
blob = tagged.url?.toString() ?? "";
|
|
3364
|
+
break;
|
|
3365
|
+
case "text":
|
|
3366
|
+
blob = tagged.text ?? "";
|
|
3367
|
+
break;
|
|
3368
|
+
default:
|
|
3369
|
+
blob = JSON.stringify(data);
|
|
3370
|
+
break;
|
|
3371
|
+
}
|
|
3129
3372
|
} else {
|
|
3130
|
-
blob =
|
|
3373
|
+
blob = JSON.stringify(data);
|
|
3131
3374
|
}
|
|
3132
3375
|
return {
|
|
3133
3376
|
resource: {
|
|
@@ -3724,7 +3967,7 @@ async function createClient(server, cwd) {
|
|
|
3724
3967
|
});
|
|
3725
3968
|
}
|
|
3726
3969
|
async function closeClients(clients) {
|
|
3727
|
-
await Promise.all(clients.map((
|
|
3970
|
+
await Promise.all(clients.map((client2) => client2.close().catch(() => {
|
|
3728
3971
|
})));
|
|
3729
3972
|
}
|
|
3730
3973
|
async function createMCPSessionTools(params) {
|
|
@@ -3743,10 +3986,10 @@ async function createMCPSessionTools(params) {
|
|
|
3743
3986
|
}
|
|
3744
3987
|
const allTools = {};
|
|
3745
3988
|
for (let i = 0; i < clients.length; i++) {
|
|
3746
|
-
const
|
|
3989
|
+
const client2 = clients[i];
|
|
3747
3990
|
const server = params.mcpServers[i];
|
|
3748
3991
|
const prefix = sanitizeName(server.name || `server_${i + 1}`) || `server_${i + 1}`;
|
|
3749
|
-
const [serverTools, definitions] = await Promise.all([
|
|
3992
|
+
const [serverTools, definitions] = await Promise.all([client2.tools(), client2.listTools()]);
|
|
3750
3993
|
const definitionsByName = new Map(definitions.tools.map((item) => [item.name, item]));
|
|
3751
3994
|
for (const [toolName, toolDef] of Object.entries(serverTools)) {
|
|
3752
3995
|
const qualifiedName = `mcp_${prefix}__${toolName}`;
|
|
@@ -3962,15 +4205,282 @@ Working style:
|
|
|
3962
4205
|
|
|
3963
4206
|
File reading & editing strategy:
|
|
3964
4207
|
- ReadFile will REJECT full reads of files over 100KB. Use line/limit to read specific sections, or set force=true if you genuinely need the full file content (rare).
|
|
3965
|
-
- Use the Search MCP's file_outline tool to preview file structure before reading (function/class/interface names with line ranges). Supports TypeScript, JavaScript, TSX, Python, Go, C, C++, Swift, Kotlin.
|
|
4208
|
+
- Use the Search MCP's file_outline tool to preview file structure before reading (function/class/interface names with line ranges). Supports TypeScript, JavaScript, TSX, Python, Go, C, C++, Swift, Kotlin, Dart.
|
|
3966
4209
|
- Use the Search MCP's search tool (structured) or rg tool (raw args) to search file contents instead of reading entire files.
|
|
3967
4210
|
- EditFile uses exact string matching (StrReplace style). Use unique surrounding code context in oldText to make matches precise. Set replaceAll=true when you intend to replace all occurrences.
|
|
3968
4211
|
- You do NOT need to re-read a file after editing it \u2014 the changes are already saved.
|
|
3969
4212
|
|
|
4213
|
+
Memory:
|
|
4214
|
+
- If memory_query/memory_store tools are available, you have persistent project memory that survives across sessions.
|
|
4215
|
+
- Use memory_query at the start of complex or unfamiliar tasks to recall project conventions and user preferences.
|
|
4216
|
+
- Use memory_store when the user corrects you, states a preference, or when you discover an important project convention.
|
|
4217
|
+
- Keep stored facts concise (one sentence each) and actionable.
|
|
4218
|
+
|
|
3970
4219
|
Safety:
|
|
3971
4220
|
- Do not perform destructive actions unless explicitly requested.
|
|
3972
4221
|
- Do not fabricate results; if something fails, report the real error.`;
|
|
3973
4222
|
|
|
4223
|
+
// src/agents/subagent-tool.ts
|
|
4224
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
4225
|
+
import { streamText, isStepCount } from "ai";
|
|
4226
|
+
import { tool as tool2 } from "ai";
|
|
4227
|
+
import { z as z5 } from "zod";
|
|
4228
|
+
function createSubsessionConnectionProxy(getConnection, parentSessionId, subSessionId) {
|
|
4229
|
+
return () => {
|
|
4230
|
+
const real = getConnection();
|
|
4231
|
+
if (!real) return null;
|
|
4232
|
+
return new Proxy(real, {
|
|
4233
|
+
get(target, prop, receiver) {
|
|
4234
|
+
if (prop === "sessionUpdate") {
|
|
4235
|
+
return async (params) => {
|
|
4236
|
+
const rewritten = { ...params };
|
|
4237
|
+
if (rewritten.sessionId === parentSessionId) {
|
|
4238
|
+
rewritten.sessionId = subSessionId;
|
|
4239
|
+
}
|
|
4240
|
+
return target.sessionUpdate(rewritten);
|
|
4241
|
+
};
|
|
4242
|
+
}
|
|
4243
|
+
if (prop === "requestPermission") {
|
|
4244
|
+
return async (params) => {
|
|
4245
|
+
const rewritten = { ...params };
|
|
4246
|
+
if (rewritten.sessionId === parentSessionId) {
|
|
4247
|
+
rewritten.sessionId = subSessionId;
|
|
4248
|
+
}
|
|
4249
|
+
return target.requestPermission(rewritten);
|
|
4250
|
+
};
|
|
4251
|
+
}
|
|
4252
|
+
return Reflect.get(target, prop, receiver);
|
|
4253
|
+
}
|
|
4254
|
+
});
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4257
|
+
function sendSubagentStatusUpdate(connection, sessionId, subSessionId, name, prompt, status) {
|
|
4258
|
+
const update = {
|
|
4259
|
+
sessionUpdate: "subagent_update",
|
|
4260
|
+
sessionId: subSessionId,
|
|
4261
|
+
name,
|
|
4262
|
+
prompt,
|
|
4263
|
+
status
|
|
4264
|
+
};
|
|
4265
|
+
return connection.sessionUpdate({
|
|
4266
|
+
sessionId,
|
|
4267
|
+
update: {
|
|
4268
|
+
sessionUpdate: "session_info_update",
|
|
4269
|
+
_meta: {
|
|
4270
|
+
fello: {
|
|
4271
|
+
update
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
4277
|
+
async function executeSubagent(params, subSessionId, taskName, taskPrompt, signal) {
|
|
4278
|
+
const connection = params.getConnection();
|
|
4279
|
+
if (!connection) {
|
|
4280
|
+
throw new Error("ACP connection is not available.");
|
|
4281
|
+
}
|
|
4282
|
+
await sendSubagentStatusUpdate(
|
|
4283
|
+
connection,
|
|
4284
|
+
params.sessionId,
|
|
4285
|
+
subSessionId,
|
|
4286
|
+
taskName,
|
|
4287
|
+
taskPrompt,
|
|
4288
|
+
"in_progress"
|
|
4289
|
+
);
|
|
4290
|
+
const proxyGetConnection = createSubsessionConnectionProxy(
|
|
4291
|
+
params.getConnection,
|
|
4292
|
+
params.sessionId,
|
|
4293
|
+
subSessionId
|
|
4294
|
+
);
|
|
4295
|
+
const subAcp = createACPClientTools({
|
|
4296
|
+
cwd: params.cwd,
|
|
4297
|
+
sessionId: subSessionId,
|
|
4298
|
+
getConnection: proxyGetConnection,
|
|
4299
|
+
permissionMemory: params.permissionMemory
|
|
4300
|
+
});
|
|
4301
|
+
let accumulatedText = "";
|
|
4302
|
+
try {
|
|
4303
|
+
const result = streamText({
|
|
4304
|
+
model: params.getModel(),
|
|
4305
|
+
system: params.systemPrompt,
|
|
4306
|
+
prompt: taskPrompt,
|
|
4307
|
+
tools: {
|
|
4308
|
+
...subAcp.tools
|
|
4309
|
+
},
|
|
4310
|
+
stopWhen: isStepCount(64),
|
|
4311
|
+
abortSignal: signal
|
|
4312
|
+
});
|
|
4313
|
+
for await (const part of result.stream) {
|
|
4314
|
+
const conn = params.getConnection();
|
|
4315
|
+
if (!conn) continue;
|
|
4316
|
+
if (part.type === "text-delta") {
|
|
4317
|
+
if (!part.text) continue;
|
|
4318
|
+
accumulatedText += part.text;
|
|
4319
|
+
await conn.sessionUpdate({
|
|
4320
|
+
sessionId: subSessionId,
|
|
4321
|
+
update: {
|
|
4322
|
+
sessionUpdate: "agent_message_chunk",
|
|
4323
|
+
content: { type: "text", text: part.text }
|
|
4324
|
+
}
|
|
4325
|
+
});
|
|
4326
|
+
continue;
|
|
4327
|
+
}
|
|
4328
|
+
if (part.type === "reasoning-delta") {
|
|
4329
|
+
if (!part.text) continue;
|
|
4330
|
+
await conn.sessionUpdate({
|
|
4331
|
+
sessionId: subSessionId,
|
|
4332
|
+
update: {
|
|
4333
|
+
sessionUpdate: "agent_thought_chunk",
|
|
4334
|
+
content: { type: "text", text: part.text }
|
|
4335
|
+
}
|
|
4336
|
+
});
|
|
4337
|
+
continue;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
const conn2 = params.getConnection();
|
|
4341
|
+
if (conn2) {
|
|
4342
|
+
await sendSubagentStatusUpdate(
|
|
4343
|
+
conn2,
|
|
4344
|
+
params.sessionId,
|
|
4345
|
+
subSessionId,
|
|
4346
|
+
taskName,
|
|
4347
|
+
taskPrompt,
|
|
4348
|
+
"completed"
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
return accumulatedText || "[No output produced]";
|
|
4352
|
+
} catch (error) {
|
|
4353
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4354
|
+
const conn3 = params.getConnection();
|
|
4355
|
+
if (conn3) {
|
|
4356
|
+
await sendSubagentStatusUpdate(
|
|
4357
|
+
conn3,
|
|
4358
|
+
params.sessionId,
|
|
4359
|
+
subSessionId,
|
|
4360
|
+
taskName,
|
|
4361
|
+
taskPrompt,
|
|
4362
|
+
signal.aborted ? "completed" : "failed"
|
|
4363
|
+
);
|
|
4364
|
+
if (!signal.aborted) {
|
|
4365
|
+
await conn3.sessionUpdate({
|
|
4366
|
+
sessionId: subSessionId,
|
|
4367
|
+
update: {
|
|
4368
|
+
sessionUpdate: "agent_message_chunk",
|
|
4369
|
+
content: { type: "text", text: `
|
|
4370
|
+
|
|
4371
|
+
**Error:** ${errorMessage}` }
|
|
4372
|
+
}
|
|
4373
|
+
});
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4376
|
+
if (signal.aborted) {
|
|
4377
|
+
return accumulatedText || "[Cancelled]";
|
|
4378
|
+
}
|
|
4379
|
+
return accumulatedText || `[Error: ${errorMessage}]`;
|
|
4380
|
+
} finally {
|
|
4381
|
+
await closeACPClientTools(subAcp);
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
function createSubagentTool(params) {
|
|
4385
|
+
return {
|
|
4386
|
+
Subagent: tool2({
|
|
4387
|
+
description: `Spawn one or more independent AI sub-agents to work on tasks in parallel.
|
|
4388
|
+
Each sub-agent runs with its own context and has access to file read/write, edit, and shell tools.
|
|
4389
|
+
Use this when you need to:
|
|
4390
|
+
- Parallelize independent work (e.g., implement + test, research multiple topics)
|
|
4391
|
+
- Delegate focused sub-tasks while you coordinate the overall approach
|
|
4392
|
+
- Run longer operations without blocking other work
|
|
4393
|
+
|
|
4394
|
+
Each task gets a name (short identifier) and a prompt (detailed instructions).
|
|
4395
|
+
Sub-agents do NOT have access to the parent conversation history \u2014 include all necessary context in the prompt.
|
|
4396
|
+
Results from all sub-agents are returned when they all complete.`,
|
|
4397
|
+
inputSchema: z5.object({
|
|
4398
|
+
purpose: z5.string().describe("Brief description of the purpose for spawning sub-agents."),
|
|
4399
|
+
tasks: z5.array(
|
|
4400
|
+
z5.object({
|
|
4401
|
+
name: z5.string().describe("Short name for this sub-task (shown in UI)."),
|
|
4402
|
+
prompt: z5.string().describe(
|
|
4403
|
+
"Detailed instructions for the sub-agent. Include all context it needs since it has no access to the parent conversation history."
|
|
4404
|
+
)
|
|
4405
|
+
})
|
|
4406
|
+
).min(1).max(8).describe("List of tasks to execute in parallel. Min 1, max 8.")
|
|
4407
|
+
}),
|
|
4408
|
+
execute: async ({ purpose, tasks }, { toolCallId }) => {
|
|
4409
|
+
const connection = params.getConnection();
|
|
4410
|
+
if (!connection) {
|
|
4411
|
+
throw new Error("ACP connection is not available.");
|
|
4412
|
+
}
|
|
4413
|
+
const title = `Subagent: ${purpose}`;
|
|
4414
|
+
await connection.sessionUpdate({
|
|
4415
|
+
sessionId: params.sessionId,
|
|
4416
|
+
update: {
|
|
4417
|
+
sessionUpdate: "tool_call",
|
|
4418
|
+
toolCallId,
|
|
4419
|
+
title,
|
|
4420
|
+
kind: "other",
|
|
4421
|
+
status: "in_progress",
|
|
4422
|
+
rawInput: { purpose, tasks: tasks.map((t2) => ({ name: t2.name, prompt: t2.prompt })) }
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
const subagents = tasks.map((task) => ({
|
|
4426
|
+
subSessionId: randomUUID5(),
|
|
4427
|
+
name: task.name,
|
|
4428
|
+
prompt: task.prompt
|
|
4429
|
+
}));
|
|
4430
|
+
await Promise.all(
|
|
4431
|
+
subagents.map(
|
|
4432
|
+
(sa) => sendSubagentStatusUpdate(
|
|
4433
|
+
connection,
|
|
4434
|
+
params.sessionId,
|
|
4435
|
+
sa.subSessionId,
|
|
4436
|
+
sa.name,
|
|
4437
|
+
sa.prompt,
|
|
4438
|
+
"pending"
|
|
4439
|
+
)
|
|
4440
|
+
)
|
|
4441
|
+
);
|
|
4442
|
+
const results = await Promise.allSettled(
|
|
4443
|
+
subagents.map(
|
|
4444
|
+
(sa) => executeSubagent(
|
|
4445
|
+
params,
|
|
4446
|
+
sa.subSessionId,
|
|
4447
|
+
sa.name,
|
|
4448
|
+
sa.prompt,
|
|
4449
|
+
params.parentSignal ?? new AbortController().signal
|
|
4450
|
+
)
|
|
4451
|
+
)
|
|
4452
|
+
);
|
|
4453
|
+
const output = subagents.map((sa, i) => {
|
|
4454
|
+
const result = results[i];
|
|
4455
|
+
if (result.status === "fulfilled") {
|
|
4456
|
+
return {
|
|
4457
|
+
name: sa.name,
|
|
4458
|
+
status: "completed",
|
|
4459
|
+
result: result.value
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
return {
|
|
4463
|
+
name: sa.name,
|
|
4464
|
+
status: "failed",
|
|
4465
|
+
result: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
4466
|
+
};
|
|
4467
|
+
});
|
|
4468
|
+
const allSucceeded = output.every((o) => o.status === "completed");
|
|
4469
|
+
await connection.sessionUpdate({
|
|
4470
|
+
sessionId: params.sessionId,
|
|
4471
|
+
update: {
|
|
4472
|
+
sessionUpdate: "tool_call_update",
|
|
4473
|
+
toolCallId,
|
|
4474
|
+
status: allSucceeded ? "completed" : "failed",
|
|
4475
|
+
rawOutput: output
|
|
4476
|
+
}
|
|
4477
|
+
});
|
|
4478
|
+
return output;
|
|
4479
|
+
}
|
|
4480
|
+
})
|
|
4481
|
+
};
|
|
4482
|
+
}
|
|
4483
|
+
|
|
3974
4484
|
// src/agents/openai-compatible-agent.ts
|
|
3975
4485
|
var MODEL_CONFIG_ID = "model";
|
|
3976
4486
|
var DEFAULT_CONTEXT_WINDOW_TOKENS = 128e3;
|
|
@@ -3997,9 +4507,9 @@ ${workspacePrompt}
|
|
|
3997
4507
|
|
|
3998
4508
|
${envContext}`;
|
|
3999
4509
|
try {
|
|
4000
|
-
const agentsMdPath =
|
|
4510
|
+
const agentsMdPath = join15(cwd, "AGENTS.md");
|
|
4001
4511
|
if (statSync4(agentsMdPath).size <= 50 * 1024) {
|
|
4002
|
-
const agentsMd =
|
|
4512
|
+
const agentsMd = readFileSync6(agentsMdPath, "utf-8").trim();
|
|
4003
4513
|
if (agentsMd) {
|
|
4004
4514
|
prompt += `
|
|
4005
4515
|
|
|
@@ -4026,6 +4536,37 @@ function applyModelIdTemplate(template, modelItem) {
|
|
|
4026
4536
|
});
|
|
4027
4537
|
}
|
|
4028
4538
|
var AgentDescription = "Fello/0.1.1 CodeAgent OpenaiCompatibleAgent opencode codex";
|
|
4539
|
+
function buildSafeUserMessage(prompt) {
|
|
4540
|
+
const safeContent = prompt.map((block) => {
|
|
4541
|
+
if (block.type === "text") {
|
|
4542
|
+
return textContentToTextPart(block);
|
|
4543
|
+
}
|
|
4544
|
+
if (block.type === "image") {
|
|
4545
|
+
const desc = block.uri ? `[Image: ${block.uri}]` : `[Image]`;
|
|
4546
|
+
return textContentToTextPart({ text: desc });
|
|
4547
|
+
}
|
|
4548
|
+
if (block.type === "audio") {
|
|
4549
|
+
return textContentToTextPart({ text: `[Audio]` });
|
|
4550
|
+
}
|
|
4551
|
+
if (block.type === "resource") {
|
|
4552
|
+
const resource = block.resource;
|
|
4553
|
+
if ("text" in resource) {
|
|
4554
|
+
return textContentToTextPart({ text: resource.text });
|
|
4555
|
+
}
|
|
4556
|
+
const uri = resource.uri || "";
|
|
4557
|
+
const mime = resource.mimeType || "unknown";
|
|
4558
|
+
return textContentToTextPart({ text: `[Resource: ${uri} (${mime})]` });
|
|
4559
|
+
}
|
|
4560
|
+
if (block.type === "resource_link") {
|
|
4561
|
+
return textContentToTextPart({ text: `[Resource: ${block.uri}]` });
|
|
4562
|
+
}
|
|
4563
|
+
return textContentToTextPart({ text: `[Unknown content]` });
|
|
4564
|
+
});
|
|
4565
|
+
if (safeContent.length === 0) {
|
|
4566
|
+
safeContent.push({ type: "text", text: "[empty message]" });
|
|
4567
|
+
}
|
|
4568
|
+
return { role: "user", content: safeContent };
|
|
4569
|
+
}
|
|
4029
4570
|
var AVAILABLE_COMMANDS = [
|
|
4030
4571
|
{ name: "compact", description: "Compress conversation context" }
|
|
4031
4572
|
];
|
|
@@ -4063,8 +4604,8 @@ var OpenaiCompatibleAgent = class {
|
|
|
4063
4604
|
}
|
|
4064
4605
|
});
|
|
4065
4606
|
}
|
|
4066
|
-
setConnection(
|
|
4067
|
-
this.connection =
|
|
4607
|
+
setConnection(ctx) {
|
|
4608
|
+
this.connection = new AgentClientProxy(ctx);
|
|
4068
4609
|
}
|
|
4069
4610
|
async initialize(_request) {
|
|
4070
4611
|
return {
|
|
@@ -4204,7 +4745,7 @@ var OpenaiCompatibleAgent = class {
|
|
|
4204
4745
|
}
|
|
4205
4746
|
async newSession(params) {
|
|
4206
4747
|
const modelState = await this.getModels();
|
|
4207
|
-
const sessionId =
|
|
4748
|
+
const sessionId = randomUUID6();
|
|
4208
4749
|
const modelId = modelState.currentModelId ?? modelState.availableModels[0]?.modelId ?? null;
|
|
4209
4750
|
let sessionRef = null;
|
|
4210
4751
|
const session = await createSessionState({
|
|
@@ -4459,29 +5000,42 @@ var OpenaiCompatibleAgent = class {
|
|
|
4459
5000
|
}
|
|
4460
5001
|
}
|
|
4461
5002
|
const userMessage = { role: "user", content: userContent };
|
|
4462
|
-
session.history.push(userMessage);
|
|
4463
|
-
await this.appendSessionHistory(session.id, userMessage);
|
|
4464
5003
|
const abortController = new AbortController();
|
|
4465
5004
|
session.abortController = abortController;
|
|
5005
|
+
let accumulatedText = "";
|
|
4466
5006
|
try {
|
|
4467
5007
|
if (!session.modelId) {
|
|
4468
5008
|
throw new Error("No model available for current session.");
|
|
4469
5009
|
}
|
|
4470
|
-
const
|
|
5010
|
+
const systemPrompt = buildWorkspaceSystemPrompt(session.cwd, session.additionalDirectories);
|
|
5011
|
+
const sharedTools = {
|
|
5012
|
+
...session.mcp.tools,
|
|
5013
|
+
...session.acp.tools
|
|
5014
|
+
};
|
|
5015
|
+
const subagentTool = createSubagentTool({
|
|
5016
|
+
sessionId: params.sessionId,
|
|
5017
|
+
cwd: session.cwd,
|
|
5018
|
+
getConnection: () => this.connection,
|
|
5019
|
+
getModel: () => this.provider.chatModel(session.modelId),
|
|
5020
|
+
systemPrompt,
|
|
5021
|
+
parentSignal: abortController.signal
|
|
5022
|
+
});
|
|
5023
|
+
const result = streamText2({
|
|
4471
5024
|
model: this.provider.chatModel(session.modelId),
|
|
4472
|
-
system:
|
|
4473
|
-
messages: session.history,
|
|
5025
|
+
system: systemPrompt,
|
|
5026
|
+
messages: [...session.history, userMessage],
|
|
4474
5027
|
tools: {
|
|
4475
|
-
...
|
|
4476
|
-
...
|
|
5028
|
+
...sharedTools,
|
|
5029
|
+
...subagentTool
|
|
4477
5030
|
},
|
|
4478
|
-
stopWhen:
|
|
5031
|
+
stopWhen: isStepCount2(128),
|
|
4479
5032
|
abortSignal: abortController.signal
|
|
4480
5033
|
});
|
|
4481
|
-
for await (const part of result.
|
|
5034
|
+
for await (const part of result.stream) {
|
|
4482
5035
|
if (!this.connection) continue;
|
|
4483
5036
|
if (part.type === "text-delta") {
|
|
4484
5037
|
if (!part.text) continue;
|
|
5038
|
+
accumulatedText += part.text;
|
|
4485
5039
|
await this.connection.sessionUpdate({
|
|
4486
5040
|
sessionId: params.sessionId,
|
|
4487
5041
|
update: {
|
|
@@ -4525,8 +5079,8 @@ var OpenaiCompatibleAgent = class {
|
|
|
4525
5079
|
}
|
|
4526
5080
|
}
|
|
4527
5081
|
const response = await result.response;
|
|
4528
|
-
session.history.push(...response.messages);
|
|
4529
|
-
await this.appendSessionHistory(session.id, ...response.messages);
|
|
5082
|
+
session.history.push(userMessage, ...response.messages);
|
|
5083
|
+
await this.appendSessionHistory(session.id, userMessage, ...response.messages);
|
|
4530
5084
|
const [turnUsage, lastStepUsage] = await Promise.all([result.totalUsage, result.usage]);
|
|
4531
5085
|
session.contextUsedTokens = (lastStepUsage.inputTokens ?? 0) + (lastStepUsage.outputTokens ?? 0);
|
|
4532
5086
|
const usage = {
|
|
@@ -4553,8 +5107,28 @@ var OpenaiCompatibleAgent = class {
|
|
|
4553
5107
|
};
|
|
4554
5108
|
} catch (err) {
|
|
4555
5109
|
if (abortController.signal.aborted) {
|
|
5110
|
+
const safeUserMessage2 = buildSafeUserMessage(params.prompt);
|
|
5111
|
+
const historyEntries2 = [safeUserMessage2];
|
|
5112
|
+
if (accumulatedText.trim()) {
|
|
5113
|
+
historyEntries2.push({
|
|
5114
|
+
role: "assistant",
|
|
5115
|
+
content: accumulatedText + "\n\n_[Generation stopped by user \u2014 continue from user's request]_"
|
|
5116
|
+
});
|
|
5117
|
+
}
|
|
5118
|
+
session.history.push(...historyEntries2);
|
|
5119
|
+
await this.appendSessionHistory(session.id, ...historyEntries2);
|
|
4556
5120
|
return { stopReason: "cancelled" };
|
|
4557
5121
|
}
|
|
5122
|
+
const safeUserMessage = buildSafeUserMessage(params.prompt);
|
|
5123
|
+
const historyEntries = [safeUserMessage];
|
|
5124
|
+
if (accumulatedText.trim()) {
|
|
5125
|
+
historyEntries.push({
|
|
5126
|
+
role: "assistant",
|
|
5127
|
+
content: accumulatedText + "\n\n_[Generation interrupted \u2014 continue from user's request]_"
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
session.history.push(...historyEntries);
|
|
5131
|
+
await this.appendSessionHistory(session.id, ...historyEntries);
|
|
4558
5132
|
throw err;
|
|
4559
5133
|
} finally {
|
|
4560
5134
|
session.abortController = null;
|
|
@@ -4607,11 +5181,15 @@ function spawnOpenaiCompatibleApiAgent(options) {
|
|
|
4607
5181
|
const inputStream = new TransformStream();
|
|
4608
5182
|
const outputStream = new TransformStream();
|
|
4609
5183
|
const stream = ndJsonStream(outputStream.writable, inputStream.readable);
|
|
4610
|
-
const
|
|
4611
|
-
const
|
|
4612
|
-
|
|
5184
|
+
const impl = new OpenaiCompatibleAgent(options);
|
|
5185
|
+
const app = agent({ name: "openai-compatible-api-agent" }).onRequest(methods2.agent.initialize, (ctx) => impl.initialize(ctx.params)).onRequest(methods2.agent.authenticate, () => impl.authenticate()).onRequest(methods2.agent.session.new, (ctx) => impl.newSession(ctx.params)).onRequest(methods2.agent.session.resume, (ctx) => impl.resumeSession(ctx.params)).onRequest(methods2.agent.session.load, (ctx) => impl.loadSession(ctx.params)).onRequest(methods2.agent.session.close, (ctx) => impl.closeSession(ctx.params)).onRequest(methods2.agent.session.delete, (ctx) => impl.deleteSession(ctx.params)).onRequest(
|
|
5186
|
+
methods2.agent.session.setConfigOption,
|
|
5187
|
+
(ctx) => impl.setSessionConfigOption(ctx.params)
|
|
5188
|
+
).onRequest(methods2.agent.session.prompt, (ctx) => impl.prompt(ctx.params)).onNotification(methods2.agent.session.cancel, (ctx) => impl.cancel(ctx.params));
|
|
5189
|
+
const connection = app.connect(stream);
|
|
5190
|
+
impl.setConnection(connection.client);
|
|
4613
5191
|
const close = async () => {
|
|
4614
|
-
await
|
|
5192
|
+
await impl.abortAll();
|
|
4615
5193
|
const writer = outputStream.writable.getWriter();
|
|
4616
5194
|
try {
|
|
4617
5195
|
await writer.close();
|
|
@@ -4627,8 +5205,24 @@ function spawnOpenaiCompatibleApiAgent(options) {
|
|
|
4627
5205
|
}
|
|
4628
5206
|
|
|
4629
5207
|
// src/backend/agent/agent-terminal-manager.ts
|
|
4630
|
-
import { spawn as spawn2 } from "child_process";
|
|
5208
|
+
import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
|
|
4631
5209
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
5210
|
+
function detectTerminalOutputEncoding() {
|
|
5211
|
+
if (process.platform !== "win32") {
|
|
5212
|
+
return "utf-8";
|
|
5213
|
+
}
|
|
5214
|
+
try {
|
|
5215
|
+
const output = execFileSync2("cmd", ["/d", "/s", "/c", "chcp"], {
|
|
5216
|
+
encoding: "buffer",
|
|
5217
|
+
windowsHide: true
|
|
5218
|
+
});
|
|
5219
|
+
const codePage = Number.parseInt(output.toString("latin1").match(/(\d+)/)?.[1] ?? "936", 10);
|
|
5220
|
+
if (codePage === 65001) return "utf-8";
|
|
5221
|
+
if (codePage === 936 || codePage === 54936) return "gbk";
|
|
5222
|
+
} catch {
|
|
5223
|
+
}
|
|
5224
|
+
return "utf-8";
|
|
5225
|
+
}
|
|
4632
5226
|
var AgentTerminalManager = class {
|
|
4633
5227
|
constructor(emitOutput) {
|
|
4634
5228
|
this.emitOutput = emitOutput;
|
|
@@ -4639,8 +5233,9 @@ var AgentTerminalManager = class {
|
|
|
4639
5233
|
const proc = spawn2(command, args2, {
|
|
4640
5234
|
cwd,
|
|
4641
5235
|
env: { ...process.env, ...env },
|
|
4642
|
-
shell: true
|
|
5236
|
+
shell: true,
|
|
4643
5237
|
// Use shell to support commands like 'npm' easily on Windows
|
|
5238
|
+
windowsHide: true
|
|
4644
5239
|
});
|
|
4645
5240
|
const terminal = {
|
|
4646
5241
|
id: terminalId,
|
|
@@ -4654,7 +5249,8 @@ var AgentTerminalManager = class {
|
|
|
4654
5249
|
onData: () => {
|
|
4655
5250
|
},
|
|
4656
5251
|
onExit: () => {
|
|
4657
|
-
}
|
|
5252
|
+
},
|
|
5253
|
+
outputDecoder: new TextDecoder(detectTerminalOutputEncoding(), { fatal: false })
|
|
4658
5254
|
};
|
|
4659
5255
|
const handleData = (chunk) => {
|
|
4660
5256
|
let newBuffer = Buffer.concat([terminal.outputBuffer, chunk]);
|
|
@@ -4662,7 +5258,8 @@ var AgentTerminalManager = class {
|
|
|
4662
5258
|
newBuffer = newBuffer.subarray(newBuffer.length - terminal.outputByteLimit);
|
|
4663
5259
|
}
|
|
4664
5260
|
terminal.outputBuffer = newBuffer;
|
|
4665
|
-
|
|
5261
|
+
const text = terminal.outputDecoder.decode(chunk, { stream: true });
|
|
5262
|
+
this.emitOutput(sessionId, terminalId, text);
|
|
4666
5263
|
};
|
|
4667
5264
|
proc.stdout?.on("data", handleData);
|
|
4668
5265
|
proc.stderr?.on("data", handleData);
|
|
@@ -4686,7 +5283,7 @@ var AgentTerminalManager = class {
|
|
|
4686
5283
|
throw new Error(`Terminal ${terminalId} not found`);
|
|
4687
5284
|
}
|
|
4688
5285
|
return {
|
|
4689
|
-
output: terminal.
|
|
5286
|
+
output: terminal.outputDecoder.decode(terminal.outputBuffer),
|
|
4690
5287
|
truncated: false
|
|
4691
5288
|
// Simplification
|
|
4692
5289
|
};
|
|
@@ -4739,17 +5336,22 @@ var AgentTerminalManager = class {
|
|
|
4739
5336
|
};
|
|
4740
5337
|
|
|
4741
5338
|
// src/backend/agent/agent-bridge.ts
|
|
5339
|
+
var ACP_VERBOSE_LOG = false;
|
|
4742
5340
|
var ACPBridge = class {
|
|
4743
5341
|
constructor(id, options) {
|
|
4744
5342
|
this.id = id;
|
|
4745
5343
|
this.options = options;
|
|
5344
|
+
this.onSessionConnect = options.onSessionConnect;
|
|
4746
5345
|
this.onSessionUpdate = options.onSessionUpdate;
|
|
5346
|
+
this.onExtNotification = options.onExtNotification;
|
|
4747
5347
|
this.onPermissionRequest = options.onPermissionRequest;
|
|
4748
5348
|
this.terminalManager = new AgentTerminalManager(options.onAgentTerminalOutput);
|
|
4749
5349
|
}
|
|
4750
5350
|
process = null;
|
|
4751
5351
|
connection = null;
|
|
5352
|
+
onSessionConnect;
|
|
4752
5353
|
onSessionUpdate;
|
|
5354
|
+
onExtNotification;
|
|
4753
5355
|
onPermissionRequest;
|
|
4754
5356
|
_isConnected = false;
|
|
4755
5357
|
_initializeInfo = null;
|
|
@@ -4843,9 +5445,8 @@ var ACPBridge = class {
|
|
|
4843
5445
|
return found?.id ?? null;
|
|
4844
5446
|
}
|
|
4845
5447
|
async connect() {
|
|
4846
|
-
const ACP_VERBOSE_LOG = false;
|
|
4847
5448
|
const acpId = this.id;
|
|
4848
|
-
const proc = this.options.agentInfo.type === "stdio" ? spawnStdioAgent(this.options.agentInfo) : this.options.agentInfo.provider === "openai-compatible" ? spawnOpenaiCompatibleApiAgent(this.options.agentInfo) : (() => {
|
|
5449
|
+
const proc = this.options.agentInfo.type === "stdio" ? spawnStdioAgent({ ...this.options.agentInfo, cwd: this.options.cwd }) : this.options.agentInfo.provider === "openai-compatible" ? spawnOpenaiCompatibleApiAgent(this.options.agentInfo) : (() => {
|
|
4849
5450
|
throw new Error(`Unsupported api provider: ${this.options.agentInfo.provider}`);
|
|
4850
5451
|
})();
|
|
4851
5452
|
this.process = proc;
|
|
@@ -4882,105 +5483,74 @@ var ACPBridge = class {
|
|
|
4882
5483
|
}
|
|
4883
5484
|
const onPermission = this.onPermissionRequest;
|
|
4884
5485
|
const onUpdate = this.onSessionUpdate;
|
|
5486
|
+
const onExtNotification = this.onExtNotification;
|
|
4885
5487
|
const applyConfigOptions = this.applyConfigOptions.bind(this);
|
|
4886
5488
|
const terminalManager = this.terminalManager;
|
|
4887
5489
|
const sessionsCwdMap = this._sessionsCwdMap;
|
|
4888
|
-
const
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
}
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
sessionUpdate: "usage_update",
|
|
4949
|
-
used: params.contextUsagePercentage / 100,
|
|
4950
|
-
size: 1
|
|
4951
|
-
}
|
|
4952
|
-
});
|
|
4953
|
-
}
|
|
4954
|
-
return;
|
|
4955
|
-
}
|
|
4956
|
-
if (method === "_kiro.dev/commands/available") {
|
|
4957
|
-
if ("commands" in params && Array.isArray(params.commands)) {
|
|
4958
|
-
const commands = [];
|
|
4959
|
-
for (const item of params.commands) {
|
|
4960
|
-
if (typeof item === "object" && item && "name" in item && typeof item.name === "string") {
|
|
4961
|
-
commands.push({
|
|
4962
|
-
name: item.name.replace(/^\//, ""),
|
|
4963
|
-
description: item.description ? String(item.description) : ""
|
|
4964
|
-
});
|
|
4965
|
-
}
|
|
4966
|
-
}
|
|
4967
|
-
if (commands.length) {
|
|
4968
|
-
onUpdate({
|
|
4969
|
-
sessionId: params.sessionId,
|
|
4970
|
-
update: {
|
|
4971
|
-
sessionUpdate: "available_commands_update",
|
|
4972
|
-
availableCommands: commands
|
|
4973
|
-
}
|
|
4974
|
-
});
|
|
4975
|
-
}
|
|
4976
|
-
}
|
|
4977
|
-
return;
|
|
4978
|
-
}
|
|
4979
|
-
}
|
|
5490
|
+
const clientApp = client({ name: "Fello" }).onRequest(methods3.client.session.requestPermission, (ctx) => {
|
|
5491
|
+
return onPermission(ctx.params);
|
|
5492
|
+
}).onNotification(methods3.client.session.update, (ctx) => {
|
|
5493
|
+
const params = ctx.params;
|
|
5494
|
+
if (params.update.sessionUpdate === "config_option_update") {
|
|
5495
|
+
applyConfigOptions(params.sessionId, params.update.configOptions);
|
|
5496
|
+
}
|
|
5497
|
+
onUpdate(params);
|
|
5498
|
+
}).onRequest(methods3.client.fs.writeTextFile, async (ctx) => {
|
|
5499
|
+
await writeFile2(ctx.params.path, ctx.params.content, "utf8");
|
|
5500
|
+
return {};
|
|
5501
|
+
}).onRequest(methods3.client.fs.readTextFile, async (ctx) => {
|
|
5502
|
+
const content = await readFile6(ctx.params.path, "utf8");
|
|
5503
|
+
return { content };
|
|
5504
|
+
}).onRequest(methods3.client.terminal.create, (ctx) => {
|
|
5505
|
+
const params = ctx.params;
|
|
5506
|
+
const id = terminalManager.create(
|
|
5507
|
+
params.sessionId,
|
|
5508
|
+
params.command,
|
|
5509
|
+
params.args || [],
|
|
5510
|
+
params.cwd || sessionsCwdMap.get(params.sessionId) || process.cwd(),
|
|
5511
|
+
params.env?.reduce((acc, envVar) => ({ ...acc, [envVar.name]: envVar.value }), {}) || {},
|
|
5512
|
+
params.outputByteLimit || 1048576
|
|
5513
|
+
);
|
|
5514
|
+
return { terminalId: id };
|
|
5515
|
+
}).onRequest(methods3.client.terminal.output, (ctx) => {
|
|
5516
|
+
const params = ctx.params;
|
|
5517
|
+
const { output, truncated } = terminalManager.getOutput(params.terminalId);
|
|
5518
|
+
const term = terminalManager.getTerminal(params.terminalId);
|
|
5519
|
+
return {
|
|
5520
|
+
output,
|
|
5521
|
+
truncated,
|
|
5522
|
+
...term?.isFinished ? { exitStatus: { exitCode: term.exitCode, signal: term.signal } } : {}
|
|
5523
|
+
};
|
|
5524
|
+
}).onRequest(methods3.client.terminal.waitForExit, async (ctx) => {
|
|
5525
|
+
const { exitCode, signal } = await terminalManager.waitForExit(ctx.params.terminalId);
|
|
5526
|
+
return { exitCode: exitCode ?? void 0, signal: signal ?? void 0 };
|
|
5527
|
+
}).onRequest(methods3.client.terminal.kill, (ctx) => {
|
|
5528
|
+
terminalManager.kill(ctx.params.terminalId);
|
|
5529
|
+
return {};
|
|
5530
|
+
}).onRequest(methods3.client.terminal.release, (ctx) => {
|
|
5531
|
+
terminalManager.release(ctx.params.terminalId);
|
|
5532
|
+
return {};
|
|
5533
|
+
}).onNotification(
|
|
5534
|
+
"_kiro.dev/metadata",
|
|
5535
|
+
(p) => p,
|
|
5536
|
+
(ctx) => {
|
|
5537
|
+
onExtNotification("_kiro.dev/metadata", ctx.params);
|
|
5538
|
+
}
|
|
5539
|
+
).onNotification(
|
|
5540
|
+
"_kiro.dev/commands/available",
|
|
5541
|
+
(p) => p,
|
|
5542
|
+
(ctx) => {
|
|
5543
|
+
onExtNotification("_kiro.dev/commands/available", ctx.params);
|
|
5544
|
+
}
|
|
5545
|
+
).onNotification(
|
|
5546
|
+
"_kiro.dev/subagent/list_update",
|
|
5547
|
+
(p) => p,
|
|
5548
|
+
(ctx) => {
|
|
5549
|
+
onExtNotification("_kiro.dev/subagent/list_update", ctx.params);
|
|
4980
5550
|
}
|
|
4981
|
-
|
|
4982
|
-
this.connection =
|
|
4983
|
-
const initResult = await this.connection.initialize
|
|
5551
|
+
);
|
|
5552
|
+
this.connection = clientApp.connect(stream);
|
|
5553
|
+
const initResult = await this.connection.agent.request(methods3.agent.initialize, {
|
|
4984
5554
|
protocolVersion: PROTOCOL_VERSION2,
|
|
4985
5555
|
clientInfo: { name: "Fello", version: "0.1.0" },
|
|
4986
5556
|
clientCapabilities: {
|
|
@@ -5027,7 +5597,7 @@ var ACPBridge = class {
|
|
|
5027
5597
|
}
|
|
5028
5598
|
async newSession(params) {
|
|
5029
5599
|
if (!this.connection) throw new Error("Not connected");
|
|
5030
|
-
const result = await this.connection.
|
|
5600
|
+
const result = await this.connection.agent.request(methods3.agent.session.new, params);
|
|
5031
5601
|
const { models: configModels, modes: configModes } = result.configOptions ? this.parseSessionConfigOptions(result.configOptions) : { models: null, modes: null };
|
|
5032
5602
|
const models = configModels ?? result.models ?? null;
|
|
5033
5603
|
const modes = configModes ?? result.modes ?? null;
|
|
@@ -5036,10 +5606,12 @@ var ACPBridge = class {
|
|
|
5036
5606
|
this.applyConfigOptions(result.sessionId, result.configOptions);
|
|
5037
5607
|
this._loadedSessions.add(result.sessionId);
|
|
5038
5608
|
this._sessionsCwdMap.set(result.sessionId, params.cwd);
|
|
5609
|
+
this.onSessionConnect({ sessionId: result.sessionId });
|
|
5039
5610
|
return Object.assign(result, { models, modes });
|
|
5040
5611
|
}
|
|
5041
5612
|
async hack_setSessionModel(params) {
|
|
5042
|
-
|
|
5613
|
+
if (!this.connection) throw new Error("Not connected");
|
|
5614
|
+
return this.connection.agent.request("session/set_model", params);
|
|
5043
5615
|
}
|
|
5044
5616
|
async setSessionModel(params) {
|
|
5045
5617
|
if (!this.connection) throw new Error("Not connected");
|
|
@@ -5047,7 +5619,7 @@ var ACPBridge = class {
|
|
|
5047
5619
|
const configId = this.getConfigOptionId(params.sessionId, "model");
|
|
5048
5620
|
if (configId) {
|
|
5049
5621
|
try {
|
|
5050
|
-
const cfg = await this.connection.
|
|
5622
|
+
const cfg = await this.connection.agent.request(methods3.agent.session.setConfigOption, {
|
|
5051
5623
|
sessionId: params.sessionId,
|
|
5052
5624
|
configId,
|
|
5053
5625
|
value: params.modelId
|
|
@@ -5072,7 +5644,7 @@ var ACPBridge = class {
|
|
|
5072
5644
|
async closeSession(sessionId) {
|
|
5073
5645
|
if (this.connection) {
|
|
5074
5646
|
try {
|
|
5075
|
-
await this.connection.
|
|
5647
|
+
await this.connection.agent.request(methods3.agent.session.close, { sessionId }).catch(() => {
|
|
5076
5648
|
});
|
|
5077
5649
|
} catch {
|
|
5078
5650
|
}
|
|
@@ -5091,7 +5663,7 @@ var ACPBridge = class {
|
|
|
5091
5663
|
async deleteSession(sessionId) {
|
|
5092
5664
|
if (!this.connection) return;
|
|
5093
5665
|
try {
|
|
5094
|
-
await this.connection.
|
|
5666
|
+
await this.connection.agent.request(methods3.agent.session.delete, { sessionId });
|
|
5095
5667
|
} catch {
|
|
5096
5668
|
}
|
|
5097
5669
|
this._modelStates.delete(sessionId);
|
|
@@ -5111,9 +5683,9 @@ var ACPBridge = class {
|
|
|
5111
5683
|
}
|
|
5112
5684
|
let result;
|
|
5113
5685
|
try {
|
|
5114
|
-
result = await this.connection.
|
|
5686
|
+
result = await this.connection.agent.request(methods3.agent.session.resume, params);
|
|
5115
5687
|
} catch {
|
|
5116
|
-
result = await this.connection.
|
|
5688
|
+
result = await this.connection.agent.request(methods3.agent.session.load, {
|
|
5117
5689
|
...params,
|
|
5118
5690
|
mcpServers: params.mcpServers ?? []
|
|
5119
5691
|
});
|
|
@@ -5126,6 +5698,7 @@ var ACPBridge = class {
|
|
|
5126
5698
|
this.applyConfigOptions(params.sessionId, result.configOptions);
|
|
5127
5699
|
this._loadedSessions.add(params.sessionId);
|
|
5128
5700
|
this._sessionsCwdMap.set(params.sessionId, params.cwd);
|
|
5701
|
+
this.onSessionConnect({ sessionId: params.sessionId });
|
|
5129
5702
|
return Object.assign(result, { models, modes });
|
|
5130
5703
|
}
|
|
5131
5704
|
async setSessionMode(params) {
|
|
@@ -5134,7 +5707,7 @@ var ACPBridge = class {
|
|
|
5134
5707
|
const configId = this.getConfigOptionId(params.sessionId, "mode");
|
|
5135
5708
|
if (configId) {
|
|
5136
5709
|
try {
|
|
5137
|
-
const cfg = await this.connection.
|
|
5710
|
+
const cfg = await this.connection.agent.request(methods3.agent.session.setConfigOption, {
|
|
5138
5711
|
sessionId: params.sessionId,
|
|
5139
5712
|
configId,
|
|
5140
5713
|
value: params.modeId
|
|
@@ -5142,10 +5715,10 @@ var ACPBridge = class {
|
|
|
5142
5715
|
this.applyConfigOptions(params.sessionId, cfg.configOptions);
|
|
5143
5716
|
result = {};
|
|
5144
5717
|
} catch {
|
|
5145
|
-
result = await this.connection.
|
|
5718
|
+
result = await this.connection.agent.request(methods3.agent.session.setMode, params);
|
|
5146
5719
|
}
|
|
5147
5720
|
} else {
|
|
5148
|
-
result = await this.connection.
|
|
5721
|
+
result = await this.connection.agent.request(methods3.agent.session.setMode, params);
|
|
5149
5722
|
}
|
|
5150
5723
|
const state = this._modeStates.get(params.sessionId);
|
|
5151
5724
|
if (state) {
|
|
@@ -5155,18 +5728,18 @@ var ACPBridge = class {
|
|
|
5155
5728
|
}
|
|
5156
5729
|
async sendPrompt(params) {
|
|
5157
5730
|
if (!this.connection) throw new Error("Not connected");
|
|
5158
|
-
return this.connection.prompt
|
|
5731
|
+
return this.connection.agent.request(methods3.agent.session.prompt, params);
|
|
5159
5732
|
}
|
|
5160
5733
|
async cancel(params) {
|
|
5161
5734
|
if (!this.connection) return;
|
|
5162
|
-
await this.connection.cancel
|
|
5735
|
+
await this.connection.agent.notify(methods3.agent.session.cancel, params);
|
|
5163
5736
|
}
|
|
5164
5737
|
async kill() {
|
|
5165
5738
|
if (this.connection && this._isConnected) {
|
|
5166
5739
|
const sessionIds = /* @__PURE__ */ new Set([...this._modelStates.keys(), ...this._modeStates.keys()]);
|
|
5167
5740
|
for (const sid of sessionIds) {
|
|
5168
5741
|
try {
|
|
5169
|
-
await this.connection.
|
|
5742
|
+
await this.connection.agent.request(methods3.agent.session.close, { sessionId: sid }).catch(() => {
|
|
5170
5743
|
});
|
|
5171
5744
|
} catch {
|
|
5172
5745
|
}
|
|
@@ -5193,68 +5766,155 @@ var ACPBridge = class {
|
|
|
5193
5766
|
// src/backend/agent/resolve-agent-info.ts
|
|
5194
5767
|
function resolveAgentInfo(agentId) {
|
|
5195
5768
|
const settings = storageOps.getSettings();
|
|
5196
|
-
const
|
|
5197
|
-
if (!
|
|
5769
|
+
const agent2 = settings.agents.find((a) => a.id === agentId);
|
|
5770
|
+
if (!agent2) {
|
|
5198
5771
|
throw new Error(`Unknown agent: ${agentId}. Please check your settings.`);
|
|
5199
5772
|
}
|
|
5200
|
-
if (
|
|
5201
|
-
const command =
|
|
5202
|
-
if (!command) throw new Error(`Agent "${
|
|
5203
|
-
return { ...
|
|
5773
|
+
if (agent2.type === "stdio") {
|
|
5774
|
+
const command = agent2.command.trim();
|
|
5775
|
+
if (!command) throw new Error(`Agent "${agent2.id}" has no command configured.`);
|
|
5776
|
+
return { ...agent2, command };
|
|
5204
5777
|
}
|
|
5205
|
-
const provider =
|
|
5206
|
-
const baseUrl =
|
|
5207
|
-
const apiKey =
|
|
5208
|
-
if (!provider) throw new Error(`Agent "${
|
|
5209
|
-
if (!baseUrl) throw new Error(`Agent "${
|
|
5210
|
-
if (!apiKey) throw new Error(`Agent "${
|
|
5211
|
-
return { ...
|
|
5778
|
+
const provider = agent2.provider.trim();
|
|
5779
|
+
const baseUrl = agent2.baseUrl.trim();
|
|
5780
|
+
const apiKey = agent2.apiKey.trim();
|
|
5781
|
+
if (!provider) throw new Error(`Agent "${agent2.id}" has no provider configured.`);
|
|
5782
|
+
if (!baseUrl) throw new Error(`Agent "${agent2.id}" has no baseUrl configured.`);
|
|
5783
|
+
if (!apiKey) throw new Error(`Agent "${agent2.id}" has no apiKey configured.`);
|
|
5784
|
+
return { ...agent2, provider, baseUrl, apiKey };
|
|
5212
5785
|
}
|
|
5213
5786
|
|
|
5214
|
-
// src/backend/bridge-
|
|
5215
|
-
function
|
|
5787
|
+
// src/backend/bridge-connect.ts
|
|
5788
|
+
function createBridgeConnectModule(ctx, deps) {
|
|
5216
5789
|
const { sendEvent, storage } = ctx;
|
|
5217
|
-
const
|
|
5790
|
+
const bridges = /* @__PURE__ */ new Map();
|
|
5791
|
+
const sessionAgentMap = /* @__PURE__ */ new Map();
|
|
5218
5792
|
let broadcastAndSaveSessionUpdate = () => {
|
|
5219
5793
|
};
|
|
5220
5794
|
function setBroadcast(fn) {
|
|
5221
5795
|
broadcastAndSaveSessionUpdate = fn;
|
|
5222
5796
|
}
|
|
5223
|
-
async function ensureBridge(agentId) {
|
|
5224
|
-
const connectPromise =
|
|
5797
|
+
async function ensureBridge(sessionKey, agentId, cwd) {
|
|
5798
|
+
const connectPromise = bridges.get(sessionKey);
|
|
5225
5799
|
if (connectPromise) {
|
|
5226
|
-
const
|
|
5227
|
-
if (
|
|
5228
|
-
if (
|
|
5229
|
-
await
|
|
5800
|
+
const existingBridge = await connectPromise;
|
|
5801
|
+
if (existingBridge.isConnected) return existingBridge;
|
|
5802
|
+
if (bridges.get(sessionKey) === connectPromise) bridges.delete(sessionKey);
|
|
5803
|
+
await existingBridge.kill();
|
|
5230
5804
|
}
|
|
5805
|
+
sessionAgentMap.set(sessionKey, agentId);
|
|
5806
|
+
let currentSessionId = null;
|
|
5231
5807
|
const agentInfo = resolveAgentInfo(agentId);
|
|
5232
|
-
const
|
|
5808
|
+
const bridge = new ACPBridge(agentId, {
|
|
5233
5809
|
agentInfo,
|
|
5810
|
+
cwd,
|
|
5811
|
+
onSessionConnect: (connection) => {
|
|
5812
|
+
currentSessionId = `${agentId}:${connection.sessionId}`;
|
|
5813
|
+
storage.updateSession(currentSessionId, { connectionStatus: "connected" }, false);
|
|
5814
|
+
const updated = storage.getSession(currentSessionId);
|
|
5815
|
+
if (updated) sendEvent("session-changed", { session: updated });
|
|
5816
|
+
},
|
|
5234
5817
|
onSessionUpdate: (notification) => {
|
|
5235
|
-
const sessionId = `${agentId}:${notification.sessionId}`;
|
|
5236
5818
|
const sessionUpdate = notification.update?.sessionUpdate;
|
|
5237
|
-
if (
|
|
5238
|
-
if (notification.
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5819
|
+
if (currentSessionId) {
|
|
5820
|
+
if (currentSessionId === `${agentId}:${notification.sessionId}`) {
|
|
5821
|
+
if (sessionUpdate === "session_info_update") {
|
|
5822
|
+
if (notification.update.title) {
|
|
5823
|
+
storage.updateSession(currentSessionId, { title: notification.update.title });
|
|
5824
|
+
const updated = storage.getSession(currentSessionId);
|
|
5825
|
+
if (updated) sendEvent("session-changed", { session: updated });
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
if (sessionUpdate === "current_mode_update") {
|
|
5829
|
+
const session = storage.getSession(currentSessionId);
|
|
5830
|
+
if (session && session.modes) {
|
|
5831
|
+
session.modes.currentModeId = notification.update.currentModeId ?? null;
|
|
5832
|
+
storage.updateSession(currentSessionId, { modes: session.modes });
|
|
5833
|
+
const updated = storage.getSession(currentSessionId);
|
|
5834
|
+
if (updated) sendEvent("session-changed", { session: updated });
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5242
5837
|
}
|
|
5838
|
+
broadcastAndSaveSessionUpdate(currentSessionId, notification);
|
|
5243
5839
|
}
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5840
|
+
},
|
|
5841
|
+
onExtNotification: (method, params) => {
|
|
5842
|
+
if (method === "_kiro.dev/metadata") {
|
|
5843
|
+
if (typeof params === "object" && params && "sessionId" in params && typeof params.sessionId === "string" && "contextUsagePercentage" in params && typeof params.contextUsagePercentage === "number" && currentSessionId === `${agentId}:${params.sessionId}`) {
|
|
5844
|
+
broadcastAndSaveSessionUpdate(currentSessionId, {
|
|
5845
|
+
sessionId: params.sessionId,
|
|
5846
|
+
update: {
|
|
5847
|
+
sessionUpdate: "usage_update",
|
|
5848
|
+
used: params.contextUsagePercentage / 100,
|
|
5849
|
+
size: 1
|
|
5850
|
+
}
|
|
5851
|
+
});
|
|
5852
|
+
}
|
|
5853
|
+
return;
|
|
5854
|
+
}
|
|
5855
|
+
if (method === "_kiro.dev/commands/available") {
|
|
5856
|
+
if (typeof params === "object" && params && "sessionId" in params && typeof params.sessionId === "string" && "commands" in params && Array.isArray(params.commands) && currentSessionId === `${agentId}:${params.sessionId}`) {
|
|
5857
|
+
const commands = [];
|
|
5858
|
+
for (const item of params.commands) {
|
|
5859
|
+
if (typeof item === "object" && item && "name" in item && typeof item.name === "string") {
|
|
5860
|
+
commands.push({
|
|
5861
|
+
name: item.name.replace(/^\//, ""),
|
|
5862
|
+
description: item.description ? String(item.description) : ""
|
|
5863
|
+
});
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5866
|
+
if (commands.length) {
|
|
5867
|
+
broadcastAndSaveSessionUpdate(currentSessionId, {
|
|
5868
|
+
sessionId: params.sessionId,
|
|
5869
|
+
update: {
|
|
5870
|
+
sessionUpdate: "available_commands_update",
|
|
5871
|
+
availableCommands: commands
|
|
5872
|
+
}
|
|
5873
|
+
});
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
return;
|
|
5877
|
+
}
|
|
5878
|
+
if (method === "_kiro.dev/subagent/list_update") {
|
|
5879
|
+
if (typeof params === "object" && params && currentSessionId && "subagents" in params && Array.isArray(params.subagents)) {
|
|
5880
|
+
for (const subagent of params.subagents) {
|
|
5881
|
+
if (typeof subagent === "object" && subagent && "sessionId" in subagent && typeof subagent.sessionId === "string") {
|
|
5882
|
+
const update = {
|
|
5883
|
+
sessionUpdate: "subagent_update",
|
|
5884
|
+
sessionId: subagent.sessionId,
|
|
5885
|
+
name: typeof subagent.sessionName === "string" ? subagent.sessionName : void 0,
|
|
5886
|
+
prompt: typeof subagent.initialQuery === "string" ? subagent.initialQuery : void 0,
|
|
5887
|
+
status: {
|
|
5888
|
+
pending: "pending",
|
|
5889
|
+
working: "in_progress",
|
|
5890
|
+
terminated: "completed",
|
|
5891
|
+
failed: "failed"
|
|
5892
|
+
}[String(subagent?.status?.type)]
|
|
5893
|
+
};
|
|
5894
|
+
broadcastAndSaveSessionUpdate(currentSessionId, {
|
|
5895
|
+
sessionId: currentSessionId.replace(`${agentId}:`, ""),
|
|
5896
|
+
update: {
|
|
5897
|
+
sessionUpdate: "session_info_update",
|
|
5898
|
+
_meta: {
|
|
5899
|
+
fello: {
|
|
5900
|
+
update
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5251
5907
|
}
|
|
5908
|
+
return;
|
|
5252
5909
|
}
|
|
5253
|
-
broadcastAndSaveSessionUpdate(sessionId, notification);
|
|
5254
5910
|
},
|
|
5255
5911
|
onPermissionRequest: async (request) => {
|
|
5256
|
-
|
|
5257
|
-
|
|
5912
|
+
if (!currentSessionId) {
|
|
5913
|
+
return {
|
|
5914
|
+
outcome: { outcome: "selected", optionId: "deny" }
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
const session = storage.getSession(currentSessionId);
|
|
5258
5918
|
if (session?.permissionMode === "allow-all") {
|
|
5259
5919
|
const allowOption = request.options.find((o) => o.kind === "allow_always") ?? request.options.find((o) => o.kind === "allow_once") ?? request.options[0];
|
|
5260
5920
|
return {
|
|
@@ -5262,7 +5922,7 @@ function createBridgePoolModule(ctx, deps) {
|
|
|
5262
5922
|
};
|
|
5263
5923
|
}
|
|
5264
5924
|
const userResponse = await deps.askUser.askUser({
|
|
5265
|
-
sessionId,
|
|
5925
|
+
sessionId: currentSessionId,
|
|
5266
5926
|
title: request.toolCall.title ?? t("ilink.permissionRequest"),
|
|
5267
5927
|
description: request.toolCall.rawInput ? JSON.stringify(request.toolCall.rawInput) : "",
|
|
5268
5928
|
allowCustomInput: false,
|
|
@@ -5305,24 +5965,70 @@ function createBridgePoolModule(ctx, deps) {
|
|
|
5305
5965
|
}
|
|
5306
5966
|
});
|
|
5307
5967
|
let newConnectPromise;
|
|
5308
|
-
newConnectPromise =
|
|
5309
|
-
if (
|
|
5310
|
-
await
|
|
5968
|
+
newConnectPromise = bridge.connect().then(() => bridge).catch(async (error) => {
|
|
5969
|
+
if (bridges.get(sessionKey) === newConnectPromise) bridges.delete(sessionKey);
|
|
5970
|
+
await bridge.kill();
|
|
5311
5971
|
throw error;
|
|
5312
5972
|
});
|
|
5313
|
-
|
|
5973
|
+
bridges.set(sessionKey, newConnectPromise);
|
|
5974
|
+
storage.updateSession(sessionKey, { connectionStatus: "connecting" }, false);
|
|
5975
|
+
const connectingSession = storage.getSession(sessionKey);
|
|
5976
|
+
if (connectingSession) sendEvent("session-changed", { session: connectingSession });
|
|
5314
5977
|
return newConnectPromise;
|
|
5315
5978
|
}
|
|
5316
|
-
|
|
5979
|
+
function rekeyBridge(oldKey, newKey) {
|
|
5980
|
+
const p = bridges.get(oldKey);
|
|
5981
|
+
if (p) {
|
|
5982
|
+
bridges.delete(oldKey);
|
|
5983
|
+
bridges.set(newKey, p);
|
|
5984
|
+
}
|
|
5985
|
+
const agentId = sessionAgentMap.get(oldKey);
|
|
5986
|
+
if (agentId !== void 0) {
|
|
5987
|
+
sessionAgentMap.delete(oldKey);
|
|
5988
|
+
sessionAgentMap.set(newKey, agentId);
|
|
5989
|
+
}
|
|
5990
|
+
}
|
|
5991
|
+
async function killBridge(sessionKey) {
|
|
5992
|
+
const p = bridges.get(sessionKey);
|
|
5993
|
+
if (p) {
|
|
5994
|
+
bridges.delete(sessionKey);
|
|
5995
|
+
sessionAgentMap.delete(sessionKey);
|
|
5996
|
+
try {
|
|
5997
|
+
const b = await p;
|
|
5998
|
+
await b.kill();
|
|
5999
|
+
} catch {
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
storage.updateSession(sessionKey, { connectionStatus: "disconnected" }, false);
|
|
6003
|
+
const session = storage.getSession(sessionKey);
|
|
6004
|
+
if (session) sendEvent("session-changed", { session });
|
|
6005
|
+
}
|
|
6006
|
+
async function killBridgesByAgent(agentId) {
|
|
6007
|
+
const keysToKill = [];
|
|
6008
|
+
for (const [key, aid] of sessionAgentMap) {
|
|
6009
|
+
if (aid === agentId) keysToKill.push(key);
|
|
6010
|
+
}
|
|
6011
|
+
await Promise.all(keysToKill.map((key) => killBridge(key)));
|
|
6012
|
+
}
|
|
6013
|
+
async function clearAll() {
|
|
5317
6014
|
const killPromises = [];
|
|
5318
|
-
for (const p of
|
|
6015
|
+
for (const p of bridges.values()) {
|
|
5319
6016
|
killPromises.push(p.then((b) => b.kill()).catch(() => {
|
|
5320
6017
|
}));
|
|
5321
6018
|
}
|
|
5322
|
-
|
|
6019
|
+
bridges.clear();
|
|
6020
|
+
sessionAgentMap.clear();
|
|
5323
6021
|
await Promise.all(killPromises);
|
|
5324
6022
|
}
|
|
5325
|
-
return {
|
|
6023
|
+
return {
|
|
6024
|
+
bridges,
|
|
6025
|
+
ensureBridge,
|
|
6026
|
+
rekeyBridge,
|
|
6027
|
+
killBridge,
|
|
6028
|
+
killBridgesByAgent,
|
|
6029
|
+
clearAll,
|
|
6030
|
+
setBroadcast
|
|
6031
|
+
};
|
|
5326
6032
|
}
|
|
5327
6033
|
|
|
5328
6034
|
// src/backend/terminal.ts
|
|
@@ -5330,7 +6036,7 @@ import { homedir as homedir4 } from "os";
|
|
|
5330
6036
|
import { spawn as spawnPty } from "node-pty";
|
|
5331
6037
|
import { createHash as createHash3 } from "crypto";
|
|
5332
6038
|
import { chmod, stat as stat3 } from "fs/promises";
|
|
5333
|
-
import { dirname as
|
|
6039
|
+
import { dirname as dirname5, join as join16 } from "path";
|
|
5334
6040
|
import { createRequire } from "module";
|
|
5335
6041
|
var require2 = createRequire(import.meta.url);
|
|
5336
6042
|
function createTerminalModule(ctx, deps) {
|
|
@@ -5361,8 +6067,8 @@ function createTerminalModule(ctx, deps) {
|
|
|
5361
6067
|
if (process.platform === "win32") return;
|
|
5362
6068
|
if (isNodePtyHelperPrepared) return;
|
|
5363
6069
|
const packageJsonPath = require2.resolve("node-pty/package.json");
|
|
5364
|
-
const packageDir =
|
|
5365
|
-
const helperPath =
|
|
6070
|
+
const packageDir = dirname5(packageJsonPath);
|
|
6071
|
+
const helperPath = join16(
|
|
5366
6072
|
packageDir,
|
|
5367
6073
|
"prebuilds",
|
|
5368
6074
|
`${process.platform}-${process.arch}`,
|
|
@@ -5505,7 +6211,7 @@ function createTerminalModule(ctx, deps) {
|
|
|
5505
6211
|
sessionId,
|
|
5506
6212
|
terminalId
|
|
5507
6213
|
}) {
|
|
5508
|
-
for (const connectPromise of deps.
|
|
6214
|
+
for (const connectPromise of deps.bridges.values()) {
|
|
5509
6215
|
try {
|
|
5510
6216
|
const b = await connectPromise;
|
|
5511
6217
|
const output = b.terminalManager.getOutput(terminalId);
|
|
@@ -5549,7 +6255,7 @@ import {
|
|
|
5549
6255
|
copyFile
|
|
5550
6256
|
} from "fs/promises";
|
|
5551
6257
|
import * as mimeTypes4 from "mime-types";
|
|
5552
|
-
import { dirname as
|
|
6258
|
+
import { dirname as dirname6, join as join17, relative as relative7, extname as extname2, basename as basename3 } from "path";
|
|
5553
6259
|
var SEARCH_MAX_RESULTS = 10;
|
|
5554
6260
|
var SEARCH_FUSE_THRESHOLD = 0.4;
|
|
5555
6261
|
var SEARCH_CACHE_TTL_MS = 6e4;
|
|
@@ -5582,7 +6288,7 @@ function createFilesystemState(ctx) {
|
|
|
5582
6288
|
fileScene.add(dir);
|
|
5583
6289
|
const entries = await readdir(dir).catch(() => []);
|
|
5584
6290
|
for (const name of entries) {
|
|
5585
|
-
const full =
|
|
6291
|
+
const full = join17(dir, name);
|
|
5586
6292
|
const s = await stat4(full).catch(() => null);
|
|
5587
6293
|
if (!s) continue;
|
|
5588
6294
|
if (isIgnorePath(full, cwd)) continue;
|
|
@@ -5615,14 +6321,14 @@ function createFilesystemState(ctx) {
|
|
|
5615
6321
|
if (!project) throw new Error("Project not found");
|
|
5616
6322
|
const cwd = destDir || project.cwd;
|
|
5617
6323
|
const fileName = basename3(sourcePath);
|
|
5618
|
-
let destPath =
|
|
6324
|
+
let destPath = join17(cwd, fileName);
|
|
5619
6325
|
let counter = 1;
|
|
5620
6326
|
while (true) {
|
|
5621
6327
|
const info = await stat4(destPath).catch(() => null);
|
|
5622
6328
|
if (!info) break;
|
|
5623
6329
|
const ext = extname2(fileName);
|
|
5624
6330
|
const name = basename3(fileName, ext);
|
|
5625
|
-
destPath =
|
|
6331
|
+
destPath = join17(cwd, `${name}(${counter})${ext}`);
|
|
5626
6332
|
counter++;
|
|
5627
6333
|
}
|
|
5628
6334
|
await copyFile(sourcePath, destPath);
|
|
@@ -5676,7 +6382,7 @@ function createFilesystemState(ctx) {
|
|
|
5676
6382
|
const entries = await readdir(cwd).catch(() => []);
|
|
5677
6383
|
const results = [];
|
|
5678
6384
|
for (const name of entries) {
|
|
5679
|
-
const full =
|
|
6385
|
+
const full = join17(cwd, name);
|
|
5680
6386
|
if (isIgnorePath(full, cwd)) continue;
|
|
5681
6387
|
if (fileScene.has(full)) continue;
|
|
5682
6388
|
fileScene.add(full);
|
|
@@ -5718,7 +6424,7 @@ function createFilesystemState(ctx) {
|
|
|
5718
6424
|
const entries = await readdir(startPath).catch(() => []);
|
|
5719
6425
|
const results = [];
|
|
5720
6426
|
for (const name of entries) {
|
|
5721
|
-
const full =
|
|
6427
|
+
const full = join17(startPath, name);
|
|
5722
6428
|
const s = await stat4(full).catch(() => null);
|
|
5723
6429
|
if (!s) continue;
|
|
5724
6430
|
if (isIgnorePath(full, cwd)) continue;
|
|
@@ -5742,7 +6448,7 @@ function createFilesystemState(ctx) {
|
|
|
5742
6448
|
if (isFolder) {
|
|
5743
6449
|
await mkdir(targetPath, { recursive: true });
|
|
5744
6450
|
} else {
|
|
5745
|
-
await mkdir(
|
|
6451
|
+
await mkdir(dirname6(targetPath), { recursive: true });
|
|
5746
6452
|
await writeFile3(targetPath, "");
|
|
5747
6453
|
}
|
|
5748
6454
|
markProjectFsDirty(projectId);
|
|
@@ -5835,14 +6541,14 @@ function createFilesystemState(ctx) {
|
|
|
5835
6541
|
const ext = extname2(fileName);
|
|
5836
6542
|
const base = basename3(fileName, ext);
|
|
5837
6543
|
let counter = 0;
|
|
5838
|
-
let currentDest =
|
|
6544
|
+
let currentDest = join17(destDir, fileName);
|
|
5839
6545
|
while (true) {
|
|
5840
6546
|
const existing = await stat4(currentDest).catch(() => null);
|
|
5841
6547
|
if (!existing) break;
|
|
5842
6548
|
if (counter === 0 && existing.isDirectory())
|
|
5843
6549
|
throw new Error("Cannot overwrite a folder with a file");
|
|
5844
6550
|
counter++;
|
|
5845
|
-
currentDest =
|
|
6551
|
+
currentDest = join17(destDir, `${base}(${counter})${ext}`);
|
|
5846
6552
|
}
|
|
5847
6553
|
const buffer = Buffer.from(base64, "base64");
|
|
5848
6554
|
await mkdir(destDir, { recursive: true });
|
|
@@ -5874,7 +6580,7 @@ function createFilesystemState(ctx) {
|
|
|
5874
6580
|
|
|
5875
6581
|
// src/backend/project/git.ts
|
|
5876
6582
|
import { execFile } from "child_process";
|
|
5877
|
-
import { dirname as
|
|
6583
|
+
import { dirname as dirname7, relative as relative8 } from "path";
|
|
5878
6584
|
import { promisify } from "util";
|
|
5879
6585
|
var execFileAsync = promisify(execFile);
|
|
5880
6586
|
function createGitHandlers(ctx) {
|
|
@@ -5925,7 +6631,7 @@ function createGitHandlers(ctx) {
|
|
|
5925
6631
|
const project = storage.getProject(projectId);
|
|
5926
6632
|
if (!project) throw new Error("Project not found");
|
|
5927
6633
|
const targetPath = resolveSafePath(project.cwd, relativePath);
|
|
5928
|
-
const cwd =
|
|
6634
|
+
const cwd = dirname7(targetPath);
|
|
5929
6635
|
const relPath = relative8(cwd, targetPath);
|
|
5930
6636
|
const { stdout } = await execFileAsync("git", ["show", `HEAD:./${relPath}`], {
|
|
5931
6637
|
cwd,
|
|
@@ -5950,15 +6656,10 @@ function createProjectModule(ctx, deps) {
|
|
|
5950
6656
|
fs2.markProjectFsDirty(payload.projectId);
|
|
5951
6657
|
}
|
|
5952
6658
|
});
|
|
5953
|
-
async function
|
|
5954
|
-
const p = deps.bridgePool.get(agentId);
|
|
5955
|
-
if (!p) throw new Error(`No bridge for agent ${agentId}`);
|
|
5956
|
-
return p;
|
|
5957
|
-
}
|
|
5958
|
-
async function listProjects() {
|
|
6659
|
+
async function listProjects2() {
|
|
5959
6660
|
return storage.listProjects();
|
|
5960
6661
|
}
|
|
5961
|
-
async function
|
|
6662
|
+
async function addProject2(cwd) {
|
|
5962
6663
|
const info = storage.addProject(cwd);
|
|
5963
6664
|
fs2.initProjectFsVersion(info.id);
|
|
5964
6665
|
await deps.watcher.syncWatchers();
|
|
@@ -5969,49 +6670,41 @@ function createProjectModule(ctx, deps) {
|
|
|
5969
6670
|
storage.updateProjectTitle(projectId, title);
|
|
5970
6671
|
sendEvent("projects-changed", void 0);
|
|
5971
6672
|
}
|
|
5972
|
-
async function
|
|
6673
|
+
async function deleteProject2(projectId) {
|
|
5973
6674
|
const projectSessions = storage.listSessions().filter((s) => s.projectId === projectId);
|
|
5974
|
-
storage.deleteProject(projectId);
|
|
5975
6675
|
for (const session of projectSessions) {
|
|
5976
|
-
|
|
5977
|
-
const b = await ensureBridge(session.agentId);
|
|
5978
|
-
if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
|
|
5979
|
-
await b.deleteSession(session.resumeId);
|
|
5980
|
-
} catch (error) {
|
|
5981
|
-
console.warn(
|
|
5982
|
-
`[backend] Failed to close/delete session on agent for ${session.agentId}:${session.resumeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
5983
|
-
);
|
|
5984
|
-
}
|
|
6676
|
+
await deps.session.deleteSession(session.id);
|
|
5985
6677
|
}
|
|
6678
|
+
storage.deleteProject(projectId);
|
|
5986
6679
|
fs2.clearProjectSearchState(projectId);
|
|
5987
6680
|
await deps.watcher.syncWatchers();
|
|
5988
6681
|
sendEvent("projects-changed", void 0);
|
|
5989
6682
|
sendEvent("sessions-changed", void 0);
|
|
5990
6683
|
}
|
|
5991
6684
|
return {
|
|
5992
|
-
listProjects,
|
|
5993
|
-
addProject,
|
|
6685
|
+
listProjects: listProjects2,
|
|
6686
|
+
addProject: addProject2,
|
|
5994
6687
|
renameProject,
|
|
5995
|
-
deleteProject,
|
|
6688
|
+
deleteProject: deleteProject2,
|
|
5996
6689
|
...fs2,
|
|
5997
6690
|
...git
|
|
5998
6691
|
};
|
|
5999
6692
|
}
|
|
6000
6693
|
|
|
6001
6694
|
// src/backend/session/index.ts
|
|
6002
|
-
import { randomUUID as
|
|
6695
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
6003
6696
|
|
|
6004
6697
|
// src/backend/socket-server.ts
|
|
6005
6698
|
import { createServer as createServer2 } from "http";
|
|
6006
|
-
import { dirname as
|
|
6007
|
-
import { mkdirSync as
|
|
6699
|
+
import { dirname as dirname8, join as join18 } from "path";
|
|
6700
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync7, unlinkSync } from "fs";
|
|
6008
6701
|
function isWindowsPipePath(p) {
|
|
6009
6702
|
return /^\\\\.\\pipe\\/i.test(p);
|
|
6010
6703
|
}
|
|
6011
6704
|
async function startSocketServer(socketPath) {
|
|
6012
6705
|
if (!isWindowsPipePath(socketPath)) {
|
|
6013
|
-
|
|
6014
|
-
if (
|
|
6706
|
+
mkdirSync7(dirname8(socketPath), { recursive: true });
|
|
6707
|
+
if (existsSync7(socketPath)) {
|
|
6015
6708
|
unlinkSync(socketPath);
|
|
6016
6709
|
}
|
|
6017
6710
|
}
|
|
@@ -6064,7 +6757,7 @@ async function startSocketServer(socketPath) {
|
|
|
6064
6757
|
server.close();
|
|
6065
6758
|
server.closeAllConnections();
|
|
6066
6759
|
if (!isWindowsPipePath(socketPath)) {
|
|
6067
|
-
if (
|
|
6760
|
+
if (existsSync7(socketPath)) {
|
|
6068
6761
|
try {
|
|
6069
6762
|
unlinkSync(socketPath);
|
|
6070
6763
|
} catch {
|
|
@@ -6090,13 +6783,13 @@ function generateSocketPath(key) {
|
|
|
6090
6783
|
if (process.platform === "win32") {
|
|
6091
6784
|
return `\\\\.\\pipe\\fello-${key}-${timestamp}`;
|
|
6092
6785
|
}
|
|
6093
|
-
return
|
|
6786
|
+
return join18(SOCKETS_DIR, `${key}-${timestamp}.socket`);
|
|
6094
6787
|
}
|
|
6095
6788
|
|
|
6096
6789
|
// src/backend/ilink/ilink-bridge.ts
|
|
6097
6790
|
import { readFile as readFile7, writeFile as writeFile4, mkdir as mkdir2, rm as rm2 } from "fs/promises";
|
|
6098
|
-
import { join as
|
|
6099
|
-
import { randomUUID as
|
|
6791
|
+
import { join as join19 } from "path";
|
|
6792
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
6100
6793
|
|
|
6101
6794
|
// src/backend/ilink/ilink-crypto.ts
|
|
6102
6795
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, createHash as createHash4 } from "crypto";
|
|
@@ -6294,10 +6987,10 @@ var ILinkClient = class {
|
|
|
6294
6987
|
};
|
|
6295
6988
|
|
|
6296
6989
|
// src/backend/ilink/ilink-bridge.ts
|
|
6297
|
-
var ILINK_DIR =
|
|
6298
|
-
var CREDENTIALS_PATH =
|
|
6299
|
-
var CURSOR_PATH =
|
|
6300
|
-
var ACTIVE_SESSION_PATH =
|
|
6990
|
+
var ILINK_DIR = join19(FELLO_DIR, "ilink");
|
|
6991
|
+
var CREDENTIALS_PATH = join19(ILINK_DIR, "credentials.json");
|
|
6992
|
+
var CURSOR_PATH = join19(ILINK_DIR, "cursor.json");
|
|
6993
|
+
var ACTIVE_SESSION_PATH = join19(ILINK_DIR, "active-session.json");
|
|
6301
6994
|
async function ensureDir() {
|
|
6302
6995
|
await mkdir2(ILINK_DIR, { recursive: true });
|
|
6303
6996
|
}
|
|
@@ -6508,11 +7201,13 @@ var ILinkBridge = class _ILinkBridge {
|
|
|
6508
7201
|
/**
|
|
6509
7202
|
* Stop the bridge and disconnect.
|
|
6510
7203
|
*/
|
|
6511
|
-
async stop() {
|
|
7204
|
+
async stop({ logout }) {
|
|
6512
7205
|
this.stopPollLoop();
|
|
6513
7206
|
this.stopKeepalive();
|
|
6514
|
-
|
|
6515
|
-
|
|
7207
|
+
if (logout) {
|
|
7208
|
+
await clearCredentials();
|
|
7209
|
+
await clearCursor();
|
|
7210
|
+
}
|
|
6516
7211
|
this.client = null;
|
|
6517
7212
|
this.creds = null;
|
|
6518
7213
|
this.cursor = { get_updates_buf: "" };
|
|
@@ -6540,7 +7235,7 @@ var ILinkBridge = class _ILinkBridge {
|
|
|
6540
7235
|
msg: {
|
|
6541
7236
|
from_user_id: "",
|
|
6542
7237
|
to_user_id: toUserId,
|
|
6543
|
-
client_id: `fello:${Date.now()}-${
|
|
7238
|
+
client_id: `fello:${Date.now()}-${randomUUID7().slice(0, 8)}`,
|
|
6544
7239
|
message_type: 2,
|
|
6545
7240
|
// BOT
|
|
6546
7241
|
message_state: 2,
|
|
@@ -6600,7 +7295,7 @@ var ILinkBridge = class _ILinkBridge {
|
|
|
6600
7295
|
msg: {
|
|
6601
7296
|
from_user_id: "",
|
|
6602
7297
|
to_user_id: toUserId,
|
|
6603
|
-
client_id: `fello:${Date.now()}-${
|
|
7298
|
+
client_id: `fello:${Date.now()}-${randomUUID7().slice(0, 8)}`,
|
|
6604
7299
|
message_type: 2,
|
|
6605
7300
|
// BOT
|
|
6606
7301
|
message_state: 2,
|
|
@@ -6666,7 +7361,7 @@ var ILinkBridge = class _ILinkBridge {
|
|
|
6666
7361
|
msg: {
|
|
6667
7362
|
from_user_id: "",
|
|
6668
7363
|
to_user_id: toUserId,
|
|
6669
|
-
client_id: `fello:${Date.now()}-${
|
|
7364
|
+
client_id: `fello:${Date.now()}-${randomUUID7().slice(0, 8)}`,
|
|
6670
7365
|
message_type: 2,
|
|
6671
7366
|
// BOT
|
|
6672
7367
|
message_state: 2,
|
|
@@ -6885,6 +7580,9 @@ function buildMcpServersConfig(sessionMcpIds, options, ctx, deps) {
|
|
|
6885
7580
|
if (socketPath && features.includes("search")) {
|
|
6886
7581
|
servers.push(deps.search.buildSearchMcpServer({ projectDir: project.cwd, socketPath }));
|
|
6887
7582
|
}
|
|
7583
|
+
if (socketPath && features.includes("memory")) {
|
|
7584
|
+
servers.push(deps.memory.buildMemoryMcpServer({ projectDir: project.cwd, socketPath }));
|
|
7585
|
+
}
|
|
6888
7586
|
const globalSettings = ctx.storage.getSettings();
|
|
6889
7587
|
for (const id of sessionMcpIds) {
|
|
6890
7588
|
const config = globalSettings.mcpServers?.find((s) => s.id === id);
|
|
@@ -6918,12 +7616,30 @@ function buildMcpServersConfig(sessionMcpIds, options, ctx, deps) {
|
|
|
6918
7616
|
|
|
6919
7617
|
// src/backend/session/notifications.ts
|
|
6920
7618
|
import { readFile as readFile8 } from "fs/promises";
|
|
6921
|
-
import { omit } from "es-toolkit";
|
|
6922
|
-
import { randomUUID as
|
|
6923
|
-
|
|
7619
|
+
import { omit as omit2 } from "es-toolkit";
|
|
7620
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
7621
|
+
var CONTENT_BLOCK_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "resource_link", "resource"]);
|
|
7622
|
+
function isContentBlock(obj) {
|
|
7623
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
7624
|
+
const typed = obj;
|
|
7625
|
+
if (typeof typed.type !== "string" || !CONTENT_BLOCK_TYPES.has(typed.type)) return false;
|
|
7626
|
+
switch (typed.type) {
|
|
7627
|
+
case "text":
|
|
7628
|
+
return typeof typed.text === "string";
|
|
7629
|
+
case "image":
|
|
7630
|
+
return typeof typed.data === "string" || typeof typed.url === "string";
|
|
7631
|
+
case "audio":
|
|
7632
|
+
return typeof typed.data === "string" || typeof typed.url === "string";
|
|
7633
|
+
case "resource_link":
|
|
7634
|
+
return typeof typed.uri === "string";
|
|
7635
|
+
case "resource":
|
|
7636
|
+
return typeof typed.resource === "object" && typed.resource !== null;
|
|
7637
|
+
default:
|
|
7638
|
+
return false;
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
6924
7641
|
function findContentBlock(object) {
|
|
6925
|
-
|
|
6926
|
-
if (success) return data;
|
|
7642
|
+
if (isContentBlock(object)) return object;
|
|
6927
7643
|
if (object && typeof object === "object") {
|
|
6928
7644
|
for (const name in object) {
|
|
6929
7645
|
if (object.hasOwnProperty(name)) {
|
|
@@ -6993,7 +7709,11 @@ function createNotificationHandler(ctx, deps) {
|
|
|
6993
7709
|
...notification.update,
|
|
6994
7710
|
_meta: {
|
|
6995
7711
|
...notification.update?._meta,
|
|
6996
|
-
fello: {
|
|
7712
|
+
fello: {
|
|
7713
|
+
...notification.update?._meta?.fello,
|
|
7714
|
+
receivedAt: Date.now(),
|
|
7715
|
+
displayId: randomUUID8()
|
|
7716
|
+
}
|
|
6997
7717
|
}
|
|
6998
7718
|
}
|
|
6999
7719
|
};
|
|
@@ -7049,53 +7769,39 @@ function createNotificationHandler(ctx, deps) {
|
|
|
7049
7769
|
}
|
|
7050
7770
|
storage.appendSessionMessage(sessionId, {
|
|
7051
7771
|
...enrichedNotification,
|
|
7052
|
-
update:
|
|
7772
|
+
update: omit2(enrichedNotification.update, ["rawInput", "rawOutput"])
|
|
7053
7773
|
});
|
|
7054
7774
|
if (ilinkBridge?.isConnected && sessionId === ilinkActiveSessionId) {
|
|
7055
7775
|
flushIlinkMedia(ilinkBridge, deps.ilink);
|
|
7056
7776
|
}
|
|
7057
7777
|
}
|
|
7058
7778
|
} else {
|
|
7059
|
-
|
|
7779
|
+
if (sessionUpdate !== "available_commands_update" && sessionUpdate !== "usage_update") {
|
|
7780
|
+
storage.appendSessionMessage(sessionId, enrichedNotification);
|
|
7781
|
+
}
|
|
7060
7782
|
}
|
|
7061
7783
|
sendEvent("session-update", { sessionId, notification: enrichedNotification });
|
|
7062
7784
|
}
|
|
7063
7785
|
function mergeNotifications(notifications) {
|
|
7064
7786
|
const result = [];
|
|
7065
7787
|
for (const notification of notifications) {
|
|
7788
|
+
const sessionId = notification.sessionId;
|
|
7066
7789
|
const update = notification.update;
|
|
7067
|
-
if (!update) {
|
|
7068
|
-
result.push(notification);
|
|
7069
|
-
continue;
|
|
7070
|
-
}
|
|
7071
7790
|
const type = update.sessionUpdate;
|
|
7072
|
-
if (type === "agent_message_chunk" || type === "agent_thought_chunk") {
|
|
7073
|
-
const prev = result
|
|
7074
|
-
if (prev
|
|
7075
|
-
|
|
7076
|
-
...prev,
|
|
7077
|
-
update: {
|
|
7078
|
-
...prev.update,
|
|
7079
|
-
content: {
|
|
7080
|
-
...prev.update.content,
|
|
7081
|
-
text: prev.update.content.text + update.content.text
|
|
7082
|
-
}
|
|
7083
|
-
}
|
|
7084
|
-
};
|
|
7791
|
+
if ((type === "agent_message_chunk" || type === "agent_thought_chunk") && update.content.type === "text") {
|
|
7792
|
+
const prev = result[result.length - 1];
|
|
7793
|
+
if (prev && prev.sessionId === sessionId && prev.update.sessionUpdate === type && prev.update.content.type === "text") {
|
|
7794
|
+
prev.update.content.text += update.content.text;
|
|
7085
7795
|
continue;
|
|
7086
7796
|
}
|
|
7087
7797
|
}
|
|
7088
|
-
if (type === "
|
|
7798
|
+
if (type === "tool_call_update") {
|
|
7089
7799
|
const toolCallId = update.toolCallId;
|
|
7090
|
-
const
|
|
7091
|
-
|
|
7092
|
-
);
|
|
7093
|
-
if (
|
|
7094
|
-
|
|
7095
|
-
result[idx] = {
|
|
7096
|
-
...prev,
|
|
7097
|
-
update: { ...prev.update, ...update, sessionUpdate: "tool_call" }
|
|
7098
|
-
};
|
|
7800
|
+
const prev = result.find((n) => {
|
|
7801
|
+
return n.sessionId === sessionId && n.update.sessionUpdate === type && n.update.toolCallId === toolCallId;
|
|
7802
|
+
});
|
|
7803
|
+
if (prev && prev.update.sessionUpdate === type) {
|
|
7804
|
+
prev.update = { ...prev.update, ...update };
|
|
7099
7805
|
continue;
|
|
7100
7806
|
}
|
|
7101
7807
|
}
|
|
@@ -7120,10 +7826,10 @@ function createNotificationHandler(ctx, deps) {
|
|
|
7120
7826
|
// src/backend/session/index.ts
|
|
7121
7827
|
function createSessionModule(ctx, deps) {
|
|
7122
7828
|
const { sendEvent, storage } = ctx;
|
|
7123
|
-
const {
|
|
7124
|
-
const mcpDeps = { skills, askUser, shareToUser, search: search2 };
|
|
7829
|
+
const { bridgeConnect, askUser, shareToUser, skills, search: search2, memory, ilink: ilinkState } = deps;
|
|
7830
|
+
const mcpDeps = { skills, askUser, shareToUser, search: search2, memory };
|
|
7125
7831
|
const notif = createNotificationHandler(ctx, { ilink: ilinkState });
|
|
7126
|
-
|
|
7832
|
+
bridgeConnect.setBroadcast(notif.broadcastAndSaveSessionUpdate);
|
|
7127
7833
|
const sessionSocketServers = /* @__PURE__ */ new Map();
|
|
7128
7834
|
async function createSessionSocketServer(sessionId, options) {
|
|
7129
7835
|
const existing = sessionSocketServers.get(sessionId);
|
|
@@ -7137,6 +7843,9 @@ function createSessionModule(ctx, deps) {
|
|
|
7137
7843
|
skills.registerSkillsRoute(server, options.project.cwd);
|
|
7138
7844
|
shareToUser.registerShareToUserRoute(server, sessionId);
|
|
7139
7845
|
search2.registerSearchRoute(server, options.project.cwd);
|
|
7846
|
+
if (options.sessionContext) {
|
|
7847
|
+
memory.registerMemoryRoute(server, options.project.id, options.sessionContext);
|
|
7848
|
+
}
|
|
7140
7849
|
sessionSocketServers.set(sessionId, server);
|
|
7141
7850
|
return server;
|
|
7142
7851
|
}
|
|
@@ -7159,8 +7868,9 @@ function createSessionModule(ctx, deps) {
|
|
|
7159
7868
|
}) {
|
|
7160
7869
|
const project = storage.getProject(projectId);
|
|
7161
7870
|
if (!project) throw new Error("Project does not exist");
|
|
7162
|
-
const
|
|
7163
|
-
const
|
|
7871
|
+
const tempKey = `pending:${randomUUID9()}`;
|
|
7872
|
+
const b = await bridgeConnect.ensureBridge(tempKey, agentId, project.cwd);
|
|
7873
|
+
const socketPath = generateSocketPath(randomUUID9());
|
|
7164
7874
|
const sessionMcpIds = mcpServers ?? (storage.getSettings().mcpServers || []).filter((s) => !s.disabled).map((s) => s.id);
|
|
7165
7875
|
const effectiveFeatures = features ?? ALL_FEATURES;
|
|
7166
7876
|
const activeMcpServers = buildMcpServersConfig(
|
|
@@ -7182,7 +7892,13 @@ function createSessionModule(ctx, deps) {
|
|
|
7182
7892
|
modes: modes ?? null,
|
|
7183
7893
|
initializeInfo: b.initializeInfo
|
|
7184
7894
|
});
|
|
7185
|
-
|
|
7895
|
+
storage.updateSession(sessionInfo.id, { connectionStatus: "connected" }, false);
|
|
7896
|
+
bridgeConnect.rekeyBridge(tempKey, sessionInfo.id);
|
|
7897
|
+
await createSessionSocketServer(sessionInfo.id, {
|
|
7898
|
+
socketPath,
|
|
7899
|
+
project,
|
|
7900
|
+
sessionContext: { agentId, modelId: models?.currentModelId }
|
|
7901
|
+
});
|
|
7186
7902
|
sendEvent("sessions-changed", void 0);
|
|
7187
7903
|
return {
|
|
7188
7904
|
sessionId: sessionInfo.id,
|
|
@@ -7191,12 +7907,26 @@ function createSessionModule(ctx, deps) {
|
|
|
7191
7907
|
modes: modes ?? null
|
|
7192
7908
|
};
|
|
7193
7909
|
}
|
|
7910
|
+
const sessionLoadPromiseMap = /* @__PURE__ */ new Map();
|
|
7194
7911
|
async function loadSession({ sessionId, force }) {
|
|
7912
|
+
const last = sessionLoadPromiseMap.get(sessionId);
|
|
7913
|
+
if (last) {
|
|
7914
|
+
if (force) throw new Error("Session is already loading");
|
|
7915
|
+
return last;
|
|
7916
|
+
}
|
|
7917
|
+
const current = _loadSession({ sessionId, force });
|
|
7918
|
+
sessionLoadPromiseMap.set(sessionId, current);
|
|
7919
|
+
current.finally(() => {
|
|
7920
|
+
sessionLoadPromiseMap.delete(sessionId);
|
|
7921
|
+
});
|
|
7922
|
+
return current;
|
|
7923
|
+
}
|
|
7924
|
+
async function _loadSession({ sessionId, force }) {
|
|
7195
7925
|
const session = storage.getSession(sessionId);
|
|
7196
7926
|
if (!session) throw new Error("Session does not exist");
|
|
7197
7927
|
const project = storage.getProject(session.projectId);
|
|
7198
7928
|
if (!project) throw new Error("Project does not exist");
|
|
7199
|
-
const b = await
|
|
7929
|
+
const b = await bridgeConnect.ensureBridge(session.id, session.agentId, session.cwd);
|
|
7200
7930
|
if (b.isSessionLoaded(session.resumeId) && !force) {
|
|
7201
7931
|
return {
|
|
7202
7932
|
sessionId: session.id,
|
|
@@ -7211,7 +7941,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7211
7941
|
sendEvent("session-changed", { session });
|
|
7212
7942
|
}
|
|
7213
7943
|
const existingSocketServer = sessionSocketServers.get(session.id);
|
|
7214
|
-
const socketPath = existingSocketServer ? existingSocketServer.socketPath : generateSocketPath(
|
|
7944
|
+
const socketPath = existingSocketServer ? existingSocketServer.socketPath : generateSocketPath(randomUUID9());
|
|
7215
7945
|
const activeMcpServers = buildMcpServersConfig(
|
|
7216
7946
|
session.mcpServers,
|
|
7217
7947
|
{ project, socketPath, features: session.features },
|
|
@@ -7231,7 +7961,11 @@ function createSessionModule(ctx, deps) {
|
|
|
7231
7961
|
cwd: session.cwd,
|
|
7232
7962
|
mcpServers: activeMcpServers
|
|
7233
7963
|
});
|
|
7234
|
-
await createSessionSocketServer(session.id, {
|
|
7964
|
+
await createSessionSocketServer(session.id, {
|
|
7965
|
+
socketPath,
|
|
7966
|
+
project,
|
|
7967
|
+
sessionContext: { agentId: session.agentId, modelId: session.models?.currentModelId }
|
|
7968
|
+
});
|
|
7235
7969
|
} finally {
|
|
7236
7970
|
notif.removeRestoring(session.id);
|
|
7237
7971
|
}
|
|
@@ -7289,7 +8023,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7289
8023
|
console.log(
|
|
7290
8024
|
`[Fello] Session ${sessionId} is already streaming, cancelling previous generation...`
|
|
7291
8025
|
);
|
|
7292
|
-
const connectPromise =
|
|
8026
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7293
8027
|
if (connectPromise) {
|
|
7294
8028
|
const pending = await askUser.getPendingAskUserRequests({ sessionId });
|
|
7295
8029
|
for (const req of pending) {
|
|
@@ -7326,10 +8060,10 @@ function createSessionModule(ctx, deps) {
|
|
|
7326
8060
|
storage.updateSession(sessionId, { title: fallbackTitle });
|
|
7327
8061
|
}
|
|
7328
8062
|
}
|
|
7329
|
-
const b = await
|
|
8063
|
+
const b = await bridgeConnect.ensureBridge(sessionId, session.agentId, session.cwd);
|
|
7330
8064
|
if (!b.isSessionLoaded(session.resumeId)) {
|
|
7331
8065
|
console.log(`[Fello] Session ${session.resumeId} not loaded in Agent, lazy loading...`);
|
|
7332
|
-
const socketPath = generateSocketPath(
|
|
8066
|
+
const socketPath = generateSocketPath(randomUUID9());
|
|
7333
8067
|
const activeMcpServers = buildMcpServersConfig(
|
|
7334
8068
|
session.mcpServers,
|
|
7335
8069
|
{ project, socketPath, features: session.features },
|
|
@@ -7341,7 +8075,11 @@ function createSessionModule(ctx, deps) {
|
|
|
7341
8075
|
cwd: session.cwd,
|
|
7342
8076
|
mcpServers: activeMcpServers
|
|
7343
8077
|
});
|
|
7344
|
-
await createSessionSocketServer(session.id, {
|
|
8078
|
+
await createSessionSocketServer(session.id, {
|
|
8079
|
+
socketPath,
|
|
8080
|
+
project,
|
|
8081
|
+
sessionContext: { agentId: session.agentId, modelId: session.models?.currentModelId }
|
|
8082
|
+
});
|
|
7345
8083
|
}
|
|
7346
8084
|
storage.updateSession(sessionId, { isStreaming: true });
|
|
7347
8085
|
const updated = storage.getSession(sessionId);
|
|
@@ -7427,7 +8165,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7427
8165
|
console.warn("[CancelPrompt] Respond Previous Ask User Error", err);
|
|
7428
8166
|
}
|
|
7429
8167
|
}
|
|
7430
|
-
const connectPromise =
|
|
8168
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7431
8169
|
if (connectPromise) {
|
|
7432
8170
|
const b = await connectPromise;
|
|
7433
8171
|
await b.cancel({ sessionId: session.resumeId });
|
|
@@ -7436,7 +8174,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7436
8174
|
console.log(`[CancelPrompt] Killed ${killed} agent terminal(s) for session ${sessionId}`);
|
|
7437
8175
|
}
|
|
7438
8176
|
}
|
|
7439
|
-
async function
|
|
8177
|
+
async function updateSession2({
|
|
7440
8178
|
sessionId,
|
|
7441
8179
|
...updates
|
|
7442
8180
|
}) {
|
|
@@ -7447,18 +8185,22 @@ function createSessionModule(ctx, deps) {
|
|
|
7447
8185
|
async function changeWorkDir() {
|
|
7448
8186
|
return { ok: false, cwd: null };
|
|
7449
8187
|
}
|
|
7450
|
-
async function
|
|
8188
|
+
async function deleteSession2(sessionId) {
|
|
7451
8189
|
const session = storage.getSession(sessionId);
|
|
7452
8190
|
if (session) {
|
|
7453
8191
|
try {
|
|
7454
|
-
const
|
|
7455
|
-
if (
|
|
7456
|
-
|
|
8192
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
8193
|
+
if (connectPromise) {
|
|
8194
|
+
const b = await connectPromise;
|
|
8195
|
+
if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
|
|
8196
|
+
await b.deleteSession(session.resumeId);
|
|
8197
|
+
}
|
|
7457
8198
|
} catch (error) {
|
|
7458
8199
|
console.warn(
|
|
7459
8200
|
`[backend] Failed to close/delete session on agent for ${session.agentId}:${session.resumeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
7460
8201
|
);
|
|
7461
8202
|
}
|
|
8203
|
+
await bridgeConnect.killBridge(sessionId);
|
|
7462
8204
|
}
|
|
7463
8205
|
storage.deleteSession(sessionId);
|
|
7464
8206
|
stopSessionSocketServer(sessionId);
|
|
@@ -7473,11 +8215,14 @@ function createSessionModule(ctx, deps) {
|
|
|
7473
8215
|
}
|
|
7474
8216
|
sendEvent("sessions-changed", void 0);
|
|
7475
8217
|
}
|
|
8218
|
+
function getSessionDataSystemPath({ sessionId }) {
|
|
8219
|
+
return storage.getSessionDataSystemPath(sessionId);
|
|
8220
|
+
}
|
|
7476
8221
|
async function resetAgentSessions(agentId) {
|
|
7477
8222
|
const sessions = storage.listSessions().filter((s) => s.agentId === agentId);
|
|
7478
8223
|
for (const session of sessions) {
|
|
7479
8224
|
try {
|
|
7480
|
-
const connectPromise =
|
|
8225
|
+
const connectPromise = bridgeConnect.bridges.get(session.id);
|
|
7481
8226
|
if (connectPromise) {
|
|
7482
8227
|
const b = await connectPromise;
|
|
7483
8228
|
if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
|
|
@@ -7487,6 +8232,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7487
8232
|
`[backend] Failed to close session on agent for ${session.agentId}:${session.resumeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
7488
8233
|
);
|
|
7489
8234
|
}
|
|
8235
|
+
await bridgeConnect.killBridge(session.id);
|
|
7490
8236
|
stopSessionSocketServer(session.id);
|
|
7491
8237
|
}
|
|
7492
8238
|
if (sessions.length > 0) sendEvent("sessions-changed", void 0);
|
|
@@ -7497,14 +8243,18 @@ function createSessionModule(ctx, deps) {
|
|
|
7497
8243
|
const ids = sessions.map((s) => s.id);
|
|
7498
8244
|
for (const session of sessions) {
|
|
7499
8245
|
try {
|
|
7500
|
-
const
|
|
7501
|
-
if (
|
|
7502
|
-
|
|
8246
|
+
const connectPromise = bridgeConnect.bridges.get(session.id);
|
|
8247
|
+
if (connectPromise) {
|
|
8248
|
+
const b = await connectPromise;
|
|
8249
|
+
if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
|
|
8250
|
+
await b.deleteSession(session.resumeId);
|
|
8251
|
+
}
|
|
7503
8252
|
} catch (error) {
|
|
7504
8253
|
console.warn(
|
|
7505
8254
|
`[backend] Failed to close/delete session on agent for ${session.agentId}:${session.resumeId}: ${error instanceof Error ? error.message : String(error)}`
|
|
7506
8255
|
);
|
|
7507
8256
|
}
|
|
8257
|
+
await bridgeConnect.killBridge(session.id);
|
|
7508
8258
|
storage.deleteSession(session.id);
|
|
7509
8259
|
stopSessionSocketServer(session.id);
|
|
7510
8260
|
if (ilinkState.getActiveSessionId() === session.id) {
|
|
@@ -7523,7 +8273,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7523
8273
|
async function getModels({ sessionId }) {
|
|
7524
8274
|
const session = storage.getSession(sessionId);
|
|
7525
8275
|
if (!session) return null;
|
|
7526
|
-
const connectPromise =
|
|
8276
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7527
8277
|
if (!connectPromise) return null;
|
|
7528
8278
|
const b = await connectPromise;
|
|
7529
8279
|
return b.getModelState(session.resumeId);
|
|
@@ -7531,7 +8281,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7531
8281
|
async function setModel({ sessionId, modelId }) {
|
|
7532
8282
|
const session = storage.getSession(sessionId);
|
|
7533
8283
|
if (!session) throw new Error("Session does not exist");
|
|
7534
|
-
const connectPromise =
|
|
8284
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7535
8285
|
if (!connectPromise) throw new Error("Agent bridge not found for session");
|
|
7536
8286
|
const b = await connectPromise;
|
|
7537
8287
|
await b.setSessionModel({ sessionId: session.resumeId, modelId });
|
|
@@ -7545,7 +8295,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7545
8295
|
async function getModes({ sessionId }) {
|
|
7546
8296
|
const session = storage.getSession(sessionId);
|
|
7547
8297
|
if (!session) return null;
|
|
7548
|
-
const connectPromise =
|
|
8298
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7549
8299
|
if (!connectPromise) return null;
|
|
7550
8300
|
const b = await connectPromise;
|
|
7551
8301
|
return b.getModeState(session.resumeId);
|
|
@@ -7553,7 +8303,7 @@ function createSessionModule(ctx, deps) {
|
|
|
7553
8303
|
async function setMode({ sessionId, modeId }) {
|
|
7554
8304
|
const session = storage.getSession(sessionId);
|
|
7555
8305
|
if (!session) throw new Error("Session does not exist");
|
|
7556
|
-
const connectPromise =
|
|
8306
|
+
const connectPromise = bridgeConnect.bridges.get(sessionId);
|
|
7557
8307
|
if (!connectPromise) throw new Error("Agent bridge not found for session");
|
|
7558
8308
|
const b = await connectPromise;
|
|
7559
8309
|
await b.setSessionMode({ sessionId: session.resumeId, modeId });
|
|
@@ -7572,9 +8322,10 @@ function createSessionModule(ctx, deps) {
|
|
|
7572
8322
|
getSessionHistory,
|
|
7573
8323
|
sendPrompt,
|
|
7574
8324
|
cancelPrompt,
|
|
7575
|
-
updateSession,
|
|
8325
|
+
updateSession: updateSession2,
|
|
7576
8326
|
changeWorkDir,
|
|
7577
|
-
deleteSession,
|
|
8327
|
+
deleteSession: deleteSession2,
|
|
8328
|
+
getSessionDataSystemPath,
|
|
7578
8329
|
resetAgentSessions,
|
|
7579
8330
|
deleteAgentSessions,
|
|
7580
8331
|
getModels,
|
|
@@ -7818,15 +8569,15 @@ function createIlinkModule(ctx) {
|
|
|
7818
8569
|
}
|
|
7819
8570
|
const entry = projectEntries[num - 1];
|
|
7820
8571
|
const settings = storage.getSettings();
|
|
7821
|
-
const
|
|
7822
|
-
if (!
|
|
8572
|
+
const agent2 = settings.agents.find((a) => !a.disabled);
|
|
8573
|
+
if (!agent2) {
|
|
7823
8574
|
if (msg.from_user_id) bridge?.sendTextReply(msg.from_user_id, t("ilink.noAgent"));
|
|
7824
8575
|
return;
|
|
7825
8576
|
}
|
|
7826
8577
|
const defaultMcpIds = (settings.mcpServers || []).filter((s) => !s.disabled).map((s) => s.id);
|
|
7827
8578
|
getHandlers().newSession({
|
|
7828
8579
|
projectId: entry.projectId,
|
|
7829
|
-
agentId:
|
|
8580
|
+
agentId: agent2.id,
|
|
7830
8581
|
mcpServers: defaultMcpIds,
|
|
7831
8582
|
features: ALL_FEATURES,
|
|
7832
8583
|
permissionMode: "allow-all"
|
|
@@ -8016,15 +8767,17 @@ function createIlinkModule(ctx) {
|
|
|
8016
8767
|
const status = await b.checkQrcodeStatus(qrcode);
|
|
8017
8768
|
return { status };
|
|
8018
8769
|
}
|
|
8019
|
-
async function stopIlink() {
|
|
8770
|
+
async function stopIlink({ logout }) {
|
|
8020
8771
|
if (bridge) {
|
|
8021
|
-
await bridge.stop();
|
|
8772
|
+
await bridge.stop({ logout });
|
|
8022
8773
|
bridge = null;
|
|
8023
8774
|
}
|
|
8024
8775
|
activeSessionId = null;
|
|
8025
8776
|
replyBuffer = "";
|
|
8026
|
-
|
|
8027
|
-
|
|
8777
|
+
if (logout) {
|
|
8778
|
+
await writeActiveSessionId(null);
|
|
8779
|
+
sendEvent("ilink-active-session-changed", { sessionId: null });
|
|
8780
|
+
}
|
|
8028
8781
|
}
|
|
8029
8782
|
async function setActiveIlinkSession({ sessionId }) {
|
|
8030
8783
|
if (!sessionId) {
|
|
@@ -8078,7 +8831,7 @@ function createIlinkModule(ctx) {
|
|
|
8078
8831
|
}
|
|
8079
8832
|
|
|
8080
8833
|
// src/backend/inference.ts
|
|
8081
|
-
import { randomUUID as
|
|
8834
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
8082
8835
|
function createInferenceModule(_ctx, deps) {
|
|
8083
8836
|
async function runInference(req) {
|
|
8084
8837
|
const {
|
|
@@ -8087,13 +8840,17 @@ function createInferenceModule(_ctx, deps) {
|
|
|
8087
8840
|
model,
|
|
8088
8841
|
cwd = process.cwd(),
|
|
8089
8842
|
features = [],
|
|
8090
|
-
timeout =
|
|
8843
|
+
timeout = 30 * 60 * 1e3
|
|
8091
8844
|
} = req;
|
|
8092
8845
|
const notifications = [];
|
|
8093
8846
|
const textChunks = [];
|
|
8847
|
+
const terminalLogs = {};
|
|
8094
8848
|
const agentInfo = resolveAgentInfo(agentId);
|
|
8095
8849
|
const bridge = new ACPBridge(agentId, {
|
|
8096
8850
|
agentInfo,
|
|
8851
|
+
cwd,
|
|
8852
|
+
onSessionConnect: () => {
|
|
8853
|
+
},
|
|
8097
8854
|
onSessionUpdate: (notification) => {
|
|
8098
8855
|
notifications.push(notification);
|
|
8099
8856
|
if (notification.update?.sessionUpdate === "agent_message_chunk") {
|
|
@@ -8103,11 +8860,14 @@ function createInferenceModule(_ctx, deps) {
|
|
|
8103
8860
|
}
|
|
8104
8861
|
}
|
|
8105
8862
|
},
|
|
8863
|
+
onExtNotification: () => {
|
|
8864
|
+
},
|
|
8106
8865
|
onPermissionRequest: async (request) => {
|
|
8107
8866
|
const opt = request.options.find((o) => o.kind === "allow_always") ?? request.options.find((o) => o.kind === "allow_once") ?? request.options[0];
|
|
8108
8867
|
return { outcome: { outcome: "selected", optionId: opt?.optionId ?? "allow" } };
|
|
8109
8868
|
},
|
|
8110
|
-
onAgentTerminalOutput: () => {
|
|
8869
|
+
onAgentTerminalOutput: (_sessionId, terminalId, data) => {
|
|
8870
|
+
terminalLogs[terminalId] = (terminalLogs[terminalId] ?? "") + data;
|
|
8111
8871
|
}
|
|
8112
8872
|
});
|
|
8113
8873
|
const servers = [];
|
|
@@ -8115,7 +8875,7 @@ function createInferenceModule(_ctx, deps) {
|
|
|
8115
8875
|
await bridge.connect();
|
|
8116
8876
|
const mcpServers = [...req.mcpServers ?? []];
|
|
8117
8877
|
if (features.length > 0) {
|
|
8118
|
-
const socketPath = generateSocketPath(`inf-${
|
|
8878
|
+
const socketPath = generateSocketPath(`inf-${randomUUID10()}`);
|
|
8119
8879
|
const socketServer = await startSocketServer(socketPath);
|
|
8120
8880
|
servers.push(socketServer);
|
|
8121
8881
|
if (features.includes("skills")) {
|
|
@@ -8149,7 +8909,7 @@ function createInferenceModule(_ctx, deps) {
|
|
|
8149
8909
|
}
|
|
8150
8910
|
await bridge.closeSession(sessionId);
|
|
8151
8911
|
await bridge.deleteSession(sessionId);
|
|
8152
|
-
return { text: textChunks.join(""), notifications, stopReason };
|
|
8912
|
+
return { text: textChunks.join(""), notifications, terminalLogs, stopReason };
|
|
8153
8913
|
} finally {
|
|
8154
8914
|
await bridge.kill().catch(() => {
|
|
8155
8915
|
});
|
|
@@ -8160,7 +8920,7 @@ function createInferenceModule(_ctx, deps) {
|
|
|
8160
8920
|
}
|
|
8161
8921
|
|
|
8162
8922
|
// src/backend/automation/index.ts
|
|
8163
|
-
import { join as
|
|
8923
|
+
import { join as join20 } from "path";
|
|
8164
8924
|
import { CronJob } from "cron";
|
|
8165
8925
|
function createAutomationModule(ctx, deps) {
|
|
8166
8926
|
const { sendEvent, storage } = ctx;
|
|
@@ -8273,21 +9033,37 @@ function createAutomationModule(ctx, deps) {
|
|
|
8273
9033
|
...n,
|
|
8274
9034
|
update: {
|
|
8275
9035
|
...n.update,
|
|
8276
|
-
_meta: {
|
|
9036
|
+
_meta: {
|
|
9037
|
+
...n.update?._meta,
|
|
9038
|
+
fello: {
|
|
9039
|
+
...n.update?._meta?.fello,
|
|
9040
|
+
receivedAt: Date.now(),
|
|
9041
|
+
displayId: `auto-${i}`
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
8277
9044
|
}
|
|
8278
9045
|
}));
|
|
8279
|
-
const messages = reduceNotificationsToMessages(notifications);
|
|
8280
|
-
store.writeTaskFile(
|
|
8281
|
-
scheduleId,
|
|
8282
|
-
taskId,
|
|
8283
|
-
"conversation.json",
|
|
8284
|
-
JSON.stringify(messages, null, 2)
|
|
8285
|
-
);
|
|
8286
9046
|
store.writeTaskFile(
|
|
8287
9047
|
scheduleId,
|
|
8288
9048
|
taskId,
|
|
8289
|
-
"
|
|
8290
|
-
|
|
9049
|
+
".fello-conversation.json",
|
|
9050
|
+
JSON.stringify(
|
|
9051
|
+
{
|
|
9052
|
+
__type: "fello-conversation",
|
|
9053
|
+
meta: {
|
|
9054
|
+
name: schedule.name,
|
|
9055
|
+
agentId: schedule.agentId,
|
|
9056
|
+
modelId: schedule.modelId ?? null,
|
|
9057
|
+
prompt: schedule.prompt,
|
|
9058
|
+
startedAt,
|
|
9059
|
+
completedAt: Date.now()
|
|
9060
|
+
},
|
|
9061
|
+
notifications,
|
|
9062
|
+
terminalLogs: result.terminalLogs
|
|
9063
|
+
},
|
|
9064
|
+
null,
|
|
9065
|
+
2
|
|
9066
|
+
)
|
|
8291
9067
|
);
|
|
8292
9068
|
schedule.lastRunAt = Date.now();
|
|
8293
9069
|
schedule.updatedAt = Date.now();
|
|
@@ -8306,15 +9082,6 @@ function createAutomationModule(ctx, deps) {
|
|
|
8306
9082
|
store.saveTask(scheduleId, task);
|
|
8307
9083
|
sendEvent("task-update", { scheduleId, task });
|
|
8308
9084
|
console.error(`[Automation] Task failed:`, errorMessage);
|
|
8309
|
-
try {
|
|
8310
|
-
store.writeTaskFile(
|
|
8311
|
-
scheduleId,
|
|
8312
|
-
taskId,
|
|
8313
|
-
"README.md",
|
|
8314
|
-
generateErrorReadme(schedule, [], startedAt, errorMessage, storage.getSettings().i18n?.language)
|
|
8315
|
-
);
|
|
8316
|
-
} catch {
|
|
8317
|
-
}
|
|
8318
9085
|
return task;
|
|
8319
9086
|
} finally {
|
|
8320
9087
|
runningTasks.delete(scheduleId);
|
|
@@ -8353,7 +9120,7 @@ function createAutomationModule(ctx, deps) {
|
|
|
8353
9120
|
}
|
|
8354
9121
|
function getTaskFileSystemPath(scheduleId, taskId, filePath) {
|
|
8355
9122
|
const base = store.taskDir(scheduleId, taskId);
|
|
8356
|
-
const fullPath =
|
|
9123
|
+
const fullPath = join20(base, filePath);
|
|
8357
9124
|
if (!fullPath.startsWith(base + "/") && fullPath !== base) throw new Error("Invalid file path");
|
|
8358
9125
|
return fullPath;
|
|
8359
9126
|
}
|
|
@@ -8375,84 +9142,374 @@ function createAutomationModule(ctx, deps) {
|
|
|
8375
9142
|
stopAllCrons
|
|
8376
9143
|
};
|
|
8377
9144
|
}
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
9145
|
+
|
|
9146
|
+
// src/backend/memory.ts
|
|
9147
|
+
import { join as join21 } from "path";
|
|
9148
|
+
import { randomUUID as randomUUID11, createHash as createHash5 } from "crypto";
|
|
9149
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync8, rmSync as rmSync4 } from "fs";
|
|
9150
|
+
|
|
9151
|
+
// src/shared/zod/mcp-memory-schema.ts
|
|
9152
|
+
import { z as z6 } from "zod";
|
|
9153
|
+
var memoryQueryRequestSchema = z6.object({
|
|
9154
|
+
query: z6.string().min(1).describe(
|
|
9155
|
+
"What to search for in memory. Describe what you need to recall (e.g. 'tech stack', 'user preferences', 'build commands')."
|
|
9156
|
+
)
|
|
9157
|
+
});
|
|
9158
|
+
var memoryQueryRespondSchema = z6.object({
|
|
9159
|
+
content: z6.string().describe("The memory content (full or filtered).")
|
|
9160
|
+
});
|
|
9161
|
+
var memoryStoreRequestSchema = z6.object({
|
|
9162
|
+
facts: z6.array(
|
|
9163
|
+
z6.object({
|
|
9164
|
+
text: z6.string().max(200).describe("The fact to remember, concise and self-contained."),
|
|
9165
|
+
reason: z6.string().max(300).optional().describe(
|
|
9166
|
+
"Why this should be remembered \u2014 context that helps determine weight and tags (e.g. 'user explicitly corrected me', 'user emphasized with \u4E00\u5B9A/never')."
|
|
9167
|
+
)
|
|
9168
|
+
})
|
|
9169
|
+
).min(1).max(10).describe(
|
|
9170
|
+
"Facts to remember. The system will organize, deduplicate, and assign priority in the background."
|
|
9171
|
+
)
|
|
9172
|
+
});
|
|
9173
|
+
var memoryStoreRespondSchema = z6.object({
|
|
9174
|
+
stored: z6.number().describe("Number of facts successfully integrated."),
|
|
9175
|
+
summary: z6.string().optional().describe("Brief confirmation of what was stored.")
|
|
9176
|
+
});
|
|
9177
|
+
|
|
9178
|
+
// src/shared/zod/mcp-memo-schema.ts
|
|
9179
|
+
import { z as z7 } from "zod";
|
|
9180
|
+
var memoryEntrySchema = z7.object({
|
|
9181
|
+
weight: z7.number().int().min(1).max(3).describe("Priority weight: 3=critical, 2=important, 1=general"),
|
|
9182
|
+
text: z7.string().max(200).describe("The memory fact, concise and self-contained"),
|
|
9183
|
+
date: z7.string().describe("Date when this fact was recorded or last updated (YYYY-MM-DD)"),
|
|
9184
|
+
tags: z7.array(z7.string()).describe("Category tags: preferences, architecture, commands, corrections, context")
|
|
9185
|
+
});
|
|
9186
|
+
var memoryFileSchema = z7.object({
|
|
9187
|
+
version: z7.number().int().describe("Schema version for future migration"),
|
|
9188
|
+
entries: z7.array(memoryEntrySchema).describe("Memory entries sorted by weight descending")
|
|
9189
|
+
});
|
|
9190
|
+
var MEMORY_FILE_VERSION = 1;
|
|
9191
|
+
var memoGetCurrentRequestSchema = z7.object({});
|
|
9192
|
+
var memoGetCurrentRespondSchema = z7.object({
|
|
9193
|
+
content: z7.string().describe("The current memory.json content as a JSON string.")
|
|
9194
|
+
});
|
|
9195
|
+
var memoSaveRequestSchema = z7.object({
|
|
9196
|
+
content: z7.string().max(16e3).describe(
|
|
9197
|
+
"The complete memory content as a JSON string. Must conform to the memory file schema: { version, entries: [...] }"
|
|
9198
|
+
)
|
|
9199
|
+
});
|
|
9200
|
+
var memoSaveRespondSchema = z7.object({
|
|
9201
|
+
ok: z7.boolean(),
|
|
9202
|
+
entries: z7.number().describe("Number of entries saved.")
|
|
9203
|
+
});
|
|
9204
|
+
var memoTouchRequestSchema = z7.object({
|
|
9205
|
+
indices: z7.array(z7.number().int().min(0)).min(1).describe(
|
|
9206
|
+
"Indices of entries in the entries array that were relevant/used. Updates their date to today."
|
|
9207
|
+
)
|
|
9208
|
+
});
|
|
9209
|
+
var memoTouchRespondSchema = z7.object({
|
|
9210
|
+
ok: z7.boolean(),
|
|
9211
|
+
touched: z7.number().describe("Number of entries touched.")
|
|
9212
|
+
});
|
|
9213
|
+
|
|
9214
|
+
// src/backend/memory.ts
|
|
9215
|
+
var MEMORY_FILENAME = "memory.json";
|
|
9216
|
+
var MAX_ENTRIES = 50;
|
|
9217
|
+
var SUMMARY_MAX_ENTRIES = 15;
|
|
9218
|
+
function getProjectIdFromCwd(cwd) {
|
|
9219
|
+
return createHash5("sha1").update(cwd).digest("hex");
|
|
9220
|
+
}
|
|
9221
|
+
function getMemoryPath(projectId) {
|
|
9222
|
+
return join21(PROJECTS_DIR, projectId, MEMORY_FILENAME);
|
|
9223
|
+
}
|
|
9224
|
+
function readMemoryEntries(projectId) {
|
|
9225
|
+
const memoryPath = getMemoryPath(projectId);
|
|
9226
|
+
if (!existsSync8(memoryPath)) return [];
|
|
9227
|
+
try {
|
|
9228
|
+
const raw = readFileSync7(memoryPath, "utf-8");
|
|
9229
|
+
const parsed = JSON.parse(raw);
|
|
9230
|
+
const file = memoryFileSchema.parse(parsed);
|
|
9231
|
+
return file.entries;
|
|
9232
|
+
} catch {
|
|
9233
|
+
return [];
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9236
|
+
function writeMemoryEntries(projectId, entries) {
|
|
9237
|
+
const dir = join21(PROJECTS_DIR, projectId);
|
|
9238
|
+
mkdirSync8(dir, { recursive: true });
|
|
9239
|
+
const sorted = [...entries].sort((a, b) => b.weight - a.weight);
|
|
9240
|
+
const capped = sorted.slice(0, MAX_ENTRIES);
|
|
9241
|
+
const file = { version: MEMORY_FILE_VERSION, entries: capped };
|
|
9242
|
+
writeFileSync6(getMemoryPath(projectId), JSON.stringify(file, null, 2), "utf-8");
|
|
9243
|
+
}
|
|
9244
|
+
function entriesToText(entries) {
|
|
9245
|
+
return entries.map((e) => e.text).join("\n");
|
|
9246
|
+
}
|
|
9247
|
+
var WriteQueue = class {
|
|
9248
|
+
queues = /* @__PURE__ */ new Map();
|
|
9249
|
+
async enqueue(projectId, task) {
|
|
9250
|
+
const prev = this.queues.get(projectId) ?? Promise.resolve();
|
|
9251
|
+
const next = prev.then(task, task);
|
|
9252
|
+
this.queues.set(projectId, next);
|
|
9253
|
+
next.finally(() => {
|
|
9254
|
+
if (this.queues.get(projectId) === next) {
|
|
9255
|
+
this.queues.delete(projectId);
|
|
8407
9256
|
}
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
9257
|
+
});
|
|
9258
|
+
return next;
|
|
9259
|
+
}
|
|
9260
|
+
};
|
|
9261
|
+
function buildMemoWritePrompt(newFacts) {
|
|
9262
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9263
|
+
const factsFormatted = newFacts.map((f) => {
|
|
9264
|
+
if (f.reason) return ` - "${f.text}" (reason: ${f.reason})`;
|
|
9265
|
+
return ` - "${f.text}"`;
|
|
9266
|
+
}).join("\n");
|
|
9267
|
+
return `You are a Memory Organizer Agent. Your job is to maintain a project memory file in JSON format.
|
|
9268
|
+
|
|
9269
|
+
## Storage Format
|
|
9270
|
+
|
|
9271
|
+
The memory file is a JSON object with this structure:
|
|
9272
|
+
|
|
9273
|
+
\`\`\`json
|
|
9274
|
+
{
|
|
9275
|
+
"version": 1,
|
|
9276
|
+
"entries": [
|
|
9277
|
+
{ "weight": 3, "text": "The fact to remember", "date": "YYYY-MM-DD", "tags": ["category"] }
|
|
9278
|
+
]
|
|
9279
|
+
}
|
|
9280
|
+
\`\`\`
|
|
9281
|
+
|
|
9282
|
+
## Fields
|
|
9283
|
+
- **version**: Always 1
|
|
9284
|
+
- **entries**: Array sorted by weight descending. Each entry:
|
|
9285
|
+
- **weight**: Priority (3=critical, 2=important, 1=general). Never use negative values in output; just remove outdated entries.
|
|
9286
|
+
- **text**: Concise fact, under 100 characters when possible. Self-contained.
|
|
9287
|
+
- **date**: Today's date for new/updated entries: ${today}
|
|
9288
|
+
- **tags**: One or more from: preferences, architecture, commands, corrections, context
|
|
9289
|
+
|
|
9290
|
+
## Weight Assignment
|
|
9291
|
+
- 3: Critical rule. User strongly emphasized (keywords: "always", "never", "remember", "\u4E00\u5B9A", "\u6C38\u8FDC\u4E0D\u8981", "\u5FC5\u987B")
|
|
9292
|
+
- 2: Important preference or decision
|
|
9293
|
+
- 1: General info (default for new facts)
|
|
9294
|
+
|
|
9295
|
+
## Your Task
|
|
9296
|
+
1. Call memo_get_current() to read current memory
|
|
9297
|
+
2. Parse the JSON
|
|
9298
|
+
3. Integrate the following new facts (use the "reason" to help determine weight and tags):
|
|
9299
|
+
${factsFormatted}
|
|
9300
|
+
4. Integration rules:
|
|
9301
|
+
- If a new fact contradicts an existing one: remove the old, add the new
|
|
9302
|
+
- If a new fact reinforces an existing one: upgrade weight (max 3)
|
|
9303
|
+
- If a new fact is a duplicate: skip it
|
|
9304
|
+
5. Maintenance:
|
|
9305
|
+
- Remove outdated/negated entries entirely
|
|
9306
|
+
- Keep total entries \u2264 50
|
|
9307
|
+
- If over 50: remove lowest weight + oldest date first
|
|
9308
|
+
6. Call memo_save(content) with the COMPLETE JSON object as a string
|
|
9309
|
+
- Must include "version": 1 and "entries" array
|
|
9310
|
+
- Entries must be sorted by weight descending
|
|
9311
|
+
- Output valid JSON only \u2014 no markdown fences, no explanation
|
|
9312
|
+
|
|
9313
|
+
## Rules
|
|
9314
|
+
- Preserve the original language of each fact (don't translate)
|
|
9315
|
+
- You MUST call memo_save(content) \u2014 this is non-negotiable. The task is incomplete without it.
|
|
9316
|
+
- After memo_save() succeeds, reply briefly with the result (e.g. "Saved N entries."). No lengthy explanation needed.
|
|
9317
|
+
- The content passed to memo_save must be a valid JSON object string`;
|
|
9318
|
+
}
|
|
9319
|
+
function buildMemoQueryPrompt(query) {
|
|
9320
|
+
return `You are a Memory Retrieval Agent. Your job is to find and summarize relevant memories for a given query.
|
|
9321
|
+
|
|
9322
|
+
## Your Task
|
|
9323
|
+
1. Call memo_get_current() to read the current memory
|
|
9324
|
+
2. Find entries relevant to this query: "${query}"
|
|
9325
|
+
3. Call memo_touch({ indices: [...] }) with the indices of all relevant entries (to mark them as active)
|
|
9326
|
+
4. Summarize the relevant memories in natural language, organized and easy to understand
|
|
9327
|
+
5. If no entries are relevant, respond with: (no relevant memories found)
|
|
9328
|
+
|
|
9329
|
+
## Response Style
|
|
9330
|
+
- Use natural language, not raw data format
|
|
9331
|
+
- Group related facts together
|
|
9332
|
+
- Highlight the most important/high-weight items first
|
|
9333
|
+
- Be concise but complete \u2014 don't lose information
|
|
9334
|
+
- Respond in the same language as the query
|
|
9335
|
+
|
|
9336
|
+
## Rules
|
|
9337
|
+
- You MUST call memo_get_current() first \u2014 this is required
|
|
9338
|
+
- You MUST call memo_touch() with the indices of relevant entries \u2014 this keeps them from being evicted
|
|
9339
|
+
- Base your response ONLY on what's in the memory \u2014 do not fabricate
|
|
9340
|
+
- Preserve specific details (names, paths, versions) exactly as stored`;
|
|
9341
|
+
}
|
|
9342
|
+
function createMemoryModule(_ctx, deps) {
|
|
9343
|
+
const writeQueue = new WriteQueue();
|
|
9344
|
+
async function runMemoAgent(params) {
|
|
9345
|
+
const { projectId, prompt, sessionContext, writable = false } = params;
|
|
9346
|
+
const tempId = `memo-${randomUUID11()}`;
|
|
9347
|
+
const tempDir = join21(TEMP_DIR, tempId);
|
|
9348
|
+
mkdirSync8(tempDir, { recursive: true });
|
|
9349
|
+
const memoSocketPath = generateSocketPath(`memo-${randomUUID11()}`);
|
|
9350
|
+
let memoSocketServer = null;
|
|
9351
|
+
try {
|
|
9352
|
+
memoSocketServer = await startSocketServer(memoSocketPath);
|
|
9353
|
+
memoSocketServer.registry("memo/read", async () => {
|
|
9354
|
+
const entries = readMemoryEntries(projectId);
|
|
9355
|
+
const file = { version: MEMORY_FILE_VERSION, entries };
|
|
9356
|
+
return { content: JSON.stringify(file, null, 2) };
|
|
9357
|
+
});
|
|
9358
|
+
memoSocketServer.registry("memo/touch", async (payload) => {
|
|
9359
|
+
const { indices } = memoTouchRequestSchema.parse(payload);
|
|
9360
|
+
const entries = readMemoryEntries(projectId);
|
|
9361
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9362
|
+
let touched = 0;
|
|
9363
|
+
for (const idx of indices) {
|
|
9364
|
+
if (idx >= 0 && idx < entries.length) {
|
|
9365
|
+
entries[idx].date = today;
|
|
9366
|
+
touched++;
|
|
8416
9367
|
}
|
|
8417
9368
|
}
|
|
8418
|
-
|
|
8419
|
-
|
|
9369
|
+
if (touched > 0) {
|
|
9370
|
+
writeMemoryEntries(projectId, entries);
|
|
9371
|
+
}
|
|
9372
|
+
return { ok: true, touched };
|
|
9373
|
+
});
|
|
9374
|
+
if (writable) {
|
|
9375
|
+
memoSocketServer.registry("memo/save", async (payload) => {
|
|
9376
|
+
try {
|
|
9377
|
+
const { content } = memoSaveRequestSchema.parse(payload);
|
|
9378
|
+
const file = memoryFileSchema.parse(JSON.parse(content));
|
|
9379
|
+
writeMemoryEntries(projectId, file.entries);
|
|
9380
|
+
return { ok: true, entries: file.entries.length };
|
|
9381
|
+
} catch (err) {
|
|
9382
|
+
console.warn("[memory] memo_save received invalid JSON:", err.message);
|
|
9383
|
+
throw new Error(`Invalid memory JSON: ${err.message}`);
|
|
9384
|
+
}
|
|
9385
|
+
});
|
|
9386
|
+
}
|
|
9387
|
+
const memoMcpServer = {
|
|
9388
|
+
name: "memo",
|
|
9389
|
+
command: process.execPath,
|
|
9390
|
+
args: [join21(process.scriptsPath, "mcp-memo/server.mjs"), "--socket-path", memoSocketPath],
|
|
9391
|
+
env: [{ name: "ELECTRON_RUN_AS_NODE", value: "1" }]
|
|
9392
|
+
};
|
|
9393
|
+
const result = await deps.inference.runInference({
|
|
9394
|
+
agentId: sessionContext.agentId,
|
|
9395
|
+
prompt,
|
|
9396
|
+
model: sessionContext.modelId ?? void 0,
|
|
9397
|
+
cwd: tempDir,
|
|
9398
|
+
mcpServers: [memoMcpServer],
|
|
9399
|
+
features: []
|
|
9400
|
+
});
|
|
9401
|
+
if (memoSocketServer) {
|
|
9402
|
+
memoSocketServer.stop();
|
|
9403
|
+
memoSocketServer = null;
|
|
9404
|
+
}
|
|
9405
|
+
if (existsSync8(tempDir)) {
|
|
9406
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
9407
|
+
}
|
|
9408
|
+
return result.text || "";
|
|
9409
|
+
} catch (err) {
|
|
9410
|
+
console.warn("[memory] Memo agent failed", err);
|
|
9411
|
+
if (memoSocketServer) {
|
|
9412
|
+
memoSocketServer.stop();
|
|
9413
|
+
memoSocketServer = null;
|
|
8420
9414
|
}
|
|
9415
|
+
if (existsSync8(tempDir)) {
|
|
9416
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
9417
|
+
}
|
|
9418
|
+
throw err;
|
|
8421
9419
|
}
|
|
8422
9420
|
}
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
9421
|
+
function registerMemoryRoute(server, projectId, sessionContext) {
|
|
9422
|
+
server.registry("memory/query", async (payload) => {
|
|
9423
|
+
const { query } = memoryQueryRequestSchema.parse(payload);
|
|
9424
|
+
const entries = readMemoryEntries(projectId);
|
|
9425
|
+
if (entries.length === 0) {
|
|
9426
|
+
return { content: "(no project memories stored yet)" };
|
|
9427
|
+
}
|
|
9428
|
+
try {
|
|
9429
|
+
const result = await runMemoAgent({
|
|
9430
|
+
projectId,
|
|
9431
|
+
prompt: buildMemoQueryPrompt(query),
|
|
9432
|
+
sessionContext,
|
|
9433
|
+
writable: false
|
|
9434
|
+
});
|
|
9435
|
+
return { content: result || "(no relevant memories found)" };
|
|
9436
|
+
} catch (err) {
|
|
9437
|
+
console.warn("[memory] Memo query inference failed", err);
|
|
9438
|
+
throw err;
|
|
9439
|
+
}
|
|
9440
|
+
});
|
|
9441
|
+
server.registry("memory/store", async (payload) => {
|
|
9442
|
+
const { facts } = memoryStoreRequestSchema.parse(payload);
|
|
9443
|
+
await writeQueue.enqueue(projectId, async () => {
|
|
9444
|
+
try {
|
|
9445
|
+
await runMemoAgent({
|
|
9446
|
+
projectId,
|
|
9447
|
+
prompt: buildMemoWritePrompt(facts),
|
|
9448
|
+
sessionContext,
|
|
9449
|
+
writable: true
|
|
9450
|
+
});
|
|
9451
|
+
} catch (err) {
|
|
9452
|
+
console.warn("[memory] Memo store inference failed", err);
|
|
9453
|
+
throw err;
|
|
9454
|
+
}
|
|
9455
|
+
});
|
|
9456
|
+
return { stored: facts.length, summary: `Stored ${facts.length} fact(s) to project memory.` };
|
|
9457
|
+
});
|
|
9458
|
+
}
|
|
9459
|
+
function buildMemoryMcpServer(options) {
|
|
9460
|
+
const projectId = getProjectIdFromCwd(options.projectDir);
|
|
9461
|
+
const summary = projectId ? getMemorySummary(projectId) : "";
|
|
9462
|
+
const args2 = [
|
|
9463
|
+
join21(process.scriptsPath, "mcp-memory/server.mjs"),
|
|
9464
|
+
"--project-dir",
|
|
9465
|
+
options.projectDir,
|
|
9466
|
+
"--socket-path",
|
|
9467
|
+
options.socketPath
|
|
9468
|
+
];
|
|
9469
|
+
if (summary) {
|
|
9470
|
+
const summaryFilename = join21(TEMP_DIR, `memory-summary-${randomUUID11()}.txt`);
|
|
9471
|
+
writeFileSync6(summaryFilename, summary, "utf-8");
|
|
9472
|
+
args2.push("--memory-summary", summaryFilename);
|
|
8430
9473
|
}
|
|
9474
|
+
return {
|
|
9475
|
+
name: "memory",
|
|
9476
|
+
command: process.execPath,
|
|
9477
|
+
args: args2,
|
|
9478
|
+
env: [{ name: "ELECTRON_RUN_AS_NODE", value: "1" }]
|
|
9479
|
+
};
|
|
8431
9480
|
}
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
}
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
9481
|
+
function getMemorySummary(projectId) {
|
|
9482
|
+
const entries = readMemoryEntries(projectId);
|
|
9483
|
+
if (entries.length === 0) return "";
|
|
9484
|
+
const top = entries.slice(0, SUMMARY_MAX_ENTRIES);
|
|
9485
|
+
return entriesToText(top);
|
|
9486
|
+
}
|
|
9487
|
+
function getEntries(projectId) {
|
|
9488
|
+
const entries = readMemoryEntries(projectId);
|
|
9489
|
+
if (entries.length === 0) {
|
|
9490
|
+
const memoryPath = getMemoryPath(projectId);
|
|
9491
|
+
if (!existsSync8(memoryPath)) return null;
|
|
9492
|
+
}
|
|
9493
|
+
return { version: MEMORY_FILE_VERSION, entries };
|
|
9494
|
+
}
|
|
9495
|
+
function clearMemory(projectId) {
|
|
9496
|
+
const memoryPath = getMemoryPath(projectId);
|
|
9497
|
+
if (existsSync8(memoryPath)) {
|
|
9498
|
+
rmSync4(memoryPath);
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
function getFilePath(projectId) {
|
|
9502
|
+
const memoryPath = getMemoryPath(projectId);
|
|
9503
|
+
return existsSync8(memoryPath) ? memoryPath : null;
|
|
8452
9504
|
}
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
9505
|
+
return {
|
|
9506
|
+
registerMemoryRoute,
|
|
9507
|
+
buildMemoryMcpServer,
|
|
9508
|
+
getMemorySummary,
|
|
9509
|
+
getEntries,
|
|
9510
|
+
clearMemory,
|
|
9511
|
+
getFilePath
|
|
9512
|
+
};
|
|
8456
9513
|
}
|
|
8457
9514
|
|
|
8458
9515
|
// src/backend/backend.ts
|
|
@@ -8475,18 +9532,20 @@ function initBackend(emitter) {
|
|
|
8475
9532
|
const skills = createSkillsModule(ctx);
|
|
8476
9533
|
const search2 = createSearchModule(ctx);
|
|
8477
9534
|
const watcher2 = createWatcherModule(ctx);
|
|
8478
|
-
const
|
|
9535
|
+
const bridgeConnect = createBridgeConnectModule(ctx, { askUser });
|
|
9536
|
+
const _inference = createInferenceModule(ctx, { skills, search: search2 });
|
|
9537
|
+
const memory = createMemoryModule(ctx, { inference: _inference });
|
|
8479
9538
|
const session = createSessionModule(ctx, {
|
|
8480
|
-
|
|
9539
|
+
bridgeConnect,
|
|
8481
9540
|
askUser,
|
|
8482
9541
|
shareToUser,
|
|
8483
9542
|
skills,
|
|
8484
9543
|
search: search2,
|
|
9544
|
+
memory,
|
|
8485
9545
|
ilink: ilink.state
|
|
8486
9546
|
});
|
|
8487
|
-
const project = createProjectModule(ctx, {
|
|
8488
|
-
const terminal = createTerminalModule(ctx, {
|
|
8489
|
-
const _inference = createInferenceModule(ctx, { skills, search: search2 });
|
|
9547
|
+
const project = createProjectModule(ctx, { session, watcher: watcher2 });
|
|
9548
|
+
const terminal = createTerminalModule(ctx, { bridges: bridgeConnect.bridges });
|
|
8490
9549
|
ilink.setHandlers({
|
|
8491
9550
|
sendPrompt: session.sendPrompt,
|
|
8492
9551
|
cancelPrompt: session.cancelPrompt,
|
|
@@ -8564,12 +9623,7 @@ function initBackend(emitter) {
|
|
|
8564
9623
|
deleteOrphanedAgentSessionDirectories(agentId, knownResumeIds);
|
|
8565
9624
|
}
|
|
8566
9625
|
}
|
|
8567
|
-
|
|
8568
|
-
if (p) {
|
|
8569
|
-
bridgePool.pool.delete(agentId);
|
|
8570
|
-
p.then((b) => b.kill()).catch(() => {
|
|
8571
|
-
});
|
|
8572
|
-
}
|
|
9626
|
+
await bridgeConnect.killBridgesByAgent(agentId);
|
|
8573
9627
|
}
|
|
8574
9628
|
}
|
|
8575
9629
|
}
|
|
@@ -8597,7 +9651,7 @@ function initBackend(emitter) {
|
|
|
8597
9651
|
const projectRoot = projectId ? storageOps.getProject(projectId)?.cwd : void 0;
|
|
8598
9652
|
const skillDir = skills.getSkillSystemPathFromId(skillId, { projectRoot });
|
|
8599
9653
|
if (!skillDir) throw new Error(`Failed to read skill: ${skillId}`);
|
|
8600
|
-
return
|
|
9654
|
+
return join22(skillDir, SKILL_FILENAME);
|
|
8601
9655
|
},
|
|
8602
9656
|
async uninstallSkill({ skillId, projectId }) {
|
|
8603
9657
|
const projectRoot = projectId ? storageOps.getProject(projectId)?.cwd : void 0;
|
|
@@ -8652,6 +9706,9 @@ function initBackend(emitter) {
|
|
|
8652
9706
|
async deleteSession(sessionId) {
|
|
8653
9707
|
return session.deleteSession(sessionId);
|
|
8654
9708
|
},
|
|
9709
|
+
async getSessionDataSystemPath({ sessionId }) {
|
|
9710
|
+
return session.getSessionDataSystemPath({ sessionId });
|
|
9711
|
+
},
|
|
8655
9712
|
async resetAgent({ agentId }) {
|
|
8656
9713
|
await session.resetAgentSessions(agentId);
|
|
8657
9714
|
const agentCfg = storageOps.getSettings().agents.find((a) => a.id === agentId);
|
|
@@ -8661,12 +9718,7 @@ function initBackend(emitter) {
|
|
|
8661
9718
|
);
|
|
8662
9719
|
deleteOrphanedAgentSessionDirectories(agentId, knownResumeIds);
|
|
8663
9720
|
}
|
|
8664
|
-
|
|
8665
|
-
if (p) {
|
|
8666
|
-
bridgePool.pool.delete(agentId);
|
|
8667
|
-
p.then((b) => b.kill()).catch(() => {
|
|
8668
|
-
});
|
|
8669
|
-
}
|
|
9721
|
+
await bridgeConnect.killBridgesByAgent(agentId);
|
|
8670
9722
|
},
|
|
8671
9723
|
async clearAgentSessions({ agentId }) {
|
|
8672
9724
|
const deletedSessionIds = await session.deleteAgentSessions(agentId);
|
|
@@ -8770,8 +9822,8 @@ function initBackend(emitter) {
|
|
|
8770
9822
|
async pollIlinkQrcode(params) {
|
|
8771
9823
|
return ilink.pollIlinkQrcode(params);
|
|
8772
9824
|
},
|
|
8773
|
-
async stopIlink() {
|
|
8774
|
-
return ilink.stopIlink();
|
|
9825
|
+
async stopIlink(params) {
|
|
9826
|
+
return ilink.stopIlink(params);
|
|
8775
9827
|
},
|
|
8776
9828
|
async setActiveIlinkSession(params) {
|
|
8777
9829
|
return ilink.setActiveIlinkSession(params);
|
|
@@ -8833,31 +9885,41 @@ function initBackend(emitter) {
|
|
|
8833
9885
|
}) {
|
|
8834
9886
|
const shareDir = storageOps.sessionShareDir(sessionId);
|
|
8835
9887
|
if (!shareDir) throw new Error("Session not found");
|
|
8836
|
-
return
|
|
9888
|
+
return join22(shareDir, sharePath);
|
|
8837
9889
|
},
|
|
8838
9890
|
async deleteTask({ scheduleId, taskId }) {
|
|
8839
9891
|
automation.deleteTask(scheduleId, taskId);
|
|
9892
|
+
},
|
|
9893
|
+
// Memory
|
|
9894
|
+
async getMemory({ projectId }) {
|
|
9895
|
+
return memory.getEntries(projectId);
|
|
9896
|
+
},
|
|
9897
|
+
async clearMemory({ projectId }) {
|
|
9898
|
+
memory.clearMemory(projectId);
|
|
9899
|
+
},
|
|
9900
|
+
async getMemorySystemFilePath({ projectId }) {
|
|
9901
|
+
return memory.getFilePath(projectId);
|
|
8840
9902
|
}
|
|
8841
9903
|
};
|
|
8842
9904
|
webui.setHandlers(backendHandlers2);
|
|
8843
9905
|
async function closeBackend2() {
|
|
8844
9906
|
automation.stopAllCrons();
|
|
8845
9907
|
webui.stop();
|
|
8846
|
-
await ilink.stopIlink();
|
|
9908
|
+
await ilink.stopIlink({ logout: false });
|
|
8847
9909
|
session.clearSession();
|
|
8848
|
-
await
|
|
9910
|
+
await bridgeConnect.clearAll();
|
|
8849
9911
|
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
8850
9912
|
terminal.killAllTerminals();
|
|
8851
9913
|
await watcher2.stopAll();
|
|
8852
9914
|
for (const file of await readdir2(TEMP_DIR)) {
|
|
8853
|
-
await rm3(
|
|
9915
|
+
await rm3(join22(TEMP_DIR, file), { recursive: true, force: true });
|
|
8854
9916
|
}
|
|
8855
9917
|
}
|
|
8856
9918
|
return { backendHandlers: backendHandlers2, closeBackend: closeBackend2 };
|
|
8857
9919
|
}
|
|
8858
9920
|
|
|
8859
9921
|
// package.json
|
|
8860
|
-
var version = "0.1.
|
|
9922
|
+
var version = "0.1.42";
|
|
8861
9923
|
|
|
8862
9924
|
// src/server/main.ts
|
|
8863
9925
|
var port;
|