dominds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/LICENSE +157 -0
  2. package/README.md +250 -0
  3. package/README.zh.md +161 -0
  4. package/dist/access-control.js +253 -0
  5. package/dist/cli/create.js +263 -0
  6. package/dist/cli/read.js +84 -0
  7. package/dist/cli/tui.js +199 -0
  8. package/dist/cli/webui.js +169 -0
  9. package/dist/cli.js +227 -0
  10. package/dist/dialog-factory.js +53 -0
  11. package/dist/dialog-global-registry.js +68 -0
  12. package/dist/dialog-instance-registry.js +78 -0
  13. package/dist/dialog-run-state.js +198 -0
  14. package/dist/dialog.js +1024 -0
  15. package/dist/evt-registry.js +103 -0
  16. package/dist/index.js +8 -0
  17. package/dist/llm/client.js +69 -0
  18. package/dist/llm/defaults.yaml +386 -0
  19. package/dist/llm/driver.js +3214 -0
  20. package/dist/llm/gen/anthropic.js +611 -0
  21. package/dist/llm/gen/codex.js +375 -0
  22. package/dist/llm/gen/mock.js +326 -0
  23. package/dist/llm/gen/openai.js +470 -0
  24. package/dist/llm/gen/registry.js +26 -0
  25. package/dist/llm/gen.js +2 -0
  26. package/dist/llm/tools-projection.js +37 -0
  27. package/dist/log.js +228 -0
  28. package/dist/mcp/config.js +230 -0
  29. package/dist/mcp/sdk-client.js +129 -0
  30. package/dist/mcp/server-runtime.js +57 -0
  31. package/dist/mcp/stdio-client.js +280 -0
  32. package/dist/mcp/supervisor.js +979 -0
  33. package/dist/mcp/tool-names.js +109 -0
  34. package/dist/minds/builtin/cmdr/persona.md +3 -0
  35. package/dist/minds/builtin/dijiang/knowledge.md +287 -0
  36. package/dist/minds/builtin/dijiang/persona.md +7 -0
  37. package/dist/minds/builtin/fuxi/persona.en.md +59 -0
  38. package/dist/minds/builtin/fuxi/persona.zh.md +49 -0
  39. package/dist/minds/builtin/pangu/persona.en.md +78 -0
  40. package/dist/minds/builtin/pangu/persona.zh.md +71 -0
  41. package/dist/minds/load.js +617 -0
  42. package/dist/minds/minds-i18n.js +131 -0
  43. package/dist/minds/system-prompt.js +281 -0
  44. package/dist/persistence.js +3128 -0
  45. package/dist/problems.js +109 -0
  46. package/dist/server/api-routes.js +1031 -0
  47. package/dist/server/auth.js +180 -0
  48. package/dist/server/mime-types.js +32 -0
  49. package/dist/server/prompts-routes.js +543 -0
  50. package/dist/server/server-core.js +235 -0
  51. package/dist/server/setup-routes.js +697 -0
  52. package/dist/server/static-server.js +132 -0
  53. package/dist/server/websocket-handler.js +1011 -0
  54. package/dist/server.js +164 -0
  55. package/dist/shared/async-fifo-mutex.js +36 -0
  56. package/dist/shared/diligence.js +20 -0
  57. package/dist/shared/dotenv.js +144 -0
  58. package/dist/shared/evt.js +195 -0
  59. package/dist/shared/i18n/driver-messages.js +267 -0
  60. package/dist/shared/i18n/text.js +9 -0
  61. package/dist/shared/i18n/tool-result-messages.js +51 -0
  62. package/dist/shared/rtws-cli.js +73 -0
  63. package/dist/shared/runtime-language.js +47 -0
  64. package/dist/shared/team-mgmt-manual.js +116 -0
  65. package/dist/shared/types/context-health.js +2 -0
  66. package/dist/shared/types/dialog.js +11 -0
  67. package/dist/shared/types/i18n.js +2 -0
  68. package/dist/shared/types/index.js +26 -0
  69. package/dist/shared/types/language.js +40 -0
  70. package/dist/shared/types/problems.js +2 -0
  71. package/dist/shared/types/prompts.js +2 -0
  72. package/dist/shared/types/q4h.js +7 -0
  73. package/dist/shared/types/run-state.js +8 -0
  74. package/dist/shared/types/setup.js +2 -0
  75. package/dist/shared/types/storage.js +10 -0
  76. package/dist/shared/types/tellask.js +8 -0
  77. package/dist/shared/types/tools-registry.js +2 -0
  78. package/dist/shared/types/wire.js +12 -0
  79. package/dist/shared/utils/fmt.js +9 -0
  80. package/dist/shared/utils/html.js +20 -0
  81. package/dist/shared/utils/id.js +18 -0
  82. package/dist/shared/utils/inter-dialog-format.js +101 -0
  83. package/dist/shared/utils/time.js +13 -0
  84. package/dist/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  85. package/dist/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  86. package/dist/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  87. package/dist/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  88. package/dist/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  89. package/dist/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  90. package/dist/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  91. package/dist/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  92. package/dist/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  93. package/dist/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  94. package/dist/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  95. package/dist/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  96. package/dist/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  97. package/dist/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  98. package/dist/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  99. package/dist/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  100. package/dist/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  101. package/dist/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  102. package/dist/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  103. package/dist/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  104. package/dist/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  105. package/dist/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  106. package/dist/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  107. package/dist/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  108. package/dist/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  109. package/dist/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  110. package/dist/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  111. package/dist/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  112. package/dist/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  113. package/dist/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  114. package/dist/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  115. package/dist/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  116. package/dist/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  117. package/dist/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  118. package/dist/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  119. package/dist/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  120. package/dist/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  121. package/dist/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  122. package/dist/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  123. package/dist/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  124. package/dist/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  125. package/dist/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  126. package/dist/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  127. package/dist/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  128. package/dist/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  129. package/dist/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  130. package/dist/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  131. package/dist/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  132. package/dist/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  133. package/dist/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  134. package/dist/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  135. package/dist/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  136. package/dist/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  137. package/dist/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  138. package/dist/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  139. package/dist/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  140. package/dist/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  141. package/dist/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  142. package/dist/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  143. package/dist/static/assets/_baseUniq-Crfl3d5Y.js +661 -0
  144. package/dist/static/assets/_baseUniq-Crfl3d5Y.js.map +1 -0
  145. package/dist/static/assets/arc-CbA_x9GD.js +132 -0
  146. package/dist/static/assets/arc-CbA_x9GD.js.map +1 -0
  147. package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js +8685 -0
  148. package/dist/static/assets/architectureDiagram-VXUJARFQ-lcFS8ZQJ.js.map +1 -0
  149. package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js +3608 -0
  150. package/dist/static/assets/blockDiagram-VD42YOAC-B3Q36qRc.js.map +1 -0
  151. package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js +2482 -0
  152. package/dist/static/assets/c4Diagram-YG6GDRKO-Mt-aq3VH.js.map +1 -0
  153. package/dist/static/assets/channel-BVr1Yke-.js +8 -0
  154. package/dist/static/assets/channel-BVr1Yke-.js.map +1 -0
  155. package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js +17 -0
  156. package/dist/static/assets/chunk-4BX2VUAB-qCIn5Iic.js.map +1 -0
  157. package/dist/static/assets/chunk-55IACEB6-q172NeCV.js +14 -0
  158. package/dist/static/assets/chunk-55IACEB6-q172NeCV.js.map +1 -0
  159. package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js +1827 -0
  160. package/dist/static/assets/chunk-B4BG7PRW-CMJmtYzq.js.map +1 -0
  161. package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js +1916 -0
  162. package/dist/static/assets/chunk-DI55MBZ5-DiuwwZPL.js.map +1 -0
  163. package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js +20 -0
  164. package/dist/static/assets/chunk-FMBD7UC4-06sqZTTn.js.map +1 -0
  165. package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js +25 -0
  166. package/dist/static/assets/chunk-QN33PNHL-CnpBNkpP.js.map +1 -0
  167. package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js +18 -0
  168. package/dist/static/assets/chunk-QZHKN3VN-CNgjMR-e.js.map +1 -0
  169. package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js +109 -0
  170. package/dist/static/assets/chunk-TZMSLE5B-BxtzW6--.js.map +1 -0
  171. package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js +23 -0
  172. package/dist/static/assets/classDiagram-2ON5EDUG-29huvmn-.js.map +1 -0
  173. package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js +23 -0
  174. package/dist/static/assets/classDiagram-v2-WZHVMYZB-29huvmn-.js.map +1 -0
  175. package/dist/static/assets/clone-D2OgLSSn.js +9 -0
  176. package/dist/static/assets/clone-D2OgLSSn.js.map +1 -0
  177. package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js +4943 -0
  178. package/dist/static/assets/cose-bilkent-S5V4N54A-BNegDCxl.js.map +1 -0
  179. package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js +30240 -0
  180. package/dist/static/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -0
  181. package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js +695 -0
  182. package/dist/static/assets/dagre-6UL2VRFP-f1XrTRSn.js.map +1 -0
  183. package/dist/static/assets/defaultLocale-DVr69WTU.js +207 -0
  184. package/dist/static/assets/defaultLocale-DVr69WTU.js.map +1 -0
  185. package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js +849 -0
  186. package/dist/static/assets/diagram-PSM6KHXK-8w1WbeDi.js.map +1 -0
  187. package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js +303 -0
  188. package/dist/static/assets/diagram-QEK2KX5R-CF4wtMmR.js.map +1 -0
  189. package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js +213 -0
  190. package/dist/static/assets/diagram-S2PKOQOG-8p3Avgn2.js.map +1 -0
  191. package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js +1159 -0
  192. package/dist/static/assets/erDiagram-Q2GNP2WA-BMKLxlM9.js.map +1 -0
  193. package/dist/static/assets/favicon-Cmg5RbCj.svg +8 -0
  194. package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js +2332 -0
  195. package/dist/static/assets/flowDiagram-NV44I4VS-CgEuPNK2.js.map +1 -0
  196. package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js +3681 -0
  197. package/dist/static/assets/ganttDiagram-JELNMOA3-bJkDCf-9.js.map +1 -0
  198. package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js +1206 -0
  199. package/dist/static/assets/gitGraphDiagram-NY62KEGX-4QE9kesp.js.map +1 -0
  200. package/dist/static/assets/graph-CS0Pmm7c.js +597 -0
  201. package/dist/static/assets/graph-CS0Pmm7c.js.map +1 -0
  202. package/dist/static/assets/index-BS6HnGzC.js +112303 -0
  203. package/dist/static/assets/index-BS6HnGzC.js.map +1 -0
  204. package/dist/static/assets/index-DaIsSzC_.css +483 -0
  205. package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js +34 -0
  206. package/dist/static/assets/infoDiagram-WHAUD3N6-ypBcKfUs.js.map +1 -0
  207. package/dist/static/assets/init-ZxktEp_H.js +17 -0
  208. package/dist/static/assets/init-ZxktEp_H.js.map +1 -0
  209. package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js +1255 -0
  210. package/dist/static/assets/journeyDiagram-XKPGCS4Q-QnrxDowJ.js.map +1 -0
  211. package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js +1048 -0
  212. package/dist/static/assets/kanban-definition-3W4ZIXB7-CfvEc4z5.js.map +1 -0
  213. package/dist/static/assets/layout-8TGxpm23.js +2218 -0
  214. package/dist/static/assets/layout-8TGxpm23.js.map +1 -0
  215. package/dist/static/assets/linear-BATBPQQv.js +341 -0
  216. package/dist/static/assets/linear-BATBPQQv.js.map +1 -0
  217. package/dist/static/assets/min-B3oVH3AC.js +42 -0
  218. package/dist/static/assets/min-B3oVH3AC.js.map +1 -0
  219. package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js +1127 -0
  220. package/dist/static/assets/mindmap-definition-VGOIOE7T-L7VLwwF8.js.map +1 -0
  221. package/dist/static/assets/ordinal-CxptdPJm.js +77 -0
  222. package/dist/static/assets/ordinal-CxptdPJm.js.map +1 -0
  223. package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js +241 -0
  224. package/dist/static/assets/pieDiagram-ADFJNKIX-CFW3zIhM.js.map +1 -0
  225. package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js +1338 -0
  226. package/dist/static/assets/quadrantDiagram-AYHSOK5B-B7ssen3E.js.map +1 -0
  227. package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js +1162 -0
  228. package/dist/static/assets/requirementDiagram-UZGBJVZJ-D0v5BArv.js.map +1 -0
  229. package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js +1195 -0
  230. package/dist/static/assets/sankeyDiagram-TZEHDZUN-B7slncJe.js.map +1 -0
  231. package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js +3875 -0
  232. package/dist/static/assets/sequenceDiagram-WL72ISMW-oXU2lRh_.js.map +1 -0
  233. package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js +452 -0
  234. package/dist/static/assets/stateDiagram-FKZM4ZOC-CFYsEd0x.js.map +1 -0
  235. package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js +22 -0
  236. package/dist/static/assets/stateDiagram-v2-4FDKWEC3-C0UWaNA7.js.map +1 -0
  237. package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js +1223 -0
  238. package/dist/static/assets/timeline-definition-IT6M3QCI-C3KODUrh.js.map +1 -0
  239. package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js +18753 -0
  240. package/dist/static/assets/treemap-KMMF4GRG-DAGDLhj2.js.map +1 -0
  241. package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js +1888 -0
  242. package/dist/static/assets/xychartDiagram-PRI3JC2R-C0J9iwTO.js.map +1 -0
  243. package/dist/static/index.html +71 -0
  244. package/dist/static/testing/dom-observation-utils.js +425 -0
  245. package/dist/static/testing/e2e-test-helper.js +3119 -0
  246. package/dist/team.js +1160 -0
  247. package/dist/tellask.js +431 -0
  248. package/dist/tool.js +150 -0
  249. package/dist/tools/apply-patch.js +542 -0
  250. package/dist/tools/builtins.js +196 -0
  251. package/dist/tools/context-health.js +177 -0
  252. package/dist/tools/ctrl.js +478 -0
  253. package/dist/tools/diag.js +583 -0
  254. package/dist/tools/env.js +184 -0
  255. package/dist/tools/fs.js +818 -0
  256. package/dist/tools/mcp.js +138 -0
  257. package/dist/tools/mem.js +349 -0
  258. package/dist/tools/os.js +751 -0
  259. package/dist/tools/prompts/team_mgmt.en.md +70 -0
  260. package/dist/tools/prompts/team_mgmt.zh.md +70 -0
  261. package/dist/tools/prompts/ws_mod.en.md +86 -0
  262. package/dist/tools/prompts/ws_mod.zh.md +87 -0
  263. package/dist/tools/registry-snapshot.js +31 -0
  264. package/dist/tools/registry.js +121 -0
  265. package/dist/tools/ripgrep.js +678 -0
  266. package/dist/tools/team-mgmt.js +3300 -0
  267. package/dist/tools/txt.js +3178 -0
  268. package/dist/utils/id.js +72 -0
  269. package/dist/utils/task-doc.js +236 -0
  270. package/dist/utils/task-package.js +522 -0
  271. package/dist/utils/taskdoc-search.js +280 -0
  272. package/dist/utils/taskdoc.js +400 -0
  273. package/package.json +69 -0
@@ -0,0 +1,979 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isMcpToolsetLeasedToDialog = isMcpToolsetLeasedToDialog;
37
+ exports.releaseMcpToolsetLeaseForDialog = releaseMcpToolsetLeaseForDialog;
38
+ exports.startMcpSupervisor = startMcpSupervisor;
39
+ exports.stopMcpSupervisor = stopMcpSupervisor;
40
+ exports.requestMcpServerRestart = requestMcpServerRestart;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const log_1 = require("../log");
44
+ const problems_1 = require("../problems");
45
+ const time_1 = require("../shared/utils/time");
46
+ const registry_1 = require("../tools/registry");
47
+ const config_1 = require("./config");
48
+ const sdk_client_1 = require("./sdk-client");
49
+ const server_runtime_1 = require("./server-runtime");
50
+ const tool_names_1 = require("./tool-names");
51
+ const log = (0, log_1.createLogger)('mcp/supervisor');
52
+ const MCP_YAML_PATH = path.join('.minds', 'mcp.yaml');
53
+ const serverStateById = new Map();
54
+ const toolOwnerByName = new Map();
55
+ const toolsetOwnerByName = new Map();
56
+ function makeLeaseReminderMeta(serverId) {
57
+ return { kind: 'mcp_lease', serverId };
58
+ }
59
+ function isLeaseReminderMeta(value) {
60
+ if (!isRecord(value) || Array.isArray(value))
61
+ return false;
62
+ if (value.kind !== 'mcp_lease')
63
+ return false;
64
+ return typeof value.serverId === 'string' && value.serverId.trim().length > 0;
65
+ }
66
+ function ensureLeaseReminder(dlg, serverId) {
67
+ for (const r of dlg.reminders) {
68
+ if (!r.owner)
69
+ continue;
70
+ if (r.owner.name !== 'mcpLease')
71
+ continue;
72
+ const meta = r.meta;
73
+ if (!isLeaseReminderMeta(meta))
74
+ continue;
75
+ if (meta.serverId !== serverId)
76
+ continue;
77
+ return;
78
+ }
79
+ const owner = (0, registry_1.getReminderOwner)('mcpLease');
80
+ const content = `MCP toolset leased: ${serverId}\n\n` +
81
+ `This MCP server is treated as non-stateless. When you are confident you won't need it again soon, release it:\n` +
82
+ `mcp_release({"serverId":"${serverId}"})`;
83
+ dlg.addReminder(content, owner, makeLeaseReminderMeta(serverId));
84
+ }
85
+ class McpServerDispatch {
86
+ constructor(params) {
87
+ this.leasesByDialogKey = new Map();
88
+ this.leaseInitByDialogKey = new Map();
89
+ this.canceledLeaseDialogs = new Set();
90
+ this.stopRequested = false;
91
+ this.serverId = params.serverId;
92
+ this.toolsetName = params.toolsetName;
93
+ this.cfg = params.cfg;
94
+ this.sharedRuntime = params.sharedRuntime;
95
+ }
96
+ hasLeaseForDialog(dialogKey) {
97
+ return this.leasesByDialogKey.has(dialogKey) || this.leaseInitByDialogKey.has(dialogKey);
98
+ }
99
+ releaseLeaseForDialog(dialogKey) {
100
+ const init = this.leaseInitByDialogKey.get(dialogKey);
101
+ if (init) {
102
+ this.canceledLeaseDialogs.add(dialogKey);
103
+ // Best-effort: allow the in-flight connect to complete, then immediately stop it.
104
+ void init
105
+ .then((rt) => rt.requestStop({ forceKillAfterMs: 3000 }))
106
+ .catch(() => {
107
+ // ignore
108
+ });
109
+ this.leaseInitByDialogKey.delete(dialogKey);
110
+ }
111
+ const existing = this.leasesByDialogKey.get(dialogKey);
112
+ if (!existing)
113
+ return false;
114
+ this.leasesByDialogKey.delete(dialogKey);
115
+ existing.requestStop({ forceKillAfterMs: 3000 });
116
+ return true;
117
+ }
118
+ requestStop() {
119
+ if (this.stopRequested)
120
+ return;
121
+ this.stopRequested = true;
122
+ if (this.sharedRuntime) {
123
+ this.sharedRuntime.requestStop({ forceKillAfterMs: 3000 });
124
+ }
125
+ for (const rt of this.leasesByDialogKey.values()) {
126
+ rt.requestStop({ forceKillAfterMs: 3000 });
127
+ }
128
+ for (const [dialogKey, init] of this.leaseInitByDialogKey.entries()) {
129
+ this.canceledLeaseDialogs.add(dialogKey);
130
+ void init
131
+ .then((rt) => rt.requestStop({ forceKillAfterMs: 3000 }))
132
+ .catch(() => {
133
+ // ignore
134
+ });
135
+ }
136
+ }
137
+ async callToolForDialog(dlg, mcpToolName, args) {
138
+ if (this.cfg.truelyStateless) {
139
+ if (!this.sharedRuntime) {
140
+ throw new Error(`MCP server '${this.serverId}' missing shared runtime`);
141
+ }
142
+ return await this.sharedRuntime.callTool(mcpToolName, args);
143
+ }
144
+ const dialogKey = dlg.id.key();
145
+ const existing = this.leasesByDialogKey.get(dialogKey);
146
+ if (existing) {
147
+ return await existing.callTool(mcpToolName, args);
148
+ }
149
+ if (this.stopRequested) {
150
+ const oneShot = await this.connectNewLeaseRuntime();
151
+ oneShot.requestStop({ forceKillAfterMs: 3000 });
152
+ return await oneShot.callTool(mcpToolName, args);
153
+ }
154
+ let init = this.leaseInitByDialogKey.get(dialogKey);
155
+ if (!init) {
156
+ this.canceledLeaseDialogs.delete(dialogKey);
157
+ init = (async () => {
158
+ try {
159
+ const rt = await this.connectNewLeaseRuntime();
160
+ this.finalizeLeaseForDialog(dialogKey, rt);
161
+ return rt;
162
+ }
163
+ catch (err) {
164
+ this.leaseInitByDialogKey.delete(dialogKey);
165
+ this.canceledLeaseDialogs.delete(dialogKey);
166
+ throw err;
167
+ }
168
+ })();
169
+ this.leaseInitByDialogKey.set(dialogKey, init);
170
+ }
171
+ const runtime = await init;
172
+ this.attachLeaseReminder(dlg);
173
+ return await runtime.callTool(mcpToolName, args);
174
+ }
175
+ async connectNewLeaseRuntime() {
176
+ const serverId = this.serverId;
177
+ const client = this.cfg.transport === 'stdio'
178
+ ? await sdk_client_1.McpSdkClient.connectStdio({
179
+ serverId,
180
+ command: this.cfg.command,
181
+ args: this.cfg.args,
182
+ env: buildChildEnv(this.cfg, serverId),
183
+ cwd: process.cwd(),
184
+ })
185
+ : await sdk_client_1.McpSdkClient.connectStreamableHttp({
186
+ serverId,
187
+ url: this.cfg.url,
188
+ headers: buildHttpHeaders(this.cfg, serverId),
189
+ sessionId: this.cfg.sessionId,
190
+ });
191
+ const runtime = new server_runtime_1.McpServerRuntime({
192
+ serverId: this.serverId,
193
+ toolsetName: this.toolsetName,
194
+ client,
195
+ });
196
+ return runtime;
197
+ }
198
+ finalizeLeaseForDialog(dialogKey, runtime) {
199
+ this.leaseInitByDialogKey.delete(dialogKey);
200
+ if (this.canceledLeaseDialogs.has(dialogKey)) {
201
+ this.canceledLeaseDialogs.delete(dialogKey);
202
+ runtime.requestStop({ forceKillAfterMs: 3000 });
203
+ return;
204
+ }
205
+ if (this.stopRequested) {
206
+ runtime.requestStop({ forceKillAfterMs: 3000 });
207
+ return;
208
+ }
209
+ this.leasesByDialogKey.set(dialogKey, runtime);
210
+ }
211
+ attachLeaseReminder(dlg) {
212
+ if (this.cfg.truelyStateless)
213
+ return;
214
+ ensureLeaseReminder(dlg, this.serverId);
215
+ }
216
+ }
217
+ function isMcpToolsetLeasedToDialog(serverId, dialogKey) {
218
+ const state = serverStateById.get(serverId);
219
+ if (!state)
220
+ return false;
221
+ return state.dispatch.hasLeaseForDialog(dialogKey);
222
+ }
223
+ function releaseMcpToolsetLeaseForDialog(serverId, dialogKey) {
224
+ const state = serverStateById.get(serverId);
225
+ if (!state) {
226
+ return { ok: false, errorText: `MCP server '${serverId}' is not loaded` };
227
+ }
228
+ if (state.cfg.truelyStateless) {
229
+ return { ok: true, released: false };
230
+ }
231
+ const released = state.dispatch.releaseLeaseForDialog(dialogKey);
232
+ return { ok: true, released };
233
+ }
234
+ let mindsDirWatcher;
235
+ let workspaceWatcher;
236
+ let pollTimer;
237
+ let debounceTimer;
238
+ let lastSeenMtimeMs;
239
+ let reloadChain = Promise.resolve();
240
+ function startMcpSupervisor() {
241
+ reloadChain = reloadChain
242
+ .then(async () => await reloadNow('startup'))
243
+ .catch((err) => {
244
+ log.warn('MCP initial load failed', err);
245
+ });
246
+ // Best-effort file watch (fast feedback). `.minds/` may be wiped/recreated during a dev session,
247
+ // so watcher setup must be resilient and re-attempted at runtime.
248
+ void ensureMindsDirWatcher('startup');
249
+ // Watch workspace root for `.minds/` create/delete. This improves responsiveness when `.minds/`
250
+ // appears/disappears without requiring a full polling interval.
251
+ try {
252
+ workspaceWatcher = fs.watch('.', { persistent: false }, (_event, filename) => {
253
+ const name = filename ? filename.toString() : '';
254
+ if (name !== '' && name !== path.dirname(MCP_YAML_PATH))
255
+ return;
256
+ void ensureMindsDirWatcher('workspace.watch');
257
+ });
258
+ workspaceWatcher.on('error', () => {
259
+ if (workspaceWatcher) {
260
+ workspaceWatcher.close();
261
+ workspaceWatcher = undefined;
262
+ }
263
+ });
264
+ }
265
+ catch {
266
+ // ignore; polling still works
267
+ }
268
+ // Polling fallback for reliability (editors that write via rename, platforms where watch misses).
269
+ pollTimer = setInterval(() => {
270
+ void maybePollReload();
271
+ void ensureMindsDirWatcher('poll');
272
+ }, 1500);
273
+ }
274
+ function stopMcpSupervisor() {
275
+ if (mindsDirWatcher) {
276
+ mindsDirWatcher.close();
277
+ mindsDirWatcher = undefined;
278
+ }
279
+ if (workspaceWatcher) {
280
+ workspaceWatcher.close();
281
+ workspaceWatcher = undefined;
282
+ }
283
+ if (pollTimer) {
284
+ clearInterval(pollTimer);
285
+ pollTimer = undefined;
286
+ }
287
+ if (debounceTimer) {
288
+ clearTimeout(debounceTimer);
289
+ debounceTimer = undefined;
290
+ }
291
+ }
292
+ function requestMcpServerRestart(serverId) {
293
+ return new Promise((resolve) => {
294
+ reloadChain = reloadChain
295
+ .then(async () => {
296
+ try {
297
+ const res = await restartServerNow(serverId);
298
+ resolve(res);
299
+ }
300
+ catch (err) {
301
+ const errorText = err instanceof Error ? err.message : String(err);
302
+ log.warn(`MCP server restart failed`, err, { serverId });
303
+ resolve({ ok: false, errorText });
304
+ }
305
+ })
306
+ .catch((err) => {
307
+ const errorText = err instanceof Error ? err.message : String(err);
308
+ log.warn(`MCP server restart enqueue failed`, err, { serverId });
309
+ resolve({ ok: false, errorText });
310
+ });
311
+ });
312
+ }
313
+ async function ensureMindsDirWatcher(reason) {
314
+ let st = null;
315
+ try {
316
+ st = await fs.promises.stat(path.dirname(MCP_YAML_PATH));
317
+ }
318
+ catch (err) {
319
+ const code = isRecord(err) && 'code' in err ? err.code : undefined;
320
+ if (code === 'ENOENT') {
321
+ // `.minds/` may not exist; treat as normal and ensure watcher is detached.
322
+ if (mindsDirWatcher) {
323
+ mindsDirWatcher.close();
324
+ mindsDirWatcher = undefined;
325
+ }
326
+ return;
327
+ }
328
+ return;
329
+ }
330
+ if (!st.isDirectory()) {
331
+ if (mindsDirWatcher) {
332
+ mindsDirWatcher.close();
333
+ mindsDirWatcher = undefined;
334
+ }
335
+ return;
336
+ }
337
+ if (mindsDirWatcher)
338
+ return;
339
+ try {
340
+ mindsDirWatcher = fs.watch(path.dirname(MCP_YAML_PATH), { persistent: false }, (_event, filename) => {
341
+ if (filename && filename.toString() !== path.basename(MCP_YAML_PATH))
342
+ return;
343
+ scheduleReload(`minds.watch (${reason})`);
344
+ });
345
+ mindsDirWatcher.on('error', () => {
346
+ // Directory may be wiped/recreated; drop watcher and let polling recreate it.
347
+ if (mindsDirWatcher) {
348
+ mindsDirWatcher.close();
349
+ mindsDirWatcher = undefined;
350
+ }
351
+ });
352
+ }
353
+ catch (err) {
354
+ const code = isRecord(err) && 'code' in err ? err.code : undefined;
355
+ if (code === 'ENOENT') {
356
+ // `.minds/` disappeared between stat and watch; normal in dev.
357
+ return;
358
+ }
359
+ // Non-fatal; polling still works.
360
+ log.warn('Failed to start fs.watch for MCP config; polling only', err);
361
+ }
362
+ }
363
+ function scheduleReload(reason) {
364
+ if (debounceTimer)
365
+ clearTimeout(debounceTimer);
366
+ debounceTimer = setTimeout(() => {
367
+ reloadChain = reloadChain
368
+ .then(async () => await reloadNow(reason))
369
+ .catch((err) => {
370
+ log.warn('MCP reload failed', err);
371
+ });
372
+ }, 200);
373
+ }
374
+ async function maybePollReload() {
375
+ let mtimeMs;
376
+ try {
377
+ const st = await fs.promises.stat(MCP_YAML_PATH);
378
+ mtimeMs = st.mtimeMs;
379
+ }
380
+ catch (err) {
381
+ const code = isRecord(err) && 'code' in err ? err.code : undefined;
382
+ if (code === 'ENOENT') {
383
+ mtimeMs = 0;
384
+ }
385
+ else {
386
+ return;
387
+ }
388
+ }
389
+ if (lastSeenMtimeMs === undefined || mtimeMs !== lastSeenMtimeMs) {
390
+ lastSeenMtimeMs = mtimeMs;
391
+ scheduleReload('poll');
392
+ }
393
+ }
394
+ async function reloadNow(reason) {
395
+ let rawText;
396
+ try {
397
+ rawText = await fs.promises.readFile(MCP_YAML_PATH, 'utf8');
398
+ }
399
+ catch (err) {
400
+ const code = isRecord(err) && 'code' in err ? err.code : undefined;
401
+ if (code === 'ENOENT') {
402
+ // Deletion is treated as empty config.
403
+ await applyWorkspaceConfig({ version: 1, servers: {} }, [], [], [], `missing file (${reason})`);
404
+ clearWorkspaceConfigProblem();
405
+ return;
406
+ }
407
+ upsertWorkspaceConfigProblem(`Failed to read ${MCP_YAML_PATH}: ${String(err)}`);
408
+ return;
409
+ }
410
+ const parsed = (0, config_1.parseMcpYaml)(rawText);
411
+ if (!parsed.ok) {
412
+ upsertWorkspaceConfigProblem(parsed.errorText);
413
+ return;
414
+ }
415
+ clearWorkspaceConfigProblem();
416
+ await applyWorkspaceConfig(parsed.config, parsed.invalidServers, parsed.serverIdsInYamlOrder, parsed.validServerIdsInYamlOrder, reason);
417
+ }
418
+ async function restartServerNow(serverId) {
419
+ let rawText;
420
+ try {
421
+ rawText = await fs.promises.readFile(MCP_YAML_PATH, 'utf8');
422
+ }
423
+ catch (err) {
424
+ const code = isRecord(err) && 'code' in err ? err.code : undefined;
425
+ if (code === 'ENOENT') {
426
+ // Deletion is treated as empty config, so restart cannot proceed.
427
+ clearWorkspaceConfigProblem();
428
+ return { ok: false, errorText: `Cannot restart '${serverId}': ${MCP_YAML_PATH} is missing` };
429
+ }
430
+ upsertWorkspaceConfigProblem(`Failed to read ${MCP_YAML_PATH}: ${String(err)}`);
431
+ return { ok: false, errorText: `Failed to read ${MCP_YAML_PATH}: ${String(err)}` };
432
+ }
433
+ const parsed = (0, config_1.parseMcpYaml)(rawText);
434
+ if (!parsed.ok) {
435
+ upsertWorkspaceConfigProblem(parsed.errorText);
436
+ return { ok: false, errorText: parsed.errorText };
437
+ }
438
+ clearWorkspaceConfigProblem();
439
+ const invalid = parsed.invalidServers.find((s) => s.serverId === serverId);
440
+ if (invalid) {
441
+ (0, problems_1.upsertProblem)({
442
+ kind: 'mcp_server_error',
443
+ source: 'mcp',
444
+ id: `${problemPrefixForServer(serverId)}server_error`,
445
+ severity: 'error',
446
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
447
+ message: `MCP server '${serverId}' failed to parse config`,
448
+ detail: { serverId, errorText: invalid.errorText },
449
+ });
450
+ return { ok: false, errorText: invalid.errorText };
451
+ }
452
+ const serverCfg = parsed.config.servers[serverId];
453
+ if (!serverCfg) {
454
+ return {
455
+ ok: false,
456
+ errorText: `MCP server '${serverId}' is not configured in ${MCP_YAML_PATH}`,
457
+ };
458
+ }
459
+ const desiredToolsetName = serverId;
460
+ const fingerprint = fingerprintServerConfig(serverCfg);
461
+ const existing = serverStateById.get(serverId);
462
+ const res = await tryBuildServerState(serverCfg, desiredToolsetName, fingerprint);
463
+ if (!res.ok) {
464
+ (0, problems_1.upsertProblem)({
465
+ kind: 'mcp_server_error',
466
+ source: 'mcp',
467
+ id: `${problemPrefixForServer(serverId)}server_error`,
468
+ severity: 'error',
469
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
470
+ message: `MCP server '${serverId}' failed to (re)start`,
471
+ detail: { serverId, errorText: res.errorText },
472
+ });
473
+ return { ok: false, errorText: res.errorText };
474
+ }
475
+ (0, problems_1.removeProblemsByPrefix)(`${problemPrefixForServer(serverId)}server_error`);
476
+ if (existing) {
477
+ unregisterServer(existing);
478
+ existing.dispatch.requestStop();
479
+ }
480
+ registerServer(res.state);
481
+ serverStateById.set(serverId, res.state);
482
+ (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), res.state.problems);
483
+ reorderMcpToolsetsInRegistry(parsed.serverIdsInYamlOrder);
484
+ return { ok: true };
485
+ }
486
+ function upsertWorkspaceConfigProblem(errorText) {
487
+ (0, problems_1.upsertProblem)({
488
+ kind: 'mcp_workspace_config_error',
489
+ source: 'mcp',
490
+ id: 'mcp/workspace_config_error',
491
+ severity: 'error',
492
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
493
+ message: 'MCP workspace config error',
494
+ detail: { filePath: MCP_YAML_PATH, errorText },
495
+ });
496
+ }
497
+ function clearWorkspaceConfigProblem() {
498
+ (0, problems_1.removeProblemsByPrefix)('mcp/workspace_config_error');
499
+ }
500
+ async function applyWorkspaceConfig(config, invalidServers, serverIdsInYamlOrder, validServerIdsInYamlOrder, reason) {
501
+ log.info(`Applying MCP workspace config (${reason})`);
502
+ const invalidIds = new Set(invalidServers.map((s) => s.serverId));
503
+ const desiredIds = new Set([...Object.keys(config.servers), ...invalidIds]);
504
+ // Remove deleted servers first.
505
+ for (const [serverId, state] of serverStateById.entries()) {
506
+ if (desiredIds.has(serverId))
507
+ continue;
508
+ unregisterServer(state);
509
+ state.dispatch.requestStop();
510
+ serverStateById.delete(serverId);
511
+ (0, problems_1.removeProblemsByPrefix)(problemPrefixForServer(serverId));
512
+ }
513
+ // Surface invalid server config errors (while keeping last-known-good runtimes registered).
514
+ for (const s of invalidServers) {
515
+ (0, problems_1.upsertProblem)({
516
+ kind: 'mcp_server_error',
517
+ source: 'mcp',
518
+ id: `${problemPrefixForServer(s.serverId)}server_error`,
519
+ severity: 'error',
520
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
521
+ message: `MCP server '${s.serverId}' failed to parse config`,
522
+ detail: { serverId: s.serverId, errorText: s.errorText },
523
+ });
524
+ }
525
+ // Apply desired servers independently (deterministic order).
526
+ for (const serverId of validServerIdsInYamlOrder) {
527
+ const serverCfg = config.servers[serverId];
528
+ if (!serverCfg)
529
+ continue;
530
+ const desiredToolsetName = serverId;
531
+ const fingerprint = fingerprintServerConfig(serverCfg);
532
+ const existing = serverStateById.get(serverId);
533
+ if (!existing || existing.configFingerprint !== fingerprint) {
534
+ const res = await tryBuildServerState(serverCfg, desiredToolsetName, fingerprint);
535
+ if (!res.ok) {
536
+ // Keep last-known-good registration, but surface per-server error.
537
+ (0, problems_1.upsertProblem)({
538
+ kind: 'mcp_server_error',
539
+ source: 'mcp',
540
+ id: `${problemPrefixForServer(serverId)}server_error`,
541
+ severity: 'error',
542
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
543
+ message: `MCP server '${serverId}' failed to (re)load`,
544
+ detail: { serverId, errorText: res.errorText },
545
+ });
546
+ continue;
547
+ }
548
+ // Successful start: replace old runtime/tools.
549
+ (0, problems_1.removeProblemsByPrefix)(`${problemPrefixForServer(serverId)}server_error`);
550
+ if (existing) {
551
+ unregisterServer(existing);
552
+ existing.dispatch.requestStop();
553
+ }
554
+ registerServer(res.state);
555
+ serverStateById.set(serverId, res.state);
556
+ (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), res.state.problems);
557
+ continue;
558
+ }
559
+ // Config unchanged, but recompute desired tool registration to:
560
+ // - auto-clear disappeared problems
561
+ // - re-attempt registering tools that were previously skipped due to collisions
562
+ (0, problems_1.removeProblemsByPrefix)(`${problemPrefixForServer(serverId)}server_error`);
563
+ const rebuilt = buildToolsForServer(serverCfg, existing.dispatch, existing.listedTools);
564
+ const next = {
565
+ serverId,
566
+ toolsetName: desiredToolsetName,
567
+ configFingerprint: fingerprint,
568
+ cfg: existing.cfg,
569
+ listedTools: existing.listedTools,
570
+ dispatch: existing.dispatch,
571
+ tools: rebuilt.tools,
572
+ ownedToolNames: rebuilt.ownedToolNames,
573
+ problems: rebuilt.problems,
574
+ };
575
+ const sameTools = setsEqual(existing.ownedToolNames, next.ownedToolNames);
576
+ if (!sameTools) {
577
+ unregisterServer(existing);
578
+ registerServer(next);
579
+ serverStateById.set(serverId, next);
580
+ }
581
+ (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), next.problems);
582
+ }
583
+ // Second pass: after all adds/updates, re-run collision resolution so earlier servers can pick up
584
+ // tools that become available due to later server changes in the same reload cycle.
585
+ for (let i = 0; i < 2; i++) {
586
+ const changed = reconcileCollisionDependentRegistrations(config, validServerIdsInYamlOrder);
587
+ if (!changed)
588
+ break;
589
+ }
590
+ reorderMcpToolsetsInRegistry(serverIdsInYamlOrder);
591
+ }
592
+ function setsEqual(a, b) {
593
+ if (a.size !== b.size)
594
+ return false;
595
+ for (const v of a) {
596
+ if (!b.has(v))
597
+ return false;
598
+ }
599
+ return true;
600
+ }
601
+ function reconcileCollisionDependentRegistrations(config, serverIds) {
602
+ let changedAny = false;
603
+ for (const serverId of serverIds) {
604
+ const serverCfg = config.servers[serverId];
605
+ if (!serverCfg)
606
+ continue;
607
+ const fingerprint = fingerprintServerConfig(serverCfg);
608
+ const existing = serverStateById.get(serverId);
609
+ if (!existing)
610
+ continue;
611
+ if (existing.configFingerprint !== fingerprint)
612
+ continue; // handled by spawn/update path
613
+ const rebuilt = buildToolsForServer(serverCfg, existing.dispatch, existing.listedTools);
614
+ const next = {
615
+ serverId,
616
+ toolsetName: existing.toolsetName,
617
+ configFingerprint: fingerprint,
618
+ cfg: existing.cfg,
619
+ listedTools: existing.listedTools,
620
+ dispatch: existing.dispatch,
621
+ tools: rebuilt.tools,
622
+ ownedToolNames: rebuilt.ownedToolNames,
623
+ problems: rebuilt.problems,
624
+ };
625
+ const sameTools = setsEqual(existing.ownedToolNames, next.ownedToolNames);
626
+ if (!sameTools) {
627
+ unregisterServer(existing);
628
+ registerServer(next);
629
+ serverStateById.set(serverId, next);
630
+ changedAny = true;
631
+ }
632
+ (0, problems_1.reconcileProblemsByPrefix)(problemPrefixForServer(serverId), next.problems);
633
+ }
634
+ return changedAny;
635
+ }
636
+ function registerServer(state) {
637
+ for (const t of state.tools) {
638
+ (0, registry_1.registerTool)(t);
639
+ toolOwnerByName.set(t.name, { kind: 'mcp', serverId: state.serverId });
640
+ }
641
+ (0, registry_1.registerToolset)(state.toolsetName, state.tools);
642
+ (0, registry_1.setToolsetMeta)(state.toolsetName, {
643
+ source: 'mcp',
644
+ descriptionI18n: {
645
+ en: `MCP server: ${state.serverId}`,
646
+ zh: `MCP 服务器:${state.serverId}`,
647
+ },
648
+ });
649
+ toolsetOwnerByName.set(state.toolsetName, { kind: 'mcp', serverId: state.serverId });
650
+ }
651
+ function unregisterServer(state) {
652
+ // Unregister toolset first so Team.Member.listTools doesn't resolve stale tools.
653
+ if (toolsetOwnerByName.get(state.toolsetName)?.serverId === state.serverId) {
654
+ (0, registry_1.unregisterToolset)(state.toolsetName);
655
+ toolsetOwnerByName.delete(state.toolsetName);
656
+ }
657
+ for (const toolName of state.ownedToolNames) {
658
+ if (toolOwnerByName.get(toolName)?.serverId !== state.serverId)
659
+ continue;
660
+ (0, registry_1.unregisterTool)(toolName);
661
+ toolOwnerByName.delete(toolName);
662
+ }
663
+ }
664
+ function reorderMcpToolsetsInRegistry(serverIdsInYamlOrder) {
665
+ const desiredToolsetNames = serverIdsInYamlOrder;
666
+ const currentEntries = Array.from(registry_1.toolsetsRegistry.entries());
667
+ const nonMcpEntries = [];
668
+ const mcpToolsetsByName = new Map();
669
+ const mcpToolsetNamesInCurrentOrder = [];
670
+ for (const [toolsetName, tools] of currentEntries) {
671
+ const owner = toolsetOwnerByName.get(toolsetName);
672
+ if (owner?.kind === 'mcp') {
673
+ mcpToolsetsByName.set(toolsetName, tools);
674
+ mcpToolsetNamesInCurrentOrder.push(toolsetName);
675
+ continue;
676
+ }
677
+ nonMcpEntries.push([toolsetName, tools]);
678
+ }
679
+ const reordered = [...nonMcpEntries];
680
+ const placed = new Set();
681
+ for (const toolsetName of desiredToolsetNames) {
682
+ const tools = mcpToolsetsByName.get(toolsetName);
683
+ if (!tools)
684
+ continue;
685
+ reordered.push([toolsetName, tools]);
686
+ placed.add(toolsetName);
687
+ }
688
+ for (const toolsetName of mcpToolsetNamesInCurrentOrder) {
689
+ if (placed.has(toolsetName))
690
+ continue;
691
+ const tools = mcpToolsetsByName.get(toolsetName);
692
+ if (!tools)
693
+ continue;
694
+ reordered.push([toolsetName, tools]);
695
+ }
696
+ registry_1.toolsetsRegistry.clear();
697
+ for (const [toolsetName, tools] of reordered) {
698
+ registry_1.toolsetsRegistry.set(toolsetName, tools);
699
+ }
700
+ }
701
+ async function tryBuildServerState(cfg, toolsetName, fingerprint) {
702
+ const serverId = cfg.serverId;
703
+ // Toolset-name collisions should prevent committing this server.
704
+ const existingToolset = registry_1.toolsetsRegistry.get(toolsetName);
705
+ const existingOwner = toolsetOwnerByName.get(toolsetName);
706
+ if (existingToolset && (!existingOwner || existingOwner.serverId !== serverId)) {
707
+ return { ok: false, errorText: `Toolset name collision: ${toolsetName}` };
708
+ }
709
+ let client;
710
+ let sharedRuntime;
711
+ try {
712
+ client =
713
+ cfg.transport === 'stdio'
714
+ ? await sdk_client_1.McpSdkClient.connectStdio({
715
+ serverId,
716
+ command: cfg.command,
717
+ args: cfg.args,
718
+ env: buildChildEnv(cfg, serverId),
719
+ cwd: process.cwd(),
720
+ })
721
+ : await sdk_client_1.McpSdkClient.connectStreamableHttp({
722
+ serverId,
723
+ url: cfg.url,
724
+ headers: buildHttpHeaders(cfg, serverId),
725
+ sessionId: cfg.sessionId,
726
+ });
727
+ const listedTools = await client.listTools();
728
+ if (cfg.truelyStateless) {
729
+ sharedRuntime = new server_runtime_1.McpServerRuntime({ serverId, toolsetName, client });
730
+ client = undefined;
731
+ }
732
+ else {
733
+ await client.close();
734
+ client = undefined;
735
+ }
736
+ const dispatch = new McpServerDispatch({ serverId, toolsetName, cfg, sharedRuntime });
737
+ const build = buildToolsForServer(cfg, dispatch, listedTools);
738
+ const state = {
739
+ serverId,
740
+ toolsetName,
741
+ configFingerprint: fingerprint,
742
+ cfg,
743
+ listedTools,
744
+ dispatch,
745
+ tools: build.tools,
746
+ ownedToolNames: build.ownedToolNames,
747
+ problems: build.problems,
748
+ };
749
+ return { ok: true, state };
750
+ }
751
+ catch (err) {
752
+ if (sharedRuntime) {
753
+ sharedRuntime.requestStop({ forceKillAfterMs: 3000 });
754
+ }
755
+ if (client) {
756
+ try {
757
+ await client.close();
758
+ }
759
+ catch {
760
+ // best-effort
761
+ }
762
+ }
763
+ return { ok: false, errorText: err instanceof Error ? err.message : String(err) };
764
+ }
765
+ }
766
+ function buildToolsForServer(cfg, dispatch, listedTools) {
767
+ const serverId = cfg.serverId;
768
+ const problems = [];
769
+ const tools = [];
770
+ const ownedToolNames = new Set();
771
+ const seenDomindsNames = new Set();
772
+ for (const tool of listedTools) {
773
+ const originalName = tool.name;
774
+ if (!(0, tool_names_1.isValidProviderToolName)(originalName)) {
775
+ log.warn(`Rejecting MCP tool with invalid name`, undefined, {
776
+ serverId,
777
+ toolName: originalName,
778
+ });
779
+ problems.push({
780
+ kind: 'mcp_tool_invalid_name',
781
+ source: 'mcp',
782
+ id: `${problemPrefixForServer(serverId)}tool_invalid_name/${originalName}`,
783
+ severity: 'warning',
784
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
785
+ message: `MCP tool name is invalid and was rejected`,
786
+ detail: { serverId, toolName: originalName, rule: tool_names_1.TOOL_NAME_VALIDITY_RULE },
787
+ });
788
+ continue;
789
+ }
790
+ const exposure = (0, tool_names_1.decideToolExposure)(originalName, cfg.tools);
791
+ if (exposure.kind === 'blacklisted') {
792
+ log.warn(`MCP tool not registered (blacklisted)`, undefined, {
793
+ serverId,
794
+ toolName: originalName,
795
+ });
796
+ problems.push({
797
+ kind: 'mcp_tool_blacklisted',
798
+ source: 'mcp',
799
+ id: `${problemPrefixForServer(serverId)}tool_blacklisted/${originalName}`,
800
+ severity: 'warning',
801
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
802
+ message: `MCP tool was excluded by blacklist`,
803
+ detail: { serverId, toolName: originalName, pattern: exposure.pattern },
804
+ });
805
+ continue;
806
+ }
807
+ if (exposure.kind === 'not_whitelisted') {
808
+ log.warn(`MCP tool not registered (not whitelisted)`, undefined, {
809
+ serverId,
810
+ toolName: originalName,
811
+ });
812
+ problems.push({
813
+ kind: 'mcp_tool_not_whitelisted',
814
+ source: 'mcp',
815
+ id: `${problemPrefixForServer(serverId)}tool_not_whitelisted/${originalName}`,
816
+ severity: 'info',
817
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
818
+ message: `MCP tool was excluded by whitelist-only mode`,
819
+ detail: { serverId, toolName: originalName, pattern: exposure.pattern },
820
+ });
821
+ continue;
822
+ }
823
+ const domindsName = (0, tool_names_1.applyToolNameTransforms)(originalName, cfg.transform);
824
+ if (!(0, tool_names_1.isValidProviderToolName)(domindsName)) {
825
+ log.warn(`Rejecting MCP tool after transforms due to invalid Dominds tool name`, undefined, {
826
+ serverId,
827
+ toolName: originalName,
828
+ domindsToolName: domindsName,
829
+ });
830
+ problems.push({
831
+ kind: 'mcp_tool_invalid_name',
832
+ source: 'mcp',
833
+ id: `${problemPrefixForServer(serverId)}tool_invalid_name/${originalName}/transformed`,
834
+ severity: 'warning',
835
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
836
+ message: `Dominds tool name produced by transforms is invalid and was rejected`,
837
+ detail: { serverId, toolName: domindsName, rule: tool_names_1.TOOL_NAME_VALIDITY_RULE },
838
+ });
839
+ continue;
840
+ }
841
+ if (seenDomindsNames.has(domindsName)) {
842
+ log.warn(`Skipping MCP tool due to within-server name collision`, undefined, {
843
+ serverId,
844
+ toolName: originalName,
845
+ domindsToolName: domindsName,
846
+ });
847
+ problems.push({
848
+ kind: 'mcp_tool_collision',
849
+ source: 'mcp',
850
+ id: `${problemPrefixForServer(serverId)}tool_collision/${domindsName}`,
851
+ severity: 'warning',
852
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
853
+ message: `MCP tool name collision (after transforms)`,
854
+ detail: { serverId, toolName: originalName, domindsToolName: domindsName },
855
+ });
856
+ continue;
857
+ }
858
+ const existingTool = registry_1.toolsRegistry.get(domindsName);
859
+ const existingOwner = toolOwnerByName.get(domindsName);
860
+ const canReplace = existingOwner?.serverId === serverId;
861
+ if (existingTool && !canReplace) {
862
+ log.warn(`Skipping MCP tool due to global name collision`, undefined, {
863
+ serverId,
864
+ toolName: originalName,
865
+ domindsToolName: domindsName,
866
+ });
867
+ problems.push({
868
+ kind: 'mcp_tool_collision',
869
+ source: 'mcp',
870
+ id: `${problemPrefixForServer(serverId)}tool_collision/${domindsName}`,
871
+ severity: 'warning',
872
+ timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
873
+ message: `MCP tool name collision with existing tool`,
874
+ detail: { serverId, toolName: originalName, domindsToolName: domindsName },
875
+ });
876
+ continue;
877
+ }
878
+ const func = {
879
+ type: 'func',
880
+ name: domindsName,
881
+ description: tool.description,
882
+ parameters: tool.inputSchema,
883
+ argsValidation: 'passthrough',
884
+ call: async (dlg, _caller, args) => {
885
+ return await dispatch.callToolForDialog(dlg, originalName, args);
886
+ },
887
+ };
888
+ tools.push(func);
889
+ ownedToolNames.add(domindsName);
890
+ seenDomindsNames.add(domindsName);
891
+ }
892
+ return { tools, ownedToolNames, problems };
893
+ }
894
+ function buildChildEnv(cfg, serverId) {
895
+ if (cfg.transport !== 'stdio') {
896
+ return {};
897
+ }
898
+ const env = {};
899
+ for (const [k, v] of Object.entries(process.env)) {
900
+ if (typeof v === 'string')
901
+ env[k] = v;
902
+ }
903
+ for (const [k, v] of Object.entries(cfg.env)) {
904
+ switch (v.kind) {
905
+ case 'literal':
906
+ env[k] = v.value;
907
+ break;
908
+ case 'from_env': {
909
+ const val = process.env[v.env];
910
+ if (val === undefined) {
911
+ throw new Error(`MCP server '${serverId}' missing required host env var '${v.env}' (for env.${k})`);
912
+ }
913
+ env[k] = val;
914
+ break;
915
+ }
916
+ default: {
917
+ const _exhaustive = v;
918
+ throw new Error(`Invalid env mapping kind: ${String(_exhaustive)}`);
919
+ }
920
+ }
921
+ }
922
+ return env;
923
+ }
924
+ function fingerprintServerConfig(cfg) {
925
+ const obj = cfg.transport === 'stdio'
926
+ ? {
927
+ truelyStateless: cfg.truelyStateless,
928
+ transport: cfg.transport,
929
+ command: cfg.command,
930
+ args: cfg.args,
931
+ env: sortedEntries(cfg.env),
932
+ tools: cfg.tools,
933
+ transform: cfg.transform,
934
+ }
935
+ : {
936
+ truelyStateless: cfg.truelyStateless,
937
+ transport: cfg.transport,
938
+ url: cfg.url,
939
+ headers: sortedEntries(cfg.headers),
940
+ sessionId: cfg.sessionId ?? null,
941
+ tools: cfg.tools,
942
+ transform: cfg.transform,
943
+ };
944
+ return JSON.stringify(obj);
945
+ }
946
+ function problemPrefixForServer(serverId) {
947
+ return `mcp/server/${serverId}/`;
948
+ }
949
+ function isRecord(value) {
950
+ return typeof value === 'object' && value !== null;
951
+ }
952
+ function buildHttpHeaders(cfg, serverId) {
953
+ const headers = {};
954
+ for (const [k, v] of Object.entries(cfg.headers)) {
955
+ switch (v.kind) {
956
+ case 'literal':
957
+ headers[k] = v.value;
958
+ break;
959
+ case 'from_env': {
960
+ const val = process.env[v.env];
961
+ if (val === undefined) {
962
+ throw new Error(`MCP server '${serverId}' missing required host env var '${v.env}' (for headers.${k})`);
963
+ }
964
+ headers[k] = val;
965
+ break;
966
+ }
967
+ default: {
968
+ const _exhaustive = v;
969
+ throw new Error(`Invalid header mapping kind: ${String(_exhaustive)}`);
970
+ }
971
+ }
972
+ }
973
+ return headers;
974
+ }
975
+ function sortedEntries(obj) {
976
+ return Object.entries(obj)
977
+ .map(([k, v]) => [k, v])
978
+ .sort(([a], [b]) => a.localeCompare(b));
979
+ }