@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.
Files changed (290) hide show
  1. package/out/renderer/assets/{_baseUniq-BonfEMiF.js → _baseUniq-CNYzw1DB.js} +1 -1
  2. package/out/renderer/assets/_baseUniq-CNYzw1DB.js.br +0 -0
  3. package/out/renderer/assets/_baseUniq-CNYzw1DB.js.gz +0 -0
  4. package/out/renderer/assets/{arc-Do8LbeMR.js → arc-vyANDg9_.js} +1 -1
  5. package/out/renderer/assets/arc-vyANDg9_.js.br +0 -0
  6. package/out/renderer/assets/arc-vyANDg9_.js.gz +0 -0
  7. package/out/renderer/assets/{architectureDiagram-Q4EWVU46-B8pyh3AH.js → architectureDiagram-Q4EWVU46-BzcUqnqp.js} +5 -5
  8. package/out/renderer/assets/architectureDiagram-Q4EWVU46-BzcUqnqp.js.br +0 -0
  9. package/out/renderer/assets/architectureDiagram-Q4EWVU46-BzcUqnqp.js.gz +0 -0
  10. package/out/renderer/assets/{blockDiagram-DXYQGD6D-DxehDaBQ.js → blockDiagram-DXYQGD6D-CTEZWa8S.js} +6 -6
  11. package/out/renderer/assets/blockDiagram-DXYQGD6D-CTEZWa8S.js.br +0 -0
  12. package/out/renderer/assets/blockDiagram-DXYQGD6D-CTEZWa8S.js.gz +0 -0
  13. package/out/renderer/assets/{c4Diagram-AHTNJAMY-BwVB2HtH.js → c4Diagram-AHTNJAMY-BuFQdjjc.js} +2 -2
  14. package/out/renderer/assets/c4Diagram-AHTNJAMY-BuFQdjjc.js.br +0 -0
  15. package/out/renderer/assets/{c4Diagram-AHTNJAMY-BwVB2HtH.js.gz → c4Diagram-AHTNJAMY-BuFQdjjc.js.gz} +0 -0
  16. package/out/renderer/assets/{channel-DoK5PQCo.js → channel-DyKDQZ32.js} +1 -1
  17. package/out/renderer/assets/{chunk-4BX2VUAB-B5UbKm2i.js → chunk-4BX2VUAB-BKj1YSOw.js} +1 -1
  18. package/out/renderer/assets/{chunk-4TB4RGXK-Mp3JyPop.js → chunk-4TB4RGXK-CUfY3XSR.js} +5 -5
  19. package/out/renderer/assets/chunk-4TB4RGXK-CUfY3XSR.js.br +0 -0
  20. package/out/renderer/assets/chunk-4TB4RGXK-CUfY3XSR.js.gz +0 -0
  21. package/out/renderer/assets/{chunk-55IACEB6-BpQS-lsN.js → chunk-55IACEB6-BPafcNL-.js} +1 -1
  22. package/out/renderer/assets/{chunk-EDXVE4YY-BQEQxqzJ.js → chunk-EDXVE4YY-DlWjafzT.js} +1 -1
  23. package/out/renderer/assets/chunk-EDXVE4YY-DlWjafzT.js.br +0 -0
  24. package/out/renderer/assets/chunk-EDXVE4YY-DlWjafzT.js.gz +0 -0
  25. package/out/renderer/assets/{chunk-FMBD7UC4-dCqprN1e.js → chunk-FMBD7UC4-CI2WhJKs.js} +1 -1
  26. package/out/renderer/assets/{chunk-OYMX7WX6-D19mZGkc.js → chunk-OYMX7WX6-1XAYgymx.js} +3 -3
  27. package/out/renderer/assets/chunk-OYMX7WX6-1XAYgymx.js.br +0 -0
  28. package/out/renderer/assets/chunk-OYMX7WX6-1XAYgymx.js.gz +0 -0
  29. package/out/renderer/assets/{chunk-QZHKN3VN-DzWTtEnO.js → chunk-QZHKN3VN-B2Kh1qhq.js} +1 -1
  30. package/out/renderer/assets/{chunk-YZCP3GAM-BEBfdXkA.js → chunk-YZCP3GAM-DKTOTV26.js} +1 -1
  31. package/out/renderer/assets/chunk-YZCP3GAM-DKTOTV26.js.br +0 -0
  32. package/out/renderer/assets/chunk-YZCP3GAM-DKTOTV26.js.gz +0 -0
  33. package/out/renderer/assets/{classDiagram-6PBFFD2Q-BosKSo4l.js → classDiagram-6PBFFD2Q-BSxgcC66.js} +6 -6
  34. package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-BosKSo4l.js → classDiagram-v2-HSJHXN6E-BSxgcC66.js} +6 -6
  35. package/out/renderer/assets/{clone-C281XDWZ.js → clone-B-NXg17n.js} +1 -1
  36. package/out/renderer/assets/{cose-bilkent-S5V4N54A-6SeriN8Z.js → cose-bilkent-S5V4N54A-Dr92qdgA.js} +1 -1
  37. package/out/renderer/assets/cose-bilkent-S5V4N54A-Dr92qdgA.js.br +0 -0
  38. package/out/renderer/assets/cose-bilkent-S5V4N54A-Dr92qdgA.js.gz +0 -0
  39. package/out/renderer/assets/{dagre-KV5264BT-gXq994RD.js → dagre-KV5264BT-7l16BI52.js} +6 -6
  40. package/out/renderer/assets/dagre-KV5264BT-7l16BI52.js.br +0 -0
  41. package/out/renderer/assets/dagre-KV5264BT-7l16BI52.js.gz +0 -0
  42. package/out/renderer/assets/{diagram-5BDNPKRD-GOebOMWt.js → diagram-5BDNPKRD-7J0KHPTw.js} +6 -6
  43. package/out/renderer/assets/diagram-5BDNPKRD-7J0KHPTw.js.br +0 -0
  44. package/out/renderer/assets/diagram-5BDNPKRD-7J0KHPTw.js.gz +0 -0
  45. package/out/renderer/assets/{diagram-G4DWMVQ6-BkQmAuCz.js → diagram-G4DWMVQ6-Dgk1T3Ng.js} +6 -6
  46. package/out/renderer/assets/diagram-G4DWMVQ6-Dgk1T3Ng.js.br +0 -0
  47. package/out/renderer/assets/diagram-G4DWMVQ6-Dgk1T3Ng.js.gz +0 -0
  48. package/out/renderer/assets/{diagram-MMDJMWI5-Q9B0Tkq3.js → diagram-MMDJMWI5-DerRkmYs.js} +5 -5
  49. package/out/renderer/assets/diagram-MMDJMWI5-DerRkmYs.js.br +0 -0
  50. package/out/renderer/assets/diagram-MMDJMWI5-DerRkmYs.js.gz +0 -0
  51. package/out/renderer/assets/{diagram-TYMM5635-5wNVFKxe.js → diagram-TYMM5635-DR9dcwKl.js} +5 -5
  52. package/out/renderer/assets/diagram-TYMM5635-DR9dcwKl.js.br +0 -0
  53. package/out/renderer/assets/diagram-TYMM5635-DR9dcwKl.js.gz +0 -0
  54. package/out/renderer/assets/docx_parser_bg-Dwb1lJCk.wasm +0 -0
  55. package/out/renderer/assets/{erDiagram-SMLLAGMA-DRxQnVgW.js → erDiagram-SMLLAGMA-CZ2vNtP6.js} +4 -4
  56. package/out/renderer/assets/erDiagram-SMLLAGMA-CZ2vNtP6.js.br +0 -0
  57. package/out/renderer/assets/erDiagram-SMLLAGMA-CZ2vNtP6.js.gz +0 -0
  58. package/out/renderer/assets/{flowDiagram-DWJPFMVM-3uIpRmli.js → flowDiagram-DWJPFMVM-DOL_mdve.js} +6 -6
  59. package/out/renderer/assets/flowDiagram-DWJPFMVM-DOL_mdve.js.br +0 -0
  60. package/out/renderer/assets/flowDiagram-DWJPFMVM-DOL_mdve.js.gz +0 -0
  61. package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-CFxauQFp.js → ganttDiagram-T4ZO3ILL-DXfCf0sW.js} +2 -2
  62. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-DXfCf0sW.js.br +0 -0
  63. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-DXfCf0sW.js.gz +0 -0
  64. package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-CDBCF75U.js → gitGraphDiagram-UUTBAWPF-Bi19RzUn.js} +6 -6
  65. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-Bi19RzUn.js.br +0 -0
  66. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-Bi19RzUn.js.gz +0 -0
  67. package/out/renderer/assets/{graph-B77t0Iut.js → graph-BX6gbV3Q.js} +2 -2
  68. package/out/renderer/assets/graph-BX6gbV3Q.js.br +0 -0
  69. package/out/renderer/assets/graph-BX6gbV3Q.js.gz +0 -0
  70. package/out/renderer/assets/{highlighted-body-OFNGDK62-BOhs9DFu.js → highlighted-body-OFNGDK62-CpZorxCd.js} +1 -1
  71. package/out/renderer/assets/index-BihXRHbW.css +6729 -0
  72. package/out/renderer/assets/index-BihXRHbW.css.br +0 -0
  73. package/out/renderer/assets/index-BihXRHbW.css.gz +0 -0
  74. package/out/renderer/assets/{index-l_WeGi7a.js → index-Ckb2j9e5.js} +49891 -24876
  75. package/out/renderer/assets/index-Ckb2j9e5.js.br +0 -0
  76. package/out/renderer/assets/index-Ckb2j9e5.js.gz +0 -0
  77. package/out/renderer/assets/{infoDiagram-42DDH7IO-C3TJqapp.js → infoDiagram-42DDH7IO-BB7FT1Ls.js} +4 -4
  78. package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-C59I-VqG.js → ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js} +1 -1
  79. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js.br +0 -0
  80. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-LTRPX_Lf.js.gz +0 -0
  81. package/out/renderer/assets/{journeyDiagram-VCZTEJTY-B6jawzfM.js → journeyDiagram-VCZTEJTY-bhcMvreZ.js} +4 -4
  82. package/out/renderer/assets/journeyDiagram-VCZTEJTY-bhcMvreZ.js.br +0 -0
  83. package/out/renderer/assets/journeyDiagram-VCZTEJTY-bhcMvreZ.js.gz +0 -0
  84. package/out/renderer/assets/{kanban-definition-6JOO6SKY-BCXhVxbs.js → kanban-definition-6JOO6SKY-CRg0O3h_.js} +2 -2
  85. package/out/renderer/assets/kanban-definition-6JOO6SKY-CRg0O3h_.js.br +0 -0
  86. package/out/renderer/assets/kanban-definition-6JOO6SKY-CRg0O3h_.js.gz +0 -0
  87. package/out/renderer/assets/{layout-zx9noErc.js → layout-Gz3MMSfR.js} +4 -4
  88. package/out/renderer/assets/layout-Gz3MMSfR.js.br +0 -0
  89. package/out/renderer/assets/layout-Gz3MMSfR.js.gz +0 -0
  90. package/out/renderer/assets/{linear-Bo25ELwI.js → linear-B-XoCpT2.js} +1 -1
  91. package/out/renderer/assets/linear-B-XoCpT2.js.br +0 -0
  92. package/out/renderer/assets/linear-B-XoCpT2.js.gz +0 -0
  93. package/out/renderer/assets/{min-eGRLXD6u.js → min-pbiT8SH0.js} +2 -2
  94. package/out/renderer/assets/min-pbiT8SH0.js.br +0 -0
  95. package/out/renderer/assets/min-pbiT8SH0.js.gz +0 -0
  96. package/out/renderer/assets/{mindmap-definition-QFDTVHPH-PUpW_gKO.js → mindmap-definition-QFDTVHPH-QEcTW8Sk.js} +3 -3
  97. package/out/renderer/assets/mindmap-definition-QFDTVHPH-QEcTW8Sk.js.br +0 -0
  98. package/out/renderer/assets/mindmap-definition-QFDTVHPH-QEcTW8Sk.js.gz +0 -0
  99. package/out/renderer/assets/{pdf-worker-wrapper-Do-ikGHl.js → pdf-worker-wrapper-D9W0fDhT.js} +6514 -5934
  100. package/out/renderer/assets/pdf-worker-wrapper-D9W0fDhT.js.br +0 -0
  101. package/out/renderer/assets/pdf-worker-wrapper-D9W0fDhT.js.gz +0 -0
  102. package/out/renderer/assets/{pieDiagram-DEJITSTG-BrSaX9YD.js → pieDiagram-DEJITSTG-BdyIMG3r.js} +6 -6
  103. package/out/renderer/assets/pieDiagram-DEJITSTG-BdyIMG3r.js.br +0 -0
  104. package/out/renderer/assets/pieDiagram-DEJITSTG-BdyIMG3r.js.gz +0 -0
  105. package/out/renderer/assets/pierre-dark-BL-l9r39.js +4 -0
  106. package/out/renderer/assets/pierre-dark-BL-l9r39.js.br +0 -0
  107. package/out/renderer/assets/pierre-dark-BL-l9r39.js.gz +0 -0
  108. package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js +4 -0
  109. package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js.br +0 -0
  110. package/out/renderer/assets/pierre-dark-protanopia-deuteranopia-BBjGuJQU.js.gz +0 -0
  111. package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js +4 -0
  112. package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js.br +0 -0
  113. package/out/renderer/assets/pierre-dark-soft-B6TBhICr.js.gz +0 -0
  114. package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js +4 -0
  115. package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js.br +0 -0
  116. package/out/renderer/assets/pierre-dark-tritanopia-f-j_EX9A.js.gz +0 -0
  117. package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js +4 -0
  118. package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js.br +0 -0
  119. package/out/renderer/assets/pierre-dark-vibrant-yElMGqZN.js.gz +0 -0
  120. package/out/renderer/assets/pierre-light-BUzWTcjO.js +4 -0
  121. package/out/renderer/assets/pierre-light-BUzWTcjO.js.br +0 -0
  122. package/out/renderer/assets/pierre-light-BUzWTcjO.js.gz +0 -0
  123. package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js +4 -0
  124. package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js.br +0 -0
  125. package/out/renderer/assets/pierre-light-protanopia-deuteranopia-DlvVSCaI.js.gz +0 -0
  126. package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js +4 -0
  127. package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js.br +0 -0
  128. package/out/renderer/assets/pierre-light-soft-CCMU-Zh0.js.gz +0 -0
  129. package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js +4 -0
  130. package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js.br +0 -0
  131. package/out/renderer/assets/pierre-light-tritanopia-v4xacE1C.js.gz +0 -0
  132. package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js +4 -0
  133. package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js.br +0 -0
  134. package/out/renderer/assets/pierre-light-vibrant-BRUbQnMc.js.gz +0 -0
  135. package/out/renderer/assets/pptx_parser_bg-N_v237u9.wasm +0 -0
  136. package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-B_oUGzKN.js → quadrantDiagram-34T5L4WZ-CO63F50c.js} +2 -2
  137. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-CO63F50c.js.br +0 -0
  138. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-CO63F50c.js.gz +0 -0
  139. package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js +25 -0
  140. package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js.br +0 -0
  141. package/out/renderer/assets/render-worker-host-B5esxBj--BJuDnELc.js.gz +0 -0
  142. package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js +25 -0
  143. package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js.br +0 -0
  144. package/out/renderer/assets/render-worker-host-BzhGwe0I-Cw8cuLG0.js.gz +0 -0
  145. package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js +25 -0
  146. package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js.br +0 -0
  147. package/out/renderer/assets/render-worker-host-Cg60RAG_-C8N4a8W-.js.gz +0 -0
  148. package/out/renderer/assets/{requirementDiagram-MS252O5E-LQY3T05J.js → requirementDiagram-MS252O5E-Ch2CVgfZ.js} +3 -3
  149. package/out/renderer/assets/requirementDiagram-MS252O5E-Ch2CVgfZ.js.br +0 -0
  150. package/out/renderer/assets/requirementDiagram-MS252O5E-Ch2CVgfZ.js.gz +0 -0
  151. package/out/renderer/assets/{sankeyDiagram-XADWPNL6-BZrdYvkl.js → sankeyDiagram-XADWPNL6--b1mWkl5.js} +1 -1
  152. package/out/renderer/assets/sankeyDiagram-XADWPNL6--b1mWkl5.js.br +0 -0
  153. package/out/renderer/assets/sankeyDiagram-XADWPNL6--b1mWkl5.js.gz +0 -0
  154. package/out/renderer/assets/{sequenceDiagram-FGHM5R23-DT03DGl3.js → sequenceDiagram-FGHM5R23-CNautoFU.js} +3 -3
  155. package/out/renderer/assets/sequenceDiagram-FGHM5R23-CNautoFU.js.br +0 -0
  156. package/out/renderer/assets/sequenceDiagram-FGHM5R23-CNautoFU.js.gz +0 -0
  157. package/out/renderer/assets/{stateDiagram-FHFEXIEX-XxvRQ2fw.js → stateDiagram-FHFEXIEX-CKsv5tYk.js} +8 -8
  158. package/out/renderer/assets/stateDiagram-FHFEXIEX-CKsv5tYk.js.br +0 -0
  159. package/out/renderer/assets/stateDiagram-FHFEXIEX-CKsv5tYk.js.gz +0 -0
  160. package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-C8lZQVxS.js → stateDiagram-v2-QKLJ7IA2-CkdzkP8w.js} +4 -4
  161. package/out/renderer/assets/{timeline-definition-GMOUNBTQ-BThN9RD2.js → timeline-definition-GMOUNBTQ-CF_zqktL.js} +2 -2
  162. package/out/renderer/assets/timeline-definition-GMOUNBTQ-CF_zqktL.js.br +0 -0
  163. package/out/renderer/assets/timeline-definition-GMOUNBTQ-CF_zqktL.js.gz +0 -0
  164. package/out/renderer/assets/{vennDiagram-DHZGUBPP-DvL9CApP.js → vennDiagram-DHZGUBPP-C7q9SHIx.js} +1 -1
  165. package/out/renderer/assets/vennDiagram-DHZGUBPP-C7q9SHIx.js.br +0 -0
  166. package/out/renderer/assets/vennDiagram-DHZGUBPP-C7q9SHIx.js.gz +0 -0
  167. package/out/renderer/assets/{wardley-RL74JXVD-B4vPxA-A.js → wardley-RL74JXVD-DmW7d79y.js} +3 -3
  168. package/out/renderer/assets/wardley-RL74JXVD-DmW7d79y.js.br +0 -0
  169. package/out/renderer/assets/wardley-RL74JXVD-DmW7d79y.js.gz +0 -0
  170. package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-A3F8zuNA.js → wardleyDiagram-NUSXRM2D-1VzGmAot.js} +5 -5
  171. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-1VzGmAot.js.br +0 -0
  172. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-1VzGmAot.js.gz +0 -0
  173. package/out/renderer/assets/xlsx_parser_bg-CQ5Lc7LS.wasm +0 -0
  174. package/out/renderer/assets/{xychartDiagram-5P7HB3ND-BxjT7l5Q.js → xychartDiagram-5P7HB3ND-DznKXnsr.js} +2 -2
  175. package/out/renderer/assets/xychartDiagram-5P7HB3ND-DznKXnsr.js.br +0 -0
  176. package/out/renderer/assets/xychartDiagram-5P7HB3ND-DznKXnsr.js.gz +0 -0
  177. package/out/renderer/index.html +2 -2
  178. package/out/renderer/index.html.br +0 -0
  179. package/out/renderer/index.html.gz +0 -0
  180. package/out/scripts/electron-preload/preload.mjs +21 -0
  181. package/out/scripts/mcp-memo/server.mjs +173 -0
  182. package/out/scripts/mcp-memory/server.mjs +167 -0
  183. package/out/scripts/mcp-search/server.mjs +1 -1
  184. package/out/scripts/worker-file-outline/worker.mjs +237 -28
  185. package/out/server/main.js +2208 -1146
  186. package/package.json +14 -13
  187. package/resources/tree-sitter-wasm/tree-sitter-dart.wasm +0 -0
  188. package/out/renderer/assets/_baseUniq-BonfEMiF.js.br +0 -0
  189. package/out/renderer/assets/_baseUniq-BonfEMiF.js.gz +0 -0
  190. package/out/renderer/assets/arc-Do8LbeMR.js.br +0 -0
  191. package/out/renderer/assets/arc-Do8LbeMR.js.gz +0 -0
  192. package/out/renderer/assets/architectureDiagram-Q4EWVU46-B8pyh3AH.js.br +0 -0
  193. package/out/renderer/assets/architectureDiagram-Q4EWVU46-B8pyh3AH.js.gz +0 -0
  194. package/out/renderer/assets/blockDiagram-DXYQGD6D-DxehDaBQ.js.br +0 -0
  195. package/out/renderer/assets/blockDiagram-DXYQGD6D-DxehDaBQ.js.gz +0 -0
  196. package/out/renderer/assets/c4Diagram-AHTNJAMY-BwVB2HtH.js.br +0 -0
  197. package/out/renderer/assets/chunk-4TB4RGXK-Mp3JyPop.js.br +0 -0
  198. package/out/renderer/assets/chunk-4TB4RGXK-Mp3JyPop.js.gz +0 -0
  199. package/out/renderer/assets/chunk-EDXVE4YY-BQEQxqzJ.js.br +0 -0
  200. package/out/renderer/assets/chunk-EDXVE4YY-BQEQxqzJ.js.gz +0 -0
  201. package/out/renderer/assets/chunk-OYMX7WX6-D19mZGkc.js.br +0 -0
  202. package/out/renderer/assets/chunk-OYMX7WX6-D19mZGkc.js.gz +0 -0
  203. package/out/renderer/assets/chunk-YZCP3GAM-BEBfdXkA.js.br +0 -0
  204. package/out/renderer/assets/chunk-YZCP3GAM-BEBfdXkA.js.gz +0 -0
  205. package/out/renderer/assets/cose-bilkent-S5V4N54A-6SeriN8Z.js.br +0 -0
  206. package/out/renderer/assets/cose-bilkent-S5V4N54A-6SeriN8Z.js.gz +0 -0
  207. package/out/renderer/assets/dagre-KV5264BT-gXq994RD.js.br +0 -0
  208. package/out/renderer/assets/dagre-KV5264BT-gXq994RD.js.gz +0 -0
  209. package/out/renderer/assets/diagram-5BDNPKRD-GOebOMWt.js.br +0 -0
  210. package/out/renderer/assets/diagram-5BDNPKRD-GOebOMWt.js.gz +0 -0
  211. package/out/renderer/assets/diagram-G4DWMVQ6-BkQmAuCz.js.br +0 -0
  212. package/out/renderer/assets/diagram-G4DWMVQ6-BkQmAuCz.js.gz +0 -0
  213. package/out/renderer/assets/diagram-MMDJMWI5-Q9B0Tkq3.js.br +0 -0
  214. package/out/renderer/assets/diagram-MMDJMWI5-Q9B0Tkq3.js.gz +0 -0
  215. package/out/renderer/assets/diagram-TYMM5635-5wNVFKxe.js.br +0 -0
  216. package/out/renderer/assets/diagram-TYMM5635-5wNVFKxe.js.gz +0 -0
  217. package/out/renderer/assets/erDiagram-SMLLAGMA-DRxQnVgW.js.br +0 -0
  218. package/out/renderer/assets/erDiagram-SMLLAGMA-DRxQnVgW.js.gz +0 -0
  219. package/out/renderer/assets/flowDiagram-DWJPFMVM-3uIpRmli.js.br +0 -0
  220. package/out/renderer/assets/flowDiagram-DWJPFMVM-3uIpRmli.js.gz +0 -0
  221. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-CFxauQFp.js.br +0 -0
  222. package/out/renderer/assets/ganttDiagram-T4ZO3ILL-CFxauQFp.js.gz +0 -0
  223. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CDBCF75U.js.br +0 -0
  224. package/out/renderer/assets/gitGraphDiagram-UUTBAWPF-CDBCF75U.js.gz +0 -0
  225. package/out/renderer/assets/graph-B77t0Iut.js.br +0 -0
  226. package/out/renderer/assets/graph-B77t0Iut.js.gz +0 -0
  227. package/out/renderer/assets/index-CTSkyk5N.css +0 -7619
  228. package/out/renderer/assets/index-CTSkyk5N.css.br +0 -0
  229. package/out/renderer/assets/index-CTSkyk5N.css.gz +0 -0
  230. package/out/renderer/assets/index-l_WeGi7a.js.br +0 -0
  231. package/out/renderer/assets/index-l_WeGi7a.js.gz +0 -0
  232. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-C59I-VqG.js.br +0 -0
  233. package/out/renderer/assets/ishikawaDiagram-UXIWVN3A-C59I-VqG.js.gz +0 -0
  234. package/out/renderer/assets/journeyDiagram-VCZTEJTY-B6jawzfM.js.br +0 -0
  235. package/out/renderer/assets/journeyDiagram-VCZTEJTY-B6jawzfM.js.gz +0 -0
  236. package/out/renderer/assets/kanban-definition-6JOO6SKY-BCXhVxbs.js.br +0 -0
  237. package/out/renderer/assets/kanban-definition-6JOO6SKY-BCXhVxbs.js.gz +0 -0
  238. package/out/renderer/assets/layout-zx9noErc.js.br +0 -0
  239. package/out/renderer/assets/layout-zx9noErc.js.gz +0 -0
  240. package/out/renderer/assets/linear-Bo25ELwI.js.br +0 -0
  241. package/out/renderer/assets/linear-Bo25ELwI.js.gz +0 -0
  242. package/out/renderer/assets/min-eGRLXD6u.js.br +0 -0
  243. package/out/renderer/assets/min-eGRLXD6u.js.gz +0 -0
  244. package/out/renderer/assets/mindmap-definition-QFDTVHPH-PUpW_gKO.js.br +0 -0
  245. package/out/renderer/assets/mindmap-definition-QFDTVHPH-PUpW_gKO.js.gz +0 -0
  246. package/out/renderer/assets/pdf-worker-wrapper-Do-ikGHl.js.br +0 -0
  247. package/out/renderer/assets/pdf-worker-wrapper-Do-ikGHl.js.gz +0 -0
  248. package/out/renderer/assets/pieDiagram-DEJITSTG-BrSaX9YD.js.br +0 -0
  249. package/out/renderer/assets/pieDiagram-DEJITSTG-BrSaX9YD.js.gz +0 -0
  250. package/out/renderer/assets/pierre-dark-C-Drv5KU.js +0 -4
  251. package/out/renderer/assets/pierre-dark-C-Drv5KU.js.br +0 -0
  252. package/out/renderer/assets/pierre-dark-C-Drv5KU.js.gz +0 -0
  253. package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js +0 -4
  254. package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js.br +0 -0
  255. package/out/renderer/assets/pierre-dark-soft-Cd74uQaL.js.gz +0 -0
  256. package/out/renderer/assets/pierre-light-CfQnmt6J.js +0 -4
  257. package/out/renderer/assets/pierre-light-CfQnmt6J.js.br +0 -0
  258. package/out/renderer/assets/pierre-light-CfQnmt6J.js.gz +0 -0
  259. package/out/renderer/assets/pierre-light-soft-DMVdU74b.js +0 -4
  260. package/out/renderer/assets/pierre-light-soft-DMVdU74b.js.br +0 -0
  261. package/out/renderer/assets/pierre-light-soft-DMVdU74b.js.gz +0 -0
  262. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-B_oUGzKN.js.br +0 -0
  263. package/out/renderer/assets/quadrantDiagram-34T5L4WZ-B_oUGzKN.js.gz +0 -0
  264. package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js +0 -25
  265. package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js.br +0 -0
  266. package/out/renderer/assets/render-worker-host-4IbvpiUo-Dc5a8nQ-.js.gz +0 -0
  267. package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js +0 -25
  268. package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js.br +0 -0
  269. package/out/renderer/assets/render-worker-host-BdfGz_ou-AC9asZT1.js.gz +0 -0
  270. package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js +0 -25
  271. package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js.br +0 -0
  272. package/out/renderer/assets/render-worker-host-awn_IPjt-Bowndb19.js.gz +0 -0
  273. package/out/renderer/assets/requirementDiagram-MS252O5E-LQY3T05J.js.br +0 -0
  274. package/out/renderer/assets/requirementDiagram-MS252O5E-LQY3T05J.js.gz +0 -0
  275. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BZrdYvkl.js.br +0 -0
  276. package/out/renderer/assets/sankeyDiagram-XADWPNL6-BZrdYvkl.js.gz +0 -0
  277. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DT03DGl3.js.br +0 -0
  278. package/out/renderer/assets/sequenceDiagram-FGHM5R23-DT03DGl3.js.gz +0 -0
  279. package/out/renderer/assets/stateDiagram-FHFEXIEX-XxvRQ2fw.js.br +0 -0
  280. package/out/renderer/assets/stateDiagram-FHFEXIEX-XxvRQ2fw.js.gz +0 -0
  281. package/out/renderer/assets/timeline-definition-GMOUNBTQ-BThN9RD2.js.br +0 -0
  282. package/out/renderer/assets/timeline-definition-GMOUNBTQ-BThN9RD2.js.gz +0 -0
  283. package/out/renderer/assets/vennDiagram-DHZGUBPP-DvL9CApP.js.br +0 -0
  284. package/out/renderer/assets/vennDiagram-DHZGUBPP-DvL9CApP.js.gz +0 -0
  285. package/out/renderer/assets/wardley-RL74JXVD-B4vPxA-A.js.br +0 -0
  286. package/out/renderer/assets/wardley-RL74JXVD-B4vPxA-A.js.gz +0 -0
  287. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-A3F8zuNA.js.br +0 -0
  288. package/out/renderer/assets/wardleyDiagram-NUSXRM2D-A3F8zuNA.js.gz +0 -0
  289. package/out/renderer/assets/xychartDiagram-5P7HB3ND-BxjT7l5Q.js.br +0 -0
  290. package/out/renderer/assets/xychartDiagram-5P7HB3ND-BxjT7l5Q.js.gz +0 -0
@@ -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 join18 } from "path";
14
+ import { join as join22 } from "path";
15
15
 
16
- // src/backend/storage.ts
17
- import { basename, join as join2 } from "path";
18
- import { homedir } from "os";
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/shared/constants.ts
31
- var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
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
- var WORKSPACES_DIR = join2(FELLO_DIR, "workspaces");
51
- var WORKSPACE_TEMP_DIR = join2(WORKSPACES_DIR, "__temp__");
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 join2(FELLO_DIR, "settings.json");
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 hashCwd(cwd) {
226
- return createHash("sha1").update(cwd.trim()).digest("hex");
227
- }
228
- function projectDir(projectId) {
229
- return join2(PROJECTS_DIR, projectId);
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 projectMetaPath(projectId) {
232
- return join2(projectDir(projectId), "project.json");
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
- function projectSessionsDir(projectId) {
235
- return join2(projectDir(projectId), "sessions");
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 sessionDir(projectId, sessionId) {
241
- const safe = safeSessionDirName(sessionId);
242
- const safeDir = join2(projectSessionsDir(projectId), safe);
243
- if (safe !== sessionId && !existsSync(safeDir)) {
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
- var projectMetaCache = /* @__PURE__ */ new Map();
253
- var sessionMetaCache = /* @__PURE__ */ new Map();
254
- function readProjectMeta(projectId) {
255
- const cached = projectMetaCache.get(projectId);
256
- if (cached) return cached;
257
- try {
258
- const raw = JSON.parse(readFileSync(projectMetaPath(projectId), "utf-8"));
259
- if (!raw) return null;
260
- const id = String(raw.id || "");
261
- const title = String(raw.title || "");
262
- const cwd = String(raw.cwd || "");
263
- const created_at = typeof raw.created_at === "number" ? raw.created_at : Date.now();
264
- if (!id || !title || !cwd) return null;
265
- const meta = { id, title, cwd, created_at };
266
- projectMetaCache.set(id, meta);
267
- return meta;
268
- } catch {
269
- return null;
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 writeProjectMeta(meta) {
273
- mkdirSync(projectDir(meta.id), { recursive: true });
274
- mkdirSync(projectSessionsDir(meta.id), { recursive: true });
275
- writeFileSync(projectMetaPath(meta.id), JSON.stringify(meta, null, 2));
276
- projectMetaCache.set(meta.id, meta);
277
- }
278
- function deleteProjectMeta(projectId) {
279
- const dir = projectDir(projectId);
280
- if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
281
- projectMetaCache.delete(projectId);
282
- for (const [id, meta] of sessionMetaCache.entries()) {
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
- sessionMetaCache.delete(id);
590
+ sessionMetaMemo.delete(id);
285
591
  }
286
592
  }
287
593
  }
288
- function getProjectMeta(projectId) {
289
- return projectMetaCache.get(projectId) ?? null;
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 listProjectMetas() {
292
- const projects = Array.from(projectMetaCache.values());
293
- projects.sort((a, b) => b.created_at - a.created_at);
294
- return projects;
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 readSessionMeta(projectId, sessionId) {
297
- const cached = sessionMetaCache.get(sessionId);
298
- if (cached) return cached;
299
- try {
300
- const raw = JSON.parse(
301
- readFileSync(sessionMetaPath(projectId, sessionId), "utf-8")
302
- );
303
- if (!raw) return null;
304
- const id = String(raw.id);
305
- const title = String(raw.title || "");
306
- const agent_id = String(raw.agent_id);
307
- const resume_id = String(raw.resume_id);
308
- const project_id = String(raw.project_id);
309
- const created_at = typeof raw.created_at === "number" ? raw.created_at : Date.now();
310
- const updated_at = typeof raw.updated_at === "number" ? raw.updated_at : created_at;
311
- const mcp_servers = Array.isArray(raw.mcp_servers) ? raw.mcp_servers.filter((v) => typeof v === "string") : void 0;
312
- const permission_mode = raw.permission_mode === "allow-all" || raw.permission_mode === "ask" ? raw.permission_mode : "ask";
313
- const features = Array.isArray(raw.features) ? raw.features.filter((v) => ALL_FEATURES.includes(v)) : ALL_FEATURES;
314
- const models = raw.models ?? null;
315
- const modes = raw.modes ?? null;
316
- const initialize_info = raw.initialize_info ?? null;
317
- if (!id || !agent_id || !resume_id || !project_id) return null;
318
- const meta = {
319
- id,
320
- title,
321
- agent_id,
322
- resume_id,
323
- project_id,
324
- created_at,
325
- updated_at,
326
- mcp_servers,
327
- features,
328
- permission_mode,
329
- models,
330
- modes,
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 writeSessionMeta(meta) {
340
- mkdirSync(sessionDir(meta.project_id, meta.id), { recursive: true });
341
- writeFileSync(sessionMetaPath(meta.project_id, meta.id), JSON.stringify(meta, null, 2));
342
- sessionMetaCache.set(meta.id, meta);
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 deleteSessionMeta(sessionId) {
345
- const sessionMeta = sessionMetaCache.get(sessionId);
346
- if (!sessionMeta) return;
347
- const dir = sessionDir(sessionMeta.project_id, sessionId);
348
- if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
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 getSessionMeta(sessionId) {
352
- return sessionMetaCache.get(sessionId) ?? null;
721
+ function sessionDir(sessionId) {
722
+ const meta = sessionMetaMemo.get(sessionId);
723
+ if (!meta) return null;
724
+ return dirname2(meta.filename);
353
725
  }
354
- function listSessionMetas() {
355
- const sessions = Array.from(sessionMetaCache.values());
356
- sessions.sort((a, b) => b.updated_at - a.updated_at);
357
- return sessions;
726
+ function sessionTerminalDir(sessionId) {
727
+ const dir = sessionDir(sessionId);
728
+ if (!dir) return null;
729
+ return join4(dir, "terminals");
358
730
  }
359
- function initMetaCache() {
360
- if (!existsSync(PROJECTS_DIR)) return;
361
- const dirs = readdirSync(PROJECTS_DIR);
362
- for (const dir of dirs) {
363
- readProjectMeta(dir);
364
- }
365
- const projects = listProjectMetas();
366
- for (const project of projects) {
367
- const sessionsPath = projectSessionsDir(project.id);
368
- if (!existsSync(sessionsPath)) continue;
369
- const dirs2 = readdirSync(sessionsPath);
370
- for (const dir of dirs2) {
371
- const meta = readSessionMeta(project.id, dir);
372
- if (meta) {
373
- sessionMetaCache.set(meta.id, meta);
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
- initMetaCache();
379
- var storageOps = {
380
- getSettings() {
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 mkdirSync2, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
963
- import { join as join3 } from "path";
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 join3(
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 join3(apiAgentSessionDir(agentId, sessionId), "session.json");
982
+ return join6(apiAgentSessionDir(agentId, sessionId), "session.json");
980
983
  }
981
984
  function historyJsonlPath(agentId, sessionId) {
982
- return join3(apiAgentSessionDir(agentId, sessionId), "history.jsonl");
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
- mkdirSync2(apiAgentSessionDir(agentId, sessionId), { recursive: true });
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
- writeFileSync2(
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
- writeFileSync2(
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 = join3(FELLO_DIR, "api-agents", toSafePathSegment(agentId));
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 = join3(FELLO_DIR, "api-agents", toSafePathSegment(agentId), "sessions");
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 = join3(sessionsDir, entry);
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 join5 } from "path";
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 join4, dirname as dirname2 } from "path";
1167
+ import { join as join7, dirname as dirname3 } from "path";
1165
1168
  import { homedir as homedir2 } from "os";
1166
1169
  import {
1167
- mkdirSync as mkdirSync3,
1168
- writeFileSync as writeFileSync3,
1169
- readFileSync as readFileSync2,
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 existsSync2,
1175
+ existsSync as existsSync4,
1173
1176
  statSync as statSync2
1174
1177
  } from "fs";
1175
- var AUTOMATIONS_DIR = join4(homedir2(), ".fello", "automations");
1176
- mkdirSync3(AUTOMATIONS_DIR, { recursive: true });
1178
+ var AUTOMATIONS_DIR = join7(homedir2(), ".fello", "automations");
1179
+ mkdirSync5(AUTOMATIONS_DIR, { recursive: true });
1177
1180
  var store = {
1178
1181
  scheduleDir(scheduleId) {
1179
- return join4(AUTOMATIONS_DIR, scheduleId);
1182
+ return join7(AUTOMATIONS_DIR, scheduleId);
1180
1183
  },
1181
1184
  scheduleConfigPath(scheduleId) {
1182
- return join4(this.scheduleDir(scheduleId), "schedule.json");
1185
+ return join7(this.scheduleDir(scheduleId), "schedule.json");
1183
1186
  },
1184
1187
  tasksDir(scheduleId) {
1185
- return join4(this.scheduleDir(scheduleId), "tasks");
1188
+ return join7(this.scheduleDir(scheduleId), "tasks");
1186
1189
  },
1187
1190
  taskDir(scheduleId, taskId) {
1188
- return join4(this.tasksDir(scheduleId), taskId);
1191
+ return join7(this.tasksDir(scheduleId), taskId);
1189
1192
  },
1190
1193
  taskMetaPath(scheduleId, taskId) {
1191
- return join4(this.taskDir(scheduleId, taskId), "task.json");
1194
+ return join7(this.taskDir(scheduleId, taskId), "task.json");
1192
1195
  },
1193
1196
  listSchedules() {
1194
- if (!existsSync2(AUTOMATIONS_DIR)) return [];
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(readFileSync2(this.scheduleConfigPath(dir), "utf-8"));
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(readFileSync2(this.scheduleConfigPath(scheduleId), "utf-8"));
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
- mkdirSync3(dir, { recursive: true });
1217
- mkdirSync3(this.tasksDir(schedule.id), { recursive: true });
1218
- writeFileSync3(this.scheduleConfigPath(schedule.id), JSON.stringify(schedule, null, 2));
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 (existsSync2(dir)) {
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 (!existsSync2(dir)) return [];
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(readFileSync2(this.taskMetaPath(scheduleId, d), "utf-8"));
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(readFileSync2(this.taskMetaPath(scheduleId, taskId), "utf-8"));
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
- mkdirSync3(dir, { recursive: true });
1269
- writeFileSync3(this.taskMetaPath(scheduleId, task.id), JSON.stringify(task, null, 2));
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 (existsSync2(dir)) {
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 (!existsSync2(dir)) return [];
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 = join4(dir, f);
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 = join4(dir, entry);
1297
+ const full = join7(dir, entry);
1295
1298
  const s = statSync2(full);
1296
1299
  if (s.isDirectory()) {
1297
- result.push(...this._listDirRecursive(full, join4(prefix, entry)));
1300
+ result.push(...this._listDirRecursive(full, join7(prefix, entry)));
1298
1301
  } else {
1299
- result.push(join4(prefix, entry));
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 = join4(base, filePath);
1309
+ const fullPath = join7(base, filePath);
1307
1310
  if (!fullPath.startsWith(base + "/") && fullPath !== base) throw new Error("Invalid file path");
1308
- if (!existsSync2(fullPath)) throw new Error("File not found");
1309
- if (encoding === "base64") return readFileSync2(fullPath).toString("base64");
1310
- return readFileSync2(fullPath, "utf-8");
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 = join4(base, filePath);
1317
+ const fullPath = join7(base, filePath);
1315
1318
  if (!fullPath.startsWith(base + "/") && fullPath !== base) throw new Error("Invalid file path");
1316
- mkdirSync3(dirname2(fullPath), { recursive: true });
1317
- writeFileSync3(fullPath, content);
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 client of connectedClients) {
1517
- if (client.readyState === 1) {
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 client of connectedClients) {
1525
- if (client.readyState === 1) {
1526
- client.send(message);
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 = join5(baseDir, reqPath);
1590
+ let filePath = join8(baseDir, reqPath);
1588
1591
  let s = await stat2(filePath).catch(() => null);
1589
1592
  if (!s || s.isDirectory()) {
1590
- filePath = join5(baseDir, "index.html");
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 client of connectedClients) {
1687
- client.close();
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 join9 } from "path";
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 join7, isAbsolute as isAbsolute4, resolve as resolve3, relative as relative5 } from "path";
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: join7(process.treeSitterWasmPath, "tree-sitter-javascript.wasm"),
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", "variable_declaration"],
2130
- label: "const",
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: join7(process.treeSitterWasmPath, "tree-sitter-typescript.wasm"),
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", "variable_declaration"],
2168
- label: "const",
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
- { node: "ambient_declaration", prefix: "declare ", maxDepth: 0 }
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: join7(process.treeSitterWasmPath, "tree-sitter-tsx.wasm"),
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", "variable_declaration"],
2208
- label: "const",
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: join7(process.treeSitterWasmPath, "tree-sitter-python.wasm"),
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: join7(process.treeSitterWasmPath, "tree-sitter-go.wasm"),
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: join7(process.treeSitterWasmPath, "tree-sitter-c.wasm"),
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: join7(process.treeSitterWasmPath, "tree-sitter-cpp.wasm"),
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: join7(process.treeSitterWasmPath, "tree-sitter-swift.wasm"),
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: join7(process.treeSitterWasmPath, "tree-sitter-kotlin.wasm"),
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 = join7(process.scriptsPath, "/worker-file-outline/worker.mjs");
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
- l.push(`${indent}${sym.kind}${namePart} (${lr})${cmt}`);
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 join8, isAbsolute as isAbsolute5, resolve as resolve4, relative as relative6 } from "path";
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(join8(process.scriptsPath, "worker-ripgrep/worker.mjs"), [], { cwd });
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, projectDir2) {
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: projectDir2, ...params });
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: projectDir2, ...params });
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: projectDir2, ...params });
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
- join9(process.scriptsPath, "mcp-search/server.mjs"),
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 join10 } from "path";
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
- join10(process.scriptsPath, "mcp-ask-user/server.mjs"),
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 join11 } from "path";
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 existsSync4, mkdirSync as mkdirSync4 } from "fs";
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: join11(session.cwd, uri),
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 = join11(shareDir, shareId);
2841
- const targetPath = join11(targetDir, name);
2842
- if (!existsSync4(targetDir)) {
2843
- mkdirSync4(targetDir, { recursive: true });
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
- join11(process.scriptsPath, "mcp-share-to-user/server.mjs"),
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
- ClientSideConnection,
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", "inherit"],
2923
- cwd: WORKSPACE_TEMP_DIR,
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 { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
3151
+ import { agent, ndJsonStream, methods as methods2 } from "@agentclientprotocol/sdk";
2998
3152
 
2999
3153
  // src/agents/openai-compatible-agent.ts
3000
- import { randomUUID as randomUUID5 } from "crypto";
3001
- import { readFileSync as readFileSync4, statSync as statSync4 } from "fs";
3002
- import { join as join12 } from "path";
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 { stepCountIs, streamText, generateText } from "ai";
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
- if (typeof filePart.data === "string") {
3124
- blob = filePart.data;
3125
- } else if (filePart.data instanceof URL) {
3126
- blob = filePart.data.toString();
3127
- } else if (filePart.data instanceof ArrayBuffer) {
3128
- blob = Buffer.from(new Uint8Array(filePart.data)).toString("base64");
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 = Buffer.from(filePart.data).toString("base64");
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((client) => client.close().catch(() => {
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 client = clients[i];
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([client.tools(), client.listTools()]);
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 = join12(cwd, "AGENTS.md");
4510
+ const agentsMdPath = join15(cwd, "AGENTS.md");
4001
4511
  if (statSync4(agentsMdPath).size <= 50 * 1024) {
4002
- const agentsMd = readFileSync4(agentsMdPath, "utf-8").trim();
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(connection) {
4067
- this.connection = 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 = randomUUID5();
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 result = streamText({
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: buildWorkspaceSystemPrompt(session.cwd, session.additionalDirectories),
4473
- messages: session.history,
5025
+ system: systemPrompt,
5026
+ messages: [...session.history, userMessage],
4474
5027
  tools: {
4475
- ...session.mcp.tools,
4476
- ...session.acp.tools
5028
+ ...sharedTools,
5029
+ ...subagentTool
4477
5030
  },
4478
- stopWhen: stepCountIs(128),
5031
+ stopWhen: isStepCount2(128),
4479
5032
  abortSignal: abortController.signal
4480
5033
  });
4481
- for await (const part of result.fullStream) {
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 agent = new OpenaiCompatibleAgent(options);
4611
- const connection = new AgentSideConnection(() => agent, stream);
4612
- agent.setConnection(connection);
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 agent.abortAll();
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
- this.emitOutput(sessionId, terminalId, chunk.toString("utf-8"));
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.outputBuffer.toString("utf-8"),
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 client = {
4889
- async requestPermission(params) {
4890
- return onPermission(params);
4891
- },
4892
- async sessionUpdate(params) {
4893
- if (params.update.sessionUpdate === "config_option_update") {
4894
- applyConfigOptions(params.sessionId, params.update.configOptions);
4895
- }
4896
- onUpdate(params);
4897
- },
4898
- async writeTextFile(params) {
4899
- await writeFile2(params.path, params.content, "utf8");
4900
- return {};
4901
- },
4902
- async readTextFile(params) {
4903
- const content = await readFile6(params.path, "utf8");
4904
- return { content };
4905
- },
4906
- async createTerminal(params) {
4907
- const id = terminalManager.create(
4908
- params.sessionId,
4909
- params.command,
4910
- params.args || [],
4911
- params.cwd || sessionsCwdMap.get(params.sessionId) || WORKSPACE_TEMP_DIR,
4912
- params.env?.reduce((acc, envVar) => ({ ...acc, [envVar.name]: envVar.value }), {}) || {},
4913
- params.outputByteLimit || 1048576
4914
- );
4915
- return { terminalId: id };
4916
- },
4917
- async terminalOutput(params) {
4918
- const { output, truncated } = terminalManager.getOutput(params.terminalId);
4919
- const term = terminalManager.getTerminal(params.terminalId);
4920
- return {
4921
- output,
4922
- truncated,
4923
- ...term?.isFinished ? { exitStatus: { exitCode: term.exitCode, signal: term.signal } } : {}
4924
- };
4925
- },
4926
- async waitForTerminalExit(params) {
4927
- const { exitCode, signal } = await terminalManager.waitForExit(params.terminalId);
4928
- return { exitCode: exitCode ?? void 0, signal: signal ?? void 0 };
4929
- },
4930
- async killTerminal(params) {
4931
- terminalManager.kill(params.terminalId);
4932
- return {};
4933
- },
4934
- async releaseTerminal(params) {
4935
- terminalManager.release(params.terminalId);
4936
- return {};
4937
- },
4938
- async extNotification(method, params) {
4939
- if (ACP_VERBOSE_LOG && process.env.NODE_ENV === "development") {
4940
- console.log(`[ACP Ext Notification] Method: ${method}`);
4941
- }
4942
- if (typeof params === "object" && params && "sessionId" in params && typeof params.sessionId === "string") {
4943
- if (method === "_kiro.dev/metadata") {
4944
- if ("contextUsagePercentage" in params && typeof params.contextUsagePercentage === "number") {
4945
- onUpdate({
4946
- sessionId: params.sessionId,
4947
- update: {
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 = new ClientSideConnection((_agent) => client, stream);
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.newSession(params);
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
- return this.connection.connection.sendRequest("session/set_model", params);
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.setSessionConfigOption({
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.closeSession({ sessionId }).catch(() => {
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.deleteSession({ sessionId });
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.resumeSession(params);
5686
+ result = await this.connection.agent.request(methods3.agent.session.resume, params);
5115
5687
  } catch {
5116
- result = await this.connection.loadSession({
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.setSessionConfigOption({
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.setSessionMode(params);
5718
+ result = await this.connection.agent.request(methods3.agent.session.setMode, params);
5146
5719
  }
5147
5720
  } else {
5148
- result = await this.connection.setSessionMode(params);
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(params);
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(params);
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.closeSession({ sessionId: sid }).catch(() => {
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 agent = settings.agents.find((a) => a.id === agentId);
5197
- if (!agent) {
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 (agent.type === "stdio") {
5201
- const command = agent.command.trim();
5202
- if (!command) throw new Error(`Agent "${agent.id}" has no command configured.`);
5203
- return { ...agent, command };
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 = agent.provider.trim();
5206
- const baseUrl = agent.baseUrl.trim();
5207
- const apiKey = agent.apiKey.trim();
5208
- if (!provider) throw new Error(`Agent "${agent.id}" has no provider configured.`);
5209
- if (!baseUrl) throw new Error(`Agent "${agent.id}" has no baseUrl configured.`);
5210
- if (!apiKey) throw new Error(`Agent "${agent.id}" has no apiKey configured.`);
5211
- return { ...agent, provider, baseUrl, apiKey };
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-pool.ts
5215
- function createBridgePoolModule(ctx, deps) {
5787
+ // src/backend/bridge-connect.ts
5788
+ function createBridgeConnectModule(ctx, deps) {
5216
5789
  const { sendEvent, storage } = ctx;
5217
- const pool = /* @__PURE__ */ new Map();
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 = pool.get(agentId);
5797
+ async function ensureBridge(sessionKey, agentId, cwd) {
5798
+ const connectPromise = bridges.get(sessionKey);
5225
5799
  if (connectPromise) {
5226
- const pooledBridge = await connectPromise;
5227
- if (pooledBridge.isConnected) return pooledBridge;
5228
- if (pool.get(agentId) === connectPromise) pool.delete(agentId);
5229
- await pooledBridge.kill();
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 nextBridge = new ACPBridge(agentId, {
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 (sessionUpdate === "session_info_update") {
5238
- if (notification.update.title) {
5239
- storage.updateSession(sessionId, { title: notification.update.title });
5240
- const updated = storage.getSession(sessionId);
5241
- if (updated) sendEvent("session-changed", { session: updated });
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
- if (sessionUpdate === "current_mode_update") {
5245
- const session = storage.getSession(sessionId);
5246
- if (session && session.modes) {
5247
- session.modes.currentModeId = notification.update.currentModeId ?? null;
5248
- storage.updateSession(sessionId, { modes: session.modes });
5249
- const updated = storage.getSession(sessionId);
5250
- if (updated) sendEvent("session-changed", { session: updated });
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
- const sessionId = `${agentId}:${request.sessionId}`;
5257
- const session = storage.getSession(sessionId);
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 = nextBridge.connect().then(() => nextBridge).catch(async (error) => {
5309
- if (pool.get(agentId) === newConnectPromise) pool.delete(agentId);
5310
- await nextBridge.kill();
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
- pool.set(agentId, newConnectPromise);
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
- async function clearPool() {
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 pool.values()) {
6015
+ for (const p of bridges.values()) {
5319
6016
  killPromises.push(p.then((b) => b.kill()).catch(() => {
5320
6017
  }));
5321
6018
  }
5322
- pool.clear();
6019
+ bridges.clear();
6020
+ sessionAgentMap.clear();
5323
6021
  await Promise.all(killPromises);
5324
6022
  }
5325
- return { pool, ensureBridge, clearPool, setBroadcast };
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 dirname4, join as join13 } from "path";
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 = dirname4(packageJsonPath);
5365
- const helperPath = join13(
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.bridgePool.values()) {
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 dirname5, join as join14, relative as relative7, extname as extname2, basename as basename3 } from "path";
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 = join14(dir, name);
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 = join14(cwd, fileName);
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 = join14(cwd, `${name}(${counter})${ext}`);
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 = join14(cwd, name);
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 = join14(startPath, name);
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(dirname5(targetPath), { recursive: true });
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 = join14(destDir, fileName);
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 = join14(destDir, `${base}(${counter})${ext}`);
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 dirname6, relative as relative8 } from "path";
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 = dirname6(targetPath);
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 ensureBridge(agentId) {
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 addProject(cwd) {
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 deleteProject(projectId) {
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
- try {
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 randomUUID8 } from "crypto";
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 dirname7, join as join15 } from "path";
6007
- import { mkdirSync as mkdirSync5, existsSync as existsSync5, unlinkSync } from "fs";
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
- mkdirSync5(dirname7(socketPath), { recursive: true });
6014
- if (existsSync5(socketPath)) {
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 (existsSync5(socketPath)) {
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 join15(SOCKETS_DIR, `${key}-${timestamp}.socket`);
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 join16 } from "path";
6099
- import { randomUUID as randomUUID6 } from "crypto";
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 = join16(FELLO_DIR, "ilink");
6298
- var CREDENTIALS_PATH = join16(ILINK_DIR, "credentials.json");
6299
- var CURSOR_PATH = join16(ILINK_DIR, "cursor.json");
6300
- var ACTIVE_SESSION_PATH = join16(ILINK_DIR, "active-session.json");
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
- await clearCredentials();
6515
- await clearCursor();
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()}-${randomUUID6().slice(0, 8)}`,
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()}-${randomUUID6().slice(0, 8)}`,
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()}-${randomUUID6().slice(0, 8)}`,
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 randomUUID7 } from "crypto";
6923
- import { zContentBlock } from "@agentclientprotocol/sdk/dist/schema/zod.gen.js";
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
- const { data, success } = zContentBlock.safeParse(object);
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: { receivedAt: Date.now(), displayId: randomUUID7() }
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: omit(enrichedNotification.update, ["rawInput", "rawOutput"])
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
- storage.appendSessionMessage(sessionId, enrichedNotification);
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.length > 0 ? result[result.length - 1] : void 0;
7074
- if (prev?.update?.sessionUpdate === type && prev.update.content?.type === "text" && update.content?.type === "text") {
7075
- result[result.length - 1] = {
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 === "tool_call" || type === "tool_call_update") {
7798
+ if (type === "tool_call_update") {
7089
7799
  const toolCallId = update.toolCallId;
7090
- const idx = result.findIndex(
7091
- (n) => (n.update?.sessionUpdate === "tool_call" || n.update?.sessionUpdate === "tool_call_update") && n.update.toolCallId === toolCallId
7092
- );
7093
- if (idx !== -1) {
7094
- const prev = result[idx];
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 { bridgePool, askUser, shareToUser, skills, search: search2, ilink: ilinkState } = deps;
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
- bridgePool.setBroadcast(notif.broadcastAndSaveSessionUpdate);
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 b = await bridgePool.ensureBridge(agentId);
7163
- const socketPath = generateSocketPath(randomUUID8());
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
- await createSessionSocketServer(sessionInfo.id, { socketPath, project });
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 bridgePool.ensureBridge(session.agentId);
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(randomUUID8());
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, { socketPath, project });
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 = bridgePool.pool.get(session.agentId);
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 bridgePool.ensureBridge(session.agentId);
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(randomUUID8());
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, { socketPath, project });
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 = bridgePool.pool.get(session.agentId);
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 updateSession({
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 deleteSession(sessionId) {
8188
+ async function deleteSession2(sessionId) {
7451
8189
  const session = storage.getSession(sessionId);
7452
8190
  if (session) {
7453
8191
  try {
7454
- const b = await bridgePool.ensureBridge(session.agentId);
7455
- if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
7456
- await b.deleteSession(session.resumeId);
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 = bridgePool.pool.get(session.agentId);
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 b = await bridgePool.ensureBridge(session.agentId);
7501
- if (b.isSessionLoaded(session.resumeId)) await b.closeSession(session.resumeId);
7502
- await b.deleteSession(session.resumeId);
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 = bridgePool.pool.get(session.agentId);
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 = bridgePool.pool.get(session.agentId);
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 = bridgePool.pool.get(session.agentId);
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 = bridgePool.pool.get(session.agentId);
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 agent = settings.agents.find((a) => !a.disabled);
7822
- if (!agent) {
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: agent.id,
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
- await writeActiveSessionId(null);
8027
- sendEvent("ilink-active-session-changed", { sessionId: null });
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 randomUUID9 } from "crypto";
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 = 5 * 60 * 1e3
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-${randomUUID9()}`);
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 join17 } from "path";
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: { ...n.update?._meta, fello: { receivedAt: Date.now(), displayId: `auto-${i}` } }
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
- "README.md",
8290
- generateReadme(schedule, messages, startedAt, null, storage.getSettings().i18n?.language)
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 = join17(base, filePath);
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
- function reduceNotificationsToMessages(notifications) {
8379
- const messages = [];
8380
- for (const n of notifications) {
8381
- const update = n.update;
8382
- if (!update) continue;
8383
- switch (update.sessionUpdate) {
8384
- case "user_message_chunk": {
8385
- const content = update.content;
8386
- if (!content || content.type !== "text") break;
8387
- const last = messages[messages.length - 1];
8388
- if (last && last.role === "user") last.text += content.text ?? "";
8389
- else messages.push({ role: "user", text: content.text ?? "" });
8390
- break;
8391
- }
8392
- case "agent_message_chunk": {
8393
- const content = update.content;
8394
- if (!content || content.type !== "text") break;
8395
- const last = messages[messages.length - 1];
8396
- if (last && last.role === "assistant") last.text += content.text ?? "";
8397
- else messages.push({ role: "assistant", text: content.text ?? "" });
8398
- break;
8399
- }
8400
- case "agent_thought_chunk": {
8401
- const content = update.content;
8402
- if (!content || content.type !== "text") break;
8403
- const last = messages[messages.length - 1];
8404
- if (last && last.role === "thought") last.text += content.text ?? "";
8405
- else messages.push({ role: "thought", text: content.text ?? "" });
8406
- break;
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
- case "tool_call":
8409
- case "tool_call_update": {
8410
- const title = update.title ?? "";
8411
- const content = update.content;
8412
- let text = "";
8413
- if (Array.isArray(content)) {
8414
- for (const block of content) {
8415
- if (block.type === "content") text += block.content.type ?? "";
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
- messages.push({ role: "tool_call", text, toolTitle: title });
8419
- break;
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
- return messages;
8424
- }
8425
- function renderMessages(lines, messages) {
8426
- for (const msg of messages) {
8427
- if (msg.role === "user" || msg.role === "assistant") {
8428
- const text = msg.text.trim();
8429
- if (text) lines.push(text + "\n");
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
- function generateErrorReadme(schedule, messages, startedAt, errorMessage, locale) {
8434
- const lines = [
8435
- `# ${schedule.name}`,
8436
- `> Run at ${new Date(startedAt).toLocaleString(locale)}`,
8437
- "",
8438
- `> \u274C Task failed: ${errorMessage}`,
8439
- ""
8440
- ];
8441
- renderMessages(lines, messages);
8442
- return lines.join("\n");
8443
- }
8444
- function generateReadme(schedule, messages, startedAt, modelError, locale) {
8445
- const lines = [
8446
- `# ${schedule.name}`,
8447
- `> Run at ${new Date(startedAt).toLocaleString(locale)}`
8448
- ];
8449
- if (modelError) {
8450
- lines.push("", `> \u26A0\uFE0F ${modelError}`);
8451
- lines.push("", `> The task continued with the agent's default model.`);
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
- lines.push("");
8454
- renderMessages(lines, messages);
8455
- return lines.join("\n");
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 bridgePool = createBridgePoolModule(ctx, { askUser });
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
- bridgePool,
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, { bridgePool: bridgePool.pool, watcher: watcher2 });
8488
- const terminal = createTerminalModule(ctx, { bridgePool: bridgePool.pool });
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
- const p = bridgePool.pool.get(agentId);
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 join18(skillDir, SKILL_FILENAME);
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
- const p = bridgePool.pool.get(agentId);
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 join18(shareDir, sharePath);
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 bridgePool.clearPool();
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(join18(TEMP_DIR, file), { recursive: true, force: true });
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.39";
9922
+ var version = "0.1.42";
8861
9923
 
8862
9924
  // src/server/main.ts
8863
9925
  var port;