mindweave 0.0.1 → 2.1.2

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 (481) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +171 -14
  3. package/dist/alternator/chassis/cache.js +47 -0
  4. package/dist/alternator/chassis/cache.js.map +1 -0
  5. package/dist/alternator/chassis/domRefs.js +53 -0
  6. package/dist/alternator/chassis/domRefs.js.map +1 -0
  7. package/dist/alternator/chassis/extractWorker.js +65 -0
  8. package/dist/alternator/chassis/extractWorker.js.map +1 -0
  9. package/dist/alternator/chassis/graph.js +221 -0
  10. package/dist/alternator/chassis/graph.js.map +1 -0
  11. package/dist/alternator/chassis/index.js +463 -0
  12. package/dist/alternator/chassis/index.js.map +1 -0
  13. package/dist/alternator/chassis/isolation.js +336 -0
  14. package/dist/alternator/chassis/isolation.js.map +1 -0
  15. package/dist/alternator/chassis/langCases.js +69 -0
  16. package/dist/alternator/chassis/langCases.js.map +1 -0
  17. package/dist/alternator/chassis/lsp.js +469 -0
  18. package/dist/alternator/chassis/lsp.js.map +1 -0
  19. package/dist/alternator/chassis/markup.js +262 -0
  20. package/dist/alternator/chassis/markup.js.map +1 -0
  21. package/dist/alternator/chassis/provision.js +198 -0
  22. package/dist/alternator/chassis/provision.js.map +1 -0
  23. package/dist/alternator/chassis/rank.js +72 -0
  24. package/dist/alternator/chassis/rank.js.map +1 -0
  25. package/dist/alternator/chassis/servers.js +215 -0
  26. package/dist/alternator/chassis/servers.js.map +1 -0
  27. package/dist/alternator/chassis/treesitter.js +608 -0
  28. package/dist/alternator/chassis/treesitter.js.map +1 -0
  29. package/dist/alternator/chassis/types.js +24 -0
  30. package/dist/alternator/chassis/types.js.map +1 -0
  31. package/dist/alternator/lane.js +67 -0
  32. package/dist/alternator/lane.js.map +1 -0
  33. package/dist/cli/App.js +2586 -0
  34. package/dist/cli/App.js.map +1 -0
  35. package/dist/cli/altScreen.js +67 -0
  36. package/dist/cli/altScreen.js.map +1 -0
  37. package/dist/cli/approvalChannel.js +72 -0
  38. package/dist/cli/approvalChannel.js.map +1 -0
  39. package/dist/cli/attachments.js +188 -0
  40. package/dist/cli/attachments.js.map +1 -0
  41. package/dist/cli/bootstrap.js +279 -0
  42. package/dist/cli/bootstrap.js.map +1 -0
  43. package/dist/cli/chatAnchor.js +94 -0
  44. package/dist/cli/chatAnchor.js.map +1 -0
  45. package/dist/cli/commandArgs.js +79 -0
  46. package/dist/cli/commandArgs.js.map +1 -0
  47. package/dist/cli/commandRoute.js +78 -0
  48. package/dist/cli/commandRoute.js.map +1 -0
  49. package/dist/cli/compaction.js +69 -0
  50. package/dist/cli/compaction.js.map +1 -0
  51. package/dist/cli/components/ApprovalBox.js +100 -0
  52. package/dist/cli/components/ApprovalBox.js.map +1 -0
  53. package/dist/cli/components/BlockView.js +128 -0
  54. package/dist/cli/components/BlockView.js.map +1 -0
  55. package/dist/cli/components/FirstRunFrame.js +41 -0
  56. package/dist/cli/components/FirstRunFrame.js.map +1 -0
  57. package/dist/cli/components/KeyManager.js +197 -0
  58. package/dist/cli/components/KeyManager.js.map +1 -0
  59. package/dist/cli/components/KeySetup.js +125 -0
  60. package/dist/cli/components/KeySetup.js.map +1 -0
  61. package/dist/cli/components/MenuFrame.js +24 -0
  62. package/dist/cli/components/MenuFrame.js.map +1 -0
  63. package/dist/cli/components/Picker.js +82 -0
  64. package/dist/cli/components/Picker.js.map +1 -0
  65. package/dist/cli/components/PromptInput.js +509 -0
  66. package/dist/cli/components/PromptInput.js.map +1 -0
  67. package/dist/cli/components/SubagentView.js +86 -0
  68. package/dist/cli/components/SubagentView.js.map +1 -0
  69. package/dist/cli/components/ToolGroup.js +60 -0
  70. package/dist/cli/components/ToolGroup.js.map +1 -0
  71. package/dist/cli/components/ToolLine.js +123 -0
  72. package/dist/cli/components/ToolLine.js.map +1 -0
  73. package/dist/cli/components/TrustGate.js +37 -0
  74. package/dist/cli/components/TrustGate.js.map +1 -0
  75. package/dist/cli/costView.js +40 -0
  76. package/dist/cli/costView.js.map +1 -0
  77. package/dist/cli/frameRate.js +30 -0
  78. package/dist/cli/frameRate.js.map +1 -0
  79. package/dist/cli/framebuffer/paint.js +165 -0
  80. package/dist/cli/framebuffer/paint.js.map +1 -0
  81. package/dist/cli/framebuffer/parse.js +266 -0
  82. package/dist/cli/framebuffer/parse.js.map +1 -0
  83. package/dist/cli/framebuffer/screen.js +196 -0
  84. package/dist/cli/framebuffer/screen.js.map +1 -0
  85. package/dist/cli/framebuffer/writer.js +139 -0
  86. package/dist/cli/framebuffer/writer.js.map +1 -0
  87. package/dist/cli/groupReveal.js +51 -0
  88. package/dist/cli/groupReveal.js.map +1 -0
  89. package/dist/cli/help.js +55 -0
  90. package/dist/cli/help.js.map +1 -0
  91. package/dist/cli/inputView.js +105 -0
  92. package/dist/cli/inputView.js.map +1 -0
  93. package/dist/cli/keyManager.js +65 -0
  94. package/dist/cli/keyManager.js.map +1 -0
  95. package/dist/cli/keySetup.js +35 -0
  96. package/dist/cli/keySetup.js.map +1 -0
  97. package/dist/cli/keyStore.js +73 -0
  98. package/dist/cli/keyStore.js.map +1 -0
  99. package/dist/cli/markdown.js +460 -0
  100. package/dist/cli/markdown.js.map +1 -0
  101. package/dist/cli/messageQueue.js +96 -0
  102. package/dist/cli/messageQueue.js.map +1 -0
  103. package/dist/cli/modes.js +90 -0
  104. package/dist/cli/modes.js.map +1 -0
  105. package/dist/cli/mouse.js +78 -0
  106. package/dist/cli/mouse.js.map +1 -0
  107. package/dist/cli/pasteAssembler.js +44 -0
  108. package/dist/cli/pasteAssembler.js.map +1 -0
  109. package/dist/cli/pathComplete.js +39 -0
  110. package/dist/cli/pathComplete.js.map +1 -0
  111. package/dist/cli/perfLog.js +90 -0
  112. package/dist/cli/perfLog.js.map +1 -0
  113. package/dist/cli/reveal.js +32 -0
  114. package/dist/cli/reveal.js.map +1 -0
  115. package/dist/cli/revealPace.js +59 -0
  116. package/dist/cli/revealPace.js.map +1 -0
  117. package/dist/cli/sessionCarry.js +19 -0
  118. package/dist/cli/sessionCarry.js.map +1 -0
  119. package/dist/cli/smoothScroll.js +74 -0
  120. package/dist/cli/smoothScroll.js.map +1 -0
  121. package/dist/cli/startupArgs.js +96 -0
  122. package/dist/cli/startupArgs.js.map +1 -0
  123. package/dist/cli/terminalRestore.js +34 -0
  124. package/dist/cli/terminalRestore.js.map +1 -0
  125. package/dist/cli/toolDisplay.js +219 -0
  126. package/dist/cli/toolDisplay.js.map +1 -0
  127. package/dist/cli/toolItems.js +65 -0
  128. package/dist/cli/toolItems.js.map +1 -0
  129. package/dist/cli/transcript.js +351 -0
  130. package/dist/cli/transcript.js.map +1 -0
  131. package/dist/cli/trust.js +79 -0
  132. package/dist/cli/trust.js.map +1 -0
  133. package/dist/cli/updateCheck.js +107 -0
  134. package/dist/cli/updateCheck.js.map +1 -0
  135. package/dist/cli/version.js +39 -0
  136. package/dist/cli/version.js.map +1 -0
  137. package/dist/cli/viewport.js +179 -0
  138. package/dist/cli/viewport.js.map +1 -0
  139. package/dist/cli/virtualWindow.js +103 -0
  140. package/dist/cli/virtualWindow.js.map +1 -0
  141. package/dist/cli/workingVerb.js +37 -0
  142. package/dist/cli/workingVerb.js.map +1 -0
  143. package/dist/cli/wrap.js +145 -0
  144. package/dist/cli/wrap.js.map +1 -0
  145. package/dist/drivers/anthropic/cachePoints.js +77 -0
  146. package/dist/drivers/anthropic/cachePoints.js.map +1 -0
  147. package/dist/drivers/anthropic/client.js +432 -0
  148. package/dist/drivers/anthropic/client.js.map +1 -0
  149. package/dist/drivers/anthropic/index.js +10 -0
  150. package/dist/drivers/anthropic/index.js.map +1 -0
  151. package/dist/drivers/anthropic/manifest.js +221 -0
  152. package/dist/drivers/anthropic/manifest.js.map +1 -0
  153. package/dist/drivers/cerebras/client.js +69 -0
  154. package/dist/drivers/cerebras/client.js.map +1 -0
  155. package/dist/drivers/cerebras/index.js +5 -0
  156. package/dist/drivers/cerebras/index.js.map +1 -0
  157. package/dist/drivers/cerebras/manifest.js +140 -0
  158. package/dist/drivers/cerebras/manifest.js.map +1 -0
  159. package/dist/drivers/clientId.js +41 -0
  160. package/dist/drivers/clientId.js.map +1 -0
  161. package/dist/drivers/contextOverflow.js +77 -0
  162. package/dist/drivers/contextOverflow.js.map +1 -0
  163. package/dist/drivers/deepseek/client.js +168 -0
  164. package/dist/drivers/deepseek/client.js.map +1 -0
  165. package/dist/drivers/deepseek/index.js +16 -0
  166. package/dist/drivers/deepseek/index.js.map +1 -0
  167. package/dist/drivers/deepseek/inlineTools.js +46 -0
  168. package/dist/drivers/deepseek/inlineTools.js.map +1 -0
  169. package/dist/drivers/deepseek/manifest.js +166 -0
  170. package/dist/drivers/deepseek/manifest.js.map +1 -0
  171. package/dist/drivers/gemini/client.js +38 -0
  172. package/dist/drivers/gemini/client.js.map +1 -0
  173. package/dist/drivers/gemini/index.js +5 -0
  174. package/dist/drivers/gemini/index.js.map +1 -0
  175. package/dist/drivers/gemini/manifest.js +150 -0
  176. package/dist/drivers/gemini/manifest.js.map +1 -0
  177. package/dist/drivers/glm/client.js +90 -0
  178. package/dist/drivers/glm/client.js.map +1 -0
  179. package/dist/drivers/glm/index.js +9 -0
  180. package/dist/drivers/glm/index.js.map +1 -0
  181. package/dist/drivers/glm/manifest.js +183 -0
  182. package/dist/drivers/glm/manifest.js.map +1 -0
  183. package/dist/drivers/groq/client.js +80 -0
  184. package/dist/drivers/groq/client.js.map +1 -0
  185. package/dist/drivers/groq/index.js +5 -0
  186. package/dist/drivers/groq/index.js.map +1 -0
  187. package/dist/drivers/groq/manifest.js +160 -0
  188. package/dist/drivers/groq/manifest.js.map +1 -0
  189. package/dist/drivers/kimi/client.js +82 -0
  190. package/dist/drivers/kimi/client.js.map +1 -0
  191. package/dist/drivers/kimi/index.js +9 -0
  192. package/dist/drivers/kimi/index.js.map +1 -0
  193. package/dist/drivers/kimi/manifest.js +168 -0
  194. package/dist/drivers/kimi/manifest.js.map +1 -0
  195. package/dist/drivers/meta/client.js +30 -0
  196. package/dist/drivers/meta/client.js.map +1 -0
  197. package/dist/drivers/meta/index.js +5 -0
  198. package/dist/drivers/meta/index.js.map +1 -0
  199. package/dist/drivers/meta/manifest.js +101 -0
  200. package/dist/drivers/meta/manifest.js.map +1 -0
  201. package/dist/drivers/minimax/client.js +36 -0
  202. package/dist/drivers/minimax/client.js.map +1 -0
  203. package/dist/drivers/minimax/index.js +5 -0
  204. package/dist/drivers/minimax/index.js.map +1 -0
  205. package/dist/drivers/minimax/manifest.js +120 -0
  206. package/dist/drivers/minimax/manifest.js.map +1 -0
  207. package/dist/drivers/mistral/client.js +54 -0
  208. package/dist/drivers/mistral/client.js.map +1 -0
  209. package/dist/drivers/mistral/index.js +5 -0
  210. package/dist/drivers/mistral/index.js.map +1 -0
  211. package/dist/drivers/mistral/manifest.js +118 -0
  212. package/dist/drivers/mistral/manifest.js.map +1 -0
  213. package/dist/drivers/openai/client.js +290 -0
  214. package/dist/drivers/openai/client.js.map +1 -0
  215. package/dist/drivers/openai/index.js +9 -0
  216. package/dist/drivers/openai/index.js.map +1 -0
  217. package/dist/drivers/openai/manifest.js +133 -0
  218. package/dist/drivers/openai/manifest.js.map +1 -0
  219. package/dist/drivers/openaiCompat/wire.js +493 -0
  220. package/dist/drivers/openaiCompat/wire.js.map +1 -0
  221. package/dist/drivers/partialTurn.js +23 -0
  222. package/dist/drivers/partialTurn.js.map +1 -0
  223. package/dist/drivers/providerError.js +181 -0
  224. package/dist/drivers/providerError.js.map +1 -0
  225. package/dist/drivers/qwen/client.js +93 -0
  226. package/dist/drivers/qwen/client.js.map +1 -0
  227. package/dist/drivers/qwen/index.js +9 -0
  228. package/dist/drivers/qwen/index.js.map +1 -0
  229. package/dist/drivers/qwen/manifest.js +146 -0
  230. package/dist/drivers/qwen/manifest.js.map +1 -0
  231. package/dist/drivers/registry.js +177 -0
  232. package/dist/drivers/registry.js.map +1 -0
  233. package/dist/drivers/retryPolicy.js +129 -0
  234. package/dist/drivers/retryPolicy.js.map +1 -0
  235. package/dist/drivers/searchBlocks.js +61 -0
  236. package/dist/drivers/searchBlocks.js.map +1 -0
  237. package/dist/drivers/types.js +2 -0
  238. package/dist/drivers/types.js.map +1 -0
  239. package/dist/drivers/xai/client.js +63 -0
  240. package/dist/drivers/xai/client.js.map +1 -0
  241. package/dist/drivers/xai/index.js +5 -0
  242. package/dist/drivers/xai/index.js.map +1 -0
  243. package/dist/drivers/xai/manifest.js +137 -0
  244. package/dist/drivers/xai/manifest.js.map +1 -0
  245. package/dist/dynamo/_probe.tmp.js +20 -0
  246. package/dist/dynamo/_probe.tmp.js.map +1 -0
  247. package/dist/dynamo/cacheBreak.js +88 -0
  248. package/dist/dynamo/cacheBreak.js.map +1 -0
  249. package/dist/dynamo/contextWindow.js +243 -0
  250. package/dist/dynamo/contextWindow.js.map +1 -0
  251. package/dist/dynamo/engine.js +2011 -0
  252. package/dist/dynamo/engine.js.map +1 -0
  253. package/dist/dynamo/guard.js +132 -0
  254. package/dist/dynamo/guard.js.map +1 -0
  255. package/dist/dynamo/liveMeter.js +58 -0
  256. package/dist/dynamo/liveMeter.js.map +1 -0
  257. package/dist/dynamo/model.js +172 -0
  258. package/dist/dynamo/model.js.map +1 -0
  259. package/dist/dynamo/planArtifact.js +125 -0
  260. package/dist/dynamo/planArtifact.js.map +1 -0
  261. package/dist/dynamo/pricing.js +140 -0
  262. package/dist/dynamo/pricing.js.map +1 -0
  263. package/dist/dynamo/prompt.js +264 -0
  264. package/dist/dynamo/prompt.js.map +1 -0
  265. package/dist/dynamo/spend.js +45 -0
  266. package/dist/dynamo/spend.js.map +1 -0
  267. package/dist/dynamo/verify.js +483 -0
  268. package/dist/dynamo/verify.js.map +1 -0
  269. package/dist/governor/forbidden.js +181 -0
  270. package/dist/governor/forbidden.js.map +1 -0
  271. package/dist/governor/freshness.js +83 -0
  272. package/dist/governor/freshness.js.map +1 -0
  273. package/dist/governor/frontmatter.js +38 -0
  274. package/dist/governor/frontmatter.js.map +1 -0
  275. package/dist/governor/glob.js +75 -0
  276. package/dist/governor/glob.js.map +1 -0
  277. package/dist/governor/index.js +87 -0
  278. package/dist/governor/index.js.map +1 -0
  279. package/dist/governor/rules.js +80 -0
  280. package/dist/governor/rules.js.map +1 -0
  281. package/dist/governor/scope.js +91 -0
  282. package/dist/governor/scope.js.map +1 -0
  283. package/dist/governor/skills.js +176 -0
  284. package/dist/governor/skills.js.map +1 -0
  285. package/dist/governor/types.js +16 -0
  286. package/dist/governor/types.js.map +1 -0
  287. package/dist/governor/write.js +182 -0
  288. package/dist/governor/write.js.map +1 -0
  289. package/dist/index.js +89 -0
  290. package/dist/index.js.map +1 -0
  291. package/dist/mcp/catalog.js +232 -0
  292. package/dist/mcp/catalog.js.map +1 -0
  293. package/dist/mcp/config.js +116 -0
  294. package/dist/mcp/config.js.map +1 -0
  295. package/dist/mcp/configWrite.js +254 -0
  296. package/dist/mcp/configWrite.js.map +1 -0
  297. package/dist/mcp/connection.js +504 -0
  298. package/dist/mcp/connection.js.map +1 -0
  299. package/dist/mcp/deferred.js +104 -0
  300. package/dist/mcp/deferred.js.map +1 -0
  301. package/dist/mcp/discover.js +128 -0
  302. package/dist/mcp/discover.js.map +1 -0
  303. package/dist/mcp/manager.js +679 -0
  304. package/dist/mcp/manager.js.map +1 -0
  305. package/dist/mcp/paramHeaders.js +163 -0
  306. package/dist/mcp/paramHeaders.js.map +1 -0
  307. package/dist/mcp/prompts.js +136 -0
  308. package/dist/mcp/prompts.js.map +1 -0
  309. package/dist/mcp/protocol.js +171 -0
  310. package/dist/mcp/protocol.js.map +1 -0
  311. package/dist/mcp/resources.js +119 -0
  312. package/dist/mcp/resources.js.map +1 -0
  313. package/dist/mcp/resultStore.js +180 -0
  314. package/dist/mcp/resultStore.js.map +1 -0
  315. package/dist/mcp/subscriptions.js +74 -0
  316. package/dist/mcp/subscriptions.js.map +1 -0
  317. package/dist/mcp/transport/headerValue.js +40 -0
  318. package/dist/mcp/transport/headerValue.js.map +1 -0
  319. package/dist/mcp/transport/http.js +321 -0
  320. package/dist/mcp/transport/http.js.map +1 -0
  321. package/dist/mcp/transport/stdio.js +234 -0
  322. package/dist/mcp/transport/stdio.js.map +1 -0
  323. package/dist/mcp/transport/types.js +27 -0
  324. package/dist/mcp/transport/types.js.map +1 -0
  325. package/dist/mcp/trust.js +98 -0
  326. package/dist/mcp/trust.js.map +1 -0
  327. package/dist/mcp/trustStore.js +55 -0
  328. package/dist/mcp/trustStore.js.map +1 -0
  329. package/dist/memory/autoMemory.js +340 -0
  330. package/dist/memory/autoMemory.js.map +1 -0
  331. package/dist/memory/compaction.js +513 -0
  332. package/dist/memory/compaction.js.map +1 -0
  333. package/dist/memory/images.js +173 -0
  334. package/dist/memory/images.js.map +1 -0
  335. package/dist/memory/presence.js +59 -0
  336. package/dist/memory/presence.js.map +1 -0
  337. package/dist/memory/projectNotes.js +247 -0
  338. package/dist/memory/projectNotes.js.map +1 -0
  339. package/dist/memory/restore.js +72 -0
  340. package/dist/memory/restore.js.map +1 -0
  341. package/dist/memory/session.js +472 -0
  342. package/dist/memory/session.js.map +1 -0
  343. package/dist/memory/sessionMemory.js +142 -0
  344. package/dist/memory/sessionMemory.js.map +1 -0
  345. package/dist/memory/sessionMemoryCompact.js +136 -0
  346. package/dist/memory/sessionMemoryCompact.js.map +1 -0
  347. package/dist/memory/store.js +217 -0
  348. package/dist/memory/store.js.map +1 -0
  349. package/dist/memory/types.js +14 -0
  350. package/dist/memory/types.js.map +1 -0
  351. package/dist/memory/workingSet.js +383 -0
  352. package/dist/memory/workingSet.js.map +1 -0
  353. package/dist/project/context.js +425 -0
  354. package/dist/project/context.js.map +1 -0
  355. package/dist/tools/approval.js +189 -0
  356. package/dist/tools/approval.js.map +1 -0
  357. package/dist/tools/askUser.js +103 -0
  358. package/dist/tools/askUser.js.map +1 -0
  359. package/dist/tools/atomicWrite.js +151 -0
  360. package/dist/tools/atomicWrite.js.map +1 -0
  361. package/dist/tools/backgroundShells.js +468 -0
  362. package/dist/tools/backgroundShells.js.map +1 -0
  363. package/dist/tools/captureSweep.js +77 -0
  364. package/dist/tools/captureSweep.js.map +1 -0
  365. package/dist/tools/chassisMux.js +92 -0
  366. package/dist/tools/chassisMux.js.map +1 -0
  367. package/dist/tools/checkpoints.js +328 -0
  368. package/dist/tools/checkpoints.js.map +1 -0
  369. package/dist/tools/codeIntel.js +267 -0
  370. package/dist/tools/codeIntel.js.map +1 -0
  371. package/dist/tools/deferredNative.js +121 -0
  372. package/dist/tools/deferredNative.js.map +1 -0
  373. package/dist/tools/detail.js +135 -0
  374. package/dist/tools/detail.js.map +1 -0
  375. package/dist/tools/diagnostics.js +174 -0
  376. package/dist/tools/diagnostics.js.map +1 -0
  377. package/dist/tools/edit.js +167 -0
  378. package/dist/tools/edit.js.map +1 -0
  379. package/dist/tools/editCore.js +283 -0
  380. package/dist/tools/editCore.js.map +1 -0
  381. package/dist/tools/editFile.js +183 -0
  382. package/dist/tools/editFile.js.map +1 -0
  383. package/dist/tools/editRipple.js +126 -0
  384. package/dist/tools/editRipple.js.map +1 -0
  385. package/dist/tools/editTarget.js +131 -0
  386. package/dist/tools/editTarget.js.map +1 -0
  387. package/dist/tools/editWindow.js +49 -0
  388. package/dist/tools/editWindow.js.map +1 -0
  389. package/dist/tools/eol.js +56 -0
  390. package/dist/tools/eol.js.map +1 -0
  391. package/dist/tools/exitPlan.js +175 -0
  392. package/dist/tools/exitPlan.js.map +1 -0
  393. package/dist/tools/focus.js +42 -0
  394. package/dist/tools/focus.js.map +1 -0
  395. package/dist/tools/gitignore.js +148 -0
  396. package/dist/tools/gitignore.js.map +1 -0
  397. package/dist/tools/glob.js +134 -0
  398. package/dist/tools/glob.js.map +1 -0
  399. package/dist/tools/governorTools.js +258 -0
  400. package/dist/tools/governorTools.js.map +1 -0
  401. package/dist/tools/grep.js +450 -0
  402. package/dist/tools/grep.js.map +1 -0
  403. package/dist/tools/guard.js +220 -0
  404. package/dist/tools/guard.js.map +1 -0
  405. package/dist/tools/killTree.js +215 -0
  406. package/dist/tools/killTree.js.map +1 -0
  407. package/dist/tools/listDir.js +143 -0
  408. package/dist/tools/listDir.js.map +1 -0
  409. package/dist/tools/mcpAdd.js +176 -0
  410. package/dist/tools/mcpAdd.js.map +1 -0
  411. package/dist/tools/mcpResources.js +116 -0
  412. package/dist/tools/mcpResources.js.map +1 -0
  413. package/dist/tools/mcpSearch.js +123 -0
  414. package/dist/tools/mcpSearch.js.map +1 -0
  415. package/dist/tools/multiEdit.js +168 -0
  416. package/dist/tools/multiEdit.js.map +1 -0
  417. package/dist/tools/paths.js +225 -0
  418. package/dist/tools/paths.js.map +1 -0
  419. package/dist/tools/posixShell.js +123 -0
  420. package/dist/tools/posixShell.js.map +1 -0
  421. package/dist/tools/readFile.js +371 -0
  422. package/dist/tools/readFile.js.map +1 -0
  423. package/dist/tools/readSymbol.js +145 -0
  424. package/dist/tools/readSymbol.js.map +1 -0
  425. package/dist/tools/registry.js +120 -0
  426. package/dist/tools/registry.js.map +1 -0
  427. package/dist/tools/replaceSymbol.js +141 -0
  428. package/dist/tools/replaceSymbol.js.map +1 -0
  429. package/dist/tools/results.js +15 -0
  430. package/dist/tools/results.js.map +1 -0
  431. package/dist/tools/ripgrep.js +145 -0
  432. package/dist/tools/ripgrep.js.map +1 -0
  433. package/dist/tools/runCommand.js +633 -0
  434. package/dist/tools/runCommand.js.map +1 -0
  435. package/dist/tools/saveMemory.js +102 -0
  436. package/dist/tools/saveMemory.js.map +1 -0
  437. package/dist/tools/screenshot.js +200 -0
  438. package/dist/tools/screenshot.js.map +1 -0
  439. package/dist/tools/screenshotWin.js +227 -0
  440. package/dist/tools/screenshotWin.js.map +1 -0
  441. package/dist/tools/search.js +121 -0
  442. package/dist/tools/search.js.map +1 -0
  443. package/dist/tools/sessionTools.js +187 -0
  444. package/dist/tools/sessionTools.js.map +1 -0
  445. package/dist/tools/shellCheckpoint.js +123 -0
  446. package/dist/tools/shellCheckpoint.js.map +1 -0
  447. package/dist/tools/shellLint.js +102 -0
  448. package/dist/tools/shellLint.js.map +1 -0
  449. package/dist/tools/shellTools.js +203 -0
  450. package/dist/tools/shellTools.js.map +1 -0
  451. package/dist/tools/spanCore.js +78 -0
  452. package/dist/tools/spanCore.js.map +1 -0
  453. package/dist/tools/subagent.js +224 -0
  454. package/dist/tools/subagent.js.map +1 -0
  455. package/dist/tools/subagentReport.js +114 -0
  456. package/dist/tools/subagentReport.js.map +1 -0
  457. package/dist/tools/tempSweep.js +118 -0
  458. package/dist/tools/tempSweep.js.map +1 -0
  459. package/dist/tools/todo.js +117 -0
  460. package/dist/tools/todo.js.map +1 -0
  461. package/dist/tools/types.js +2 -0
  462. package/dist/tools/types.js.map +1 -0
  463. package/dist/tools/untrusted.js +46 -0
  464. package/dist/tools/untrusted.js.map +1 -0
  465. package/dist/tools/useSkill.js +62 -0
  466. package/dist/tools/useSkill.js.map +1 -0
  467. package/dist/tools/walk.js +152 -0
  468. package/dist/tools/walk.js.map +1 -0
  469. package/dist/tools/web.js +61 -0
  470. package/dist/tools/web.js.map +1 -0
  471. package/dist/tools/webFetch.js +444 -0
  472. package/dist/tools/webFetch.js.map +1 -0
  473. package/dist/tools/webSearch.js +130 -0
  474. package/dist/tools/webSearch.js.map +1 -0
  475. package/dist/tools/workspace.js +212 -0
  476. package/dist/tools/workspace.js.map +1 -0
  477. package/dist/tools/workspaceDiscover.js +216 -0
  478. package/dist/tools/workspaceDiscover.js.map +1 -0
  479. package/dist/tools/writeFile.js +162 -0
  480. package/dist/tools/writeFile.js.map +1 -0
  481. package/package.json +74 -28
@@ -0,0 +1,679 @@
1
+ import { outputDetail } from "../tools/detail.js";
2
+ import { formatBytes } from "../tools/webFetch.js";
3
+ import { formatElapsed } from "../tools/webSearch.js";
4
+ import { estimateCatalogTokens, frameUntrusted, mcpToolName, normalizeServerName, parseContentBlocks, parseMcpToolName, sortCatalog, toolSchemas as schemasFor, } from "./catalog.js";
5
+ import { binaryPointer, isOversized, oversizedPointer, spill, sweepOldResults } from "./resultStore.js";
6
+ import { renderPromptMessages } from "./prompts.js";
7
+ import { sortResources } from "./resources.js";
8
+ import { McpConnection } from "./connection.js";
9
+ import { searchCatalog, shouldDefer } from "./deferred.js";
10
+ import { compareTrust, acceptTools, changedToolsQuestion, fingerprintCatalog } from "./trust.js";
11
+ import { loadTrust, saveTrust } from "./trustStore.js";
12
+ /**
13
+ * Wrap one MCP tool def as a runnable Tool. Shared by the live lookup and the per-turn
14
+ * snapshot so both dispatch through exactly the same path.
15
+ */
16
+ function buildTool(name, def, connection, spillRoot) {
17
+ return {
18
+ name,
19
+ description: def.description,
20
+ parameters: def.inputSchema,
21
+ readOnly: def.readOnly,
22
+ async execute(args) {
23
+ try {
24
+ // Timed at the call site: a remote server's round trip is the one cost in a
25
+ // tool row that nothing else on screen reveals, and it is what tells a slow
26
+ // server apart from a slow query.
27
+ const startedAt = Date.now();
28
+ const result = await connection.callTool(def.name, args);
29
+ const elapsedMs = Date.now() - startedAt;
30
+ const { blocks, isError } = parseContentBlocks(result);
31
+ const text = await renderBlocks(blocks, def, spillRoot());
32
+ // Framed as data, not instruction: this text is written by a third party and
33
+ // lands where the model trusts built-in tool output.
34
+ return {
35
+ output: frameUntrusted(def.server, text),
36
+ summary: `${def.server} · ${def.name}`,
37
+ detail: mcpDetail(def.name, args, text, isError, elapsedMs),
38
+ ...(isError ? { isError: true } : {}),
39
+ };
40
+ }
41
+ catch (error) {
42
+ // A tool failure is a tool RESULT, not an exception: the model should see it,
43
+ // reason about it, and try something else — the same contract as a failed shell
44
+ // command. This is also where a server that died mid-turn surfaces, which is why
45
+ // the snapshot can keep offering it without lying.
46
+ return {
47
+ output: `Error: MCP tool '${name}' failed: ${error instanceof Error ? error.message : String(error)}`,
48
+ isError: true,
49
+ summary: `${name} failed`,
50
+ };
51
+ }
52
+ },
53
+ };
54
+ }
55
+ /** The UI-only detail block under an MCP call row — never sent to the model
56
+ * (`frameUntrusted`'s `output` is what the model sees). A short args preview
57
+ * plus how much came back, capped the same way any other tool's output is. */
58
+ function mcpDetail(toolName, args, text, isError, elapsedMs) {
59
+ const argsPreview = Object.keys(args).length > 0 ? JSON.stringify(args).slice(0, 120) : "";
60
+ const lines = [];
61
+ // "Connected" is implied by having a result at all; the LATENCY is the part worth
62
+ // stating, and it is omitted rather than invented when nothing measured it.
63
+ if (elapsedMs !== undefined)
64
+ lines.push(`Status: Connected [Latency: ${formatElapsed(elapsedMs)}]`);
65
+ lines.push(`Executed: ${toolName}${argsPreview ? `(${argsPreview})` : "()"}`);
66
+ lines.push(isError
67
+ ? "Received an error result"
68
+ : `Received ${formatBytes(Buffer.byteLength(text, "utf8"))} (OK) → ${text.length.toLocaleString("en-US")} chars`);
69
+ return outputDetail(lines.join("\n"));
70
+ }
71
+ /**
72
+ * Turn a result's blocks into the text the model receives, spilling what does not belong
73
+ * in a prompt.
74
+ *
75
+ * Two things go to disk. BYTES always: an image or a blob is base64 the model cannot
76
+ * look at, and inlining it is money spent on noise. OVERSIZED TEXT past the cap: the
77
+ * model gets the head plus the path, which beats a bare truncation because nothing is
78
+ * actually lost — it can read or grep the file for the part it needs.
79
+ *
80
+ * Failing to write is not fatal. The pointer falls back to naming the content, which is
81
+ * what the old code did for everything, so the worst case is the previous behaviour.
82
+ */
83
+ async function renderBlocks(blocks, def, root) {
84
+ const parts = [];
85
+ for (const block of blocks) {
86
+ if (block.kind === "text") {
87
+ parts.push(block.text);
88
+ continue;
89
+ }
90
+ if (block.kind === "link") {
91
+ // A link is already a pointer; it costs nothing and read_mcp_resource can follow it.
92
+ parts.push(`[resource: ${block.uri}${block.label ? ` — ${block.label}` : ""}]`);
93
+ continue;
94
+ }
95
+ const bytes = Buffer.from(block.base64, "base64");
96
+ const path = await spill(root, def.server, def.name, block.mime, bytes);
97
+ parts.push(path ? binaryPointer(block.mime, bytes.byteLength, path) : `[${block.mime} content, ${bytes.byteLength} bytes, could not be saved]`);
98
+ }
99
+ const text = parts.join("\n") || "(no output)";
100
+ if (!isOversized(text))
101
+ return text;
102
+ const path = await spill(root, def.server, def.name, "text/plain", text);
103
+ // With nowhere to put it we still must not hand over the whole thing — an unbounded
104
+ // result is the failure this exists to prevent.
105
+ return path ? oversizedPointer(text, path) : oversizedPointer(text, "(could not be saved to disk)");
106
+ }
107
+ /** The two answers to a changed-description prompt. */
108
+ const ALLOW_CHANGED = "Allow the changed tools";
109
+ const BLOCK_CHANGED = "Block them this session";
110
+ /** How many servers to bring up at once. Enough to start fast, bounded so a config
111
+ * with twenty servers does not spawn twenty processes in the same instant. */
112
+ const CONNECT_BATCH = 6;
113
+ let cleanupRegistered = false;
114
+ const live = new Set();
115
+ export class McpManager {
116
+ connections = new Map();
117
+ started = false;
118
+ onChange = null;
119
+ /** Deferred tools the model has searched for and may now call. Session-lived. */
120
+ activated = new Set();
121
+ /** Fingerprints of tools the user has accepted, loaded from the project's state dir. */
122
+ trust = {};
123
+ /** Tools blocked because their description moved and the user declined. */
124
+ quarantined = new Set();
125
+ /** Tool names the project's governor forbids outright (`forbid_mcp_tool`). */
126
+ forbidden = new Set();
127
+ /**
128
+ * One-shot messages the user needs to see. Drained by the CLI, never re-sent.
129
+ *
130
+ * Blocking a tool with no visible explanation is barely better than not blocking it:
131
+ * the user sees a shorter tool list, assumes the server is broken, and has nothing to
132
+ * act on. Mirrors `BackgroundShells.takeUiNotifications`, which solved the same
133
+ * problem for finished shells.
134
+ */
135
+ notices = [];
136
+ /**
137
+ * The project this pool belongs to: where the trust record is read and written, and
138
+ * where oversized results are spilled. Set as early as the session knows it, so a
139
+ * dispatch that beats the (async, queued) trust check still spills to the right place.
140
+ */
141
+ trustCwd = "";
142
+ /**
143
+ * Serializes every piece of trust work.
144
+ *
145
+ * The initial verification and a notification-driven recheck can otherwise interleave:
146
+ * both read the record, both write it, and the second write loses whatever the first
147
+ * decided. A chain is enough — this is rare, cheap work, and correctness matters more
148
+ * than concurrency.
149
+ */
150
+ trustQueue = Promise.resolve();
151
+ /** Fingerprints as of the last check, so an unchanged catalog costs nothing. */
152
+ trustSignature = "";
153
+ /**
154
+ * Told whenever any server's state or catalog moves, so the CLI can repaint.
155
+ *
156
+ * Servers connect in the background and can die or revive at any moment, so without
157
+ * this the UI would only ever show whatever was true when the last key was pressed.
158
+ * Mirrors `BackgroundShells.setOnChange`, which solved the same problem.
159
+ */
160
+ setOnChange(handler) {
161
+ this.onChange = handler;
162
+ // Our own handler stays attached even when the UI detaches: it carries the trust
163
+ // recheck, which is a safety gate and not a rendering concern.
164
+ for (const connection of this.connections.values())
165
+ connection.setOnChange(() => this.onConnectionChange());
166
+ }
167
+ /**
168
+ * Tell the pool which project it serves, before anything connects.
169
+ *
170
+ * Also the moment to clear out stale spilled results: they are scratch, this runs once
171
+ * per session, and sweeping here keeps the mechanism free of any background timer.
172
+ */
173
+ setProjectRoot(cwd) {
174
+ this.trustCwd = cwd;
175
+ void sweepOldResults(cwd).catch(() => { });
176
+ }
177
+ /** Where spilled results go. Falls back to the process cwd in bare contexts (tests),
178
+ * which is the same directory the rest of the tool layer defaults to. */
179
+ spillRoot() {
180
+ return this.trustCwd || process.cwd();
181
+ }
182
+ /** Bring up every configured server. Never rejects: failures become states. */
183
+ async start(configs, log) {
184
+ if (this.started)
185
+ return;
186
+ this.started = true;
187
+ if (configs.length === 0)
188
+ return;
189
+ registerCleanup();
190
+ live.add(this);
191
+ for (const config of configs) {
192
+ const connection = new McpConnection(config);
193
+ connection.setOnChange(() => this.onConnectionChange());
194
+ this.connections.set(config.name, connection);
195
+ }
196
+ const pending = [...this.connections.values()].filter((c) => !c.config.disabled);
197
+ for (let i = 0; i < pending.length; i += CONNECT_BATCH) {
198
+ await Promise.all(pending.slice(i, i + CONNECT_BATCH).map(async (connection) => {
199
+ // `connect` already converts failure into state; this catch is for the
200
+ // genuinely unexpected, so one bad server cannot reject the whole batch.
201
+ await connection.connect().catch(() => { });
202
+ const status = connection.status();
203
+ log?.(status.state === "connected"
204
+ ? `mcp ${status.name}: ${status.toolCount} tool${status.toolCount === 1 ? "" : "s"} (${status.version}${status.legacy ? ", legacy" : ""})`
205
+ : `mcp ${status.name}: ${status.state}${status.error ? ` — ${status.error}` : ""}`);
206
+ }));
207
+ }
208
+ }
209
+ /**
210
+ * Check the connected catalog against the fingerprints this project accepted before,
211
+ * and quarantine anything whose description or schema moved until the user says
212
+ * otherwise.
213
+ *
214
+ * Called after `start()`, with the project root and the approval channel, so the pool
215
+ * itself stays free of both filesystem and UI concerns. Fail-open on the mechanics
216
+ * (an unreadable record means everything reads as fresh) but fail-CLOSED on the
217
+ * decision: with no approval channel to ask through, changed tools stay blocked.
218
+ */
219
+ async verifyTrust(cwd, ask) {
220
+ this.trustQueue = this.trustQueue.then(() => this.doVerifyTrust(cwd, ask)).catch(() => { });
221
+ return this.trustQueue;
222
+ }
223
+ async doVerifyTrust(cwd, ask) {
224
+ const defs = this.catalog();
225
+ // Remember the root even with nothing to check: a server can connect later, and the
226
+ // recheck below is inert until it knows where the record lives.
227
+ this.trustCwd = cwd;
228
+ if (defs.length === 0)
229
+ return;
230
+ this.trust = await loadTrust(cwd);
231
+ this.trustSignature = JSON.stringify(fingerprintCatalog(defs));
232
+ const verdict = compareTrust(defs, this.trust);
233
+ // First sight is trusted by construction — there is nothing to compare against.
234
+ // Recording it is what makes the NEXT change detectable.
235
+ if (verdict.fresh.length > 0) {
236
+ this.trust = acceptTools(this.trust, defs, verdict.fresh);
237
+ }
238
+ if (verdict.changed.length > 0) {
239
+ const allow = ask ? await ask(changedToolsQuestion(verdict.changed), [ALLOW_CHANGED, BLOCK_CHANGED]) : BLOCK_CHANGED;
240
+ if (allow === ALLOW_CHANGED) {
241
+ this.trust = acceptTools(this.trust, defs, verdict.changed);
242
+ for (const name of verdict.changed)
243
+ this.quarantined.delete(name);
244
+ }
245
+ else {
246
+ // Blocked for the session. The stored fingerprint is left ALONE so the same
247
+ // question is asked again next session rather than the change being forgotten.
248
+ for (const name of verdict.changed)
249
+ this.quarantined.add(name);
250
+ // Say so. Without this the user just sees fewer tools — which reads as a broken
251
+ // server, not a security decision they can act on. This is also the safety net
252
+ // for the case where there was no approval channel to ask through at all.
253
+ this.notices.push(`Blocked ${verdict.changed.length} MCP tool${verdict.changed.length === 1 ? "" : "s"} whose description ` +
254
+ `changed since you last used ${verdict.changed.length === 1 ? "it" : "them"}: ${verdict.changed.join(", ")}. ` +
255
+ `Run /mcp to review.`);
256
+ this.onChange?.();
257
+ }
258
+ }
259
+ if (verdict.fresh.length > 0 || verdict.changed.length > 0)
260
+ await saveTrust(cwd, this.trust);
261
+ }
262
+ /**
263
+ * Re-verify after the catalog moves UNDERNEATH us, mid-session.
264
+ *
265
+ * This closes the hole the startup check left wide open. `trust.ts` names the rug pull
266
+ * as its whole reason to exist — "a clean server ships an update that poisons a
267
+ * description, and clients reload it silently" — and that is exactly what we did: a
268
+ * `notifications/tools/list_changed` refreshed the catalog and nothing compared the new
269
+ * descriptions against anything. A server therefore only had to connect clean and then
270
+ * announce a change to walk straight past a check built to stop it.
271
+ *
272
+ * It QUARANTINES rather than asking. The trigger arrives whenever the server feels like
273
+ * it, quite possibly mid-turn, and interrupting a running turn with a security question
274
+ * is both jarring and racy. Blocking is the safe direction, the notice says what
275
+ * happened, and `/mcp` already knows how to hand the question back.
276
+ */
277
+ async recheckTrust() {
278
+ // Before the startup check has run there is no baseline to compare against, and
279
+ // treating everything as fresh here would record fingerprints we never verified.
280
+ if (!this.trustCwd)
281
+ return;
282
+ const defs = this.catalog();
283
+ if (defs.length === 0)
284
+ return;
285
+ const signature = JSON.stringify(fingerprintCatalog(defs));
286
+ if (signature === this.trustSignature)
287
+ return; // nothing moved; the common case
288
+ this.trustSignature = signature;
289
+ const verdict = compareTrust(defs, this.trust);
290
+ if (verdict.fresh.length === 0 && verdict.changed.length === 0)
291
+ return;
292
+ // A server that GAINS a tool is trusted on first sight, exactly as at startup —
293
+ // otherwise every ordinary server upgrade would look like an attack.
294
+ if (verdict.fresh.length > 0)
295
+ this.trust = acceptTools(this.trust, defs, verdict.fresh);
296
+ if (verdict.changed.length > 0) {
297
+ for (const name of verdict.changed)
298
+ this.quarantined.add(name);
299
+ this.notices.push(`An MCP server changed ${verdict.changed.length} tool description${verdict.changed.length === 1 ? "" : "s"} ` +
300
+ `while this session was running: ${verdict.changed.join(", ")}. ` +
301
+ `Blocked for now — run /mcp to review.`);
302
+ }
303
+ await saveTrust(this.trustCwd, this.trust);
304
+ this.onChange?.();
305
+ }
306
+ /**
307
+ * A server's state or catalog moved. Repaint, and check what moved.
308
+ *
309
+ * One handler for both so a catalog refresh can never reach the model without passing
310
+ * the trust gate — the previous wiring repainted and nothing else.
311
+ */
312
+ onConnectionChange() {
313
+ // Catalogs are reloaded here (connect, and again on `list_changed`), so this is the
314
+ // one point every drop passes through, whichever load produced it.
315
+ for (const connection of this.connections.values())
316
+ this.notices.push(...connection.takeToolWarnings());
317
+ this.trustQueue = this.trustQueue.then(() => this.recheckTrust()).catch(() => { });
318
+ this.onChange?.();
319
+ }
320
+ /**
321
+ * Re-offer the currently quarantined tools, so a block is recoverable in-session.
322
+ *
323
+ * Needed because the automatic check runs while the pool connects in the background,
324
+ * possibly before the CLI has an approval channel to ask through. That path fails
325
+ * closed and silent by design; this is how the user gets the question back.
326
+ */
327
+ async reviewQuarantine(ask) {
328
+ const blocked = this.quarantinedNames();
329
+ if (blocked.length === 0)
330
+ return false;
331
+ const answer = await ask(changedToolsQuestion(blocked), [ALLOW_CHANGED, BLOCK_CHANGED]);
332
+ if (answer !== ALLOW_CHANGED)
333
+ return false;
334
+ this.trust = acceptTools(this.trust, this.catalog(), blocked);
335
+ for (const name of blocked)
336
+ this.quarantined.delete(name);
337
+ if (this.trustCwd)
338
+ await saveTrust(this.trustCwd, this.trust);
339
+ this.onChange?.();
340
+ return true;
341
+ }
342
+ /** Drain user-facing notices. One-shot: each is reported exactly once. */
343
+ takeNotices() {
344
+ const out = this.notices;
345
+ this.notices = [];
346
+ return out;
347
+ }
348
+ /** How many of a server's tools are currently blocked, for `/mcp`. */
349
+ blockedCountFor(server) {
350
+ const prefix = mcpToolName(server, "");
351
+ return this.quarantinedNames().filter((n) => n.startsWith(prefix)).length;
352
+ }
353
+ /** Apply the project's forbidden-MCP-tool list. Replaces the previous set. */
354
+ setForbidden(names) {
355
+ this.forbidden = new Set(names);
356
+ // A tool that becomes forbidden must not linger in the activated set, or a deferred
357
+ // catalog would keep advertising it.
358
+ for (const name of names)
359
+ this.activated.delete(name);
360
+ }
361
+ /** Tools currently blocked because their description changed. */
362
+ quarantinedNames() {
363
+ return [...this.quarantined].sort();
364
+ }
365
+ /**
366
+ * Bring one server up NOW, without restarting the session.
367
+ *
368
+ * Adding a server and then being told to restart would make the add path useless —
369
+ * the whole point is that you say what you want and it is there. Replaces any server
370
+ * of the same name, shutting the old one down first so we never leak a child process
371
+ * on a re-add.
372
+ */
373
+ async addServer(config) {
374
+ registerCleanup();
375
+ live.add(this);
376
+ this.started = true;
377
+ const previous = this.connections.get(config.name);
378
+ if (previous)
379
+ await previous.close().catch(() => { });
380
+ const connection = new McpConnection(config);
381
+ connection.setOnChange(() => this.onConnectionChange());
382
+ this.connections.set(config.name, connection);
383
+ if (!config.disabled)
384
+ await connection.connect().catch(() => { });
385
+ // A re-added server is a different server as far as trust goes; drop any stale
386
+ // activation so a deferred catalog does not advertise tools it no longer has.
387
+ for (const name of this.activatedNames()) {
388
+ if (name.startsWith(mcpToolName(config.name, "")))
389
+ this.activated.delete(name);
390
+ }
391
+ // Fingerprint what it brought. Without this a server added mid-session was never
392
+ // recorded at all, so its very first descriptions became the baseline only on the
393
+ // NEXT session — and anything it changed in between went unnoticed.
394
+ this.onConnectionChange();
395
+ return connection.status();
396
+ }
397
+ /**
398
+ * Every prompt every connected server offers, as slash commands.
399
+ *
400
+ * Synchronous because the CLI reads it while rendering the completion menu — a
401
+ * keystroke cannot wait on a round trip, which is why prompts are fetched at connect.
402
+ */
403
+ promptCatalog() {
404
+ return [...this.connections.values()]
405
+ .filter((c) => c.isConnected())
406
+ .flatMap((c) => [...c.prompts()])
407
+ .sort((a, b) => a.server.localeCompare(b.server) || a.name.localeCompare(b.name));
408
+ }
409
+ /** Look up one prompt by its server and name. */
410
+ findPrompt(server, name) {
411
+ return this.promptCatalog().find((p) => p.server === server && p.name === name);
412
+ }
413
+ /**
414
+ * Render a prompt into the text of a user turn.
415
+ *
416
+ * Errors are returned rather than thrown: the caller is a slash command, and the user
417
+ * needs to be told what went wrong in the chat, not shown a stack.
418
+ */
419
+ async renderPrompt(server, name, args) {
420
+ const connection = this.connectionFor(server);
421
+ if (!connection)
422
+ return { text: "", error: `MCP server '${server}' is not connected.` };
423
+ try {
424
+ const rendered = renderPromptMessages(await connection.getPrompt(name, args));
425
+ if (!rendered)
426
+ return { text: "", error: `'${server}:${name}' returned nothing to run.` };
427
+ return { text: rendered };
428
+ }
429
+ catch (error) {
430
+ return { text: "", error: error instanceof Error ? error.message : String(error) };
431
+ }
432
+ }
433
+ /**
434
+ * List resources across every connected server, or one of them.
435
+ *
436
+ * Fetches on demand rather than at connect: most projects never touch resources, and
437
+ * two extra round trips per server would be paid by every session to serve the few
438
+ * that do. The per-connection TTL keeps a model that lists twice in one turn from
439
+ * paying twice.
440
+ */
441
+ async listResources(server) {
442
+ const connections = [...this.connections.values()].filter((c) => c.isConnected() && c.offersResources() && (!server || matchesServer(c.config.name, server)));
443
+ await Promise.all(connections.map((c) => c.loadResources().catch(() => { })));
444
+ return {
445
+ resources: sortResources(connections.flatMap((c) => [...c.resources()])),
446
+ templates: connections.flatMap((c) => [...c.resourceTemplates()]),
447
+ servers: connections.length,
448
+ };
449
+ }
450
+ /**
451
+ * Read one resource, spilling it to disk if it is large or binary.
452
+ *
453
+ * Same treatment as a tool result, for the same reason: a resource is content chosen
454
+ * by a third party, and "read the schema" should not be able to swallow a turn.
455
+ */
456
+ async readResource(server, uri) {
457
+ const connection = this.connectionFor(server);
458
+ if (!connection) {
459
+ return { text: `No connected MCP server named '${server}'.`, isError: true };
460
+ }
461
+ try {
462
+ const blocks = await connection.readResource(uri);
463
+ if (blocks.length === 0)
464
+ return { text: `'${uri}' returned no content.`, isError: false };
465
+ const body = await renderBlocks(blocks, { server, name: resourceLabel(uri) }, this.spillRoot());
466
+ return { text: frameUntrusted(server, body), isError: false };
467
+ }
468
+ catch (error) {
469
+ return { text: `Could not read '${uri}': ${error instanceof Error ? error.message : String(error)}`, isError: true };
470
+ }
471
+ }
472
+ /** Which servers advertise resources at all, for a useful empty-state message. */
473
+ resourceServers() {
474
+ return [...this.connections.values()]
475
+ .filter((c) => c.isConnected() && c.offersResources())
476
+ .map((c) => c.config.name)
477
+ .sort();
478
+ }
479
+ /** Resolve a server by its config name, tolerating the normalized form the model sees. */
480
+ connectionFor(server) {
481
+ return [...this.connections.values()].find((c) => c.isConnected() && matchesServer(c.config.name, server));
482
+ }
483
+ /** Every server's state, for `/mcp` and for diagnostics. */
484
+ statuses() {
485
+ return [...this.connections.values()].map((c) => c.status()).sort((a, b) => a.name.localeCompare(b.name));
486
+ }
487
+ /** Force a reconnect of one server by name. Returns its state afterwards. */
488
+ async reconnect(name) {
489
+ const connection = this.connections.get(name);
490
+ if (!connection)
491
+ return null;
492
+ await connection.reconnect().catch(() => { });
493
+ this.onChange?.();
494
+ return connection.status();
495
+ }
496
+ /** True when there is anything at all to show in `/mcp`. */
497
+ hasServers() {
498
+ return this.connections.size > 0;
499
+ }
500
+ /**
501
+ * A frozen view of the catalog for ONE turn.
502
+ *
503
+ * The engine advertises a tool list to the model and then dispatches whatever it
504
+ * calls, and those were two separate reads of live state. In between, a server can
505
+ * die or send `notifications/tools/list_changed` — so a tool the model was OFFERED at
506
+ * the top of the turn could be gone by the time it called it, and the model would be
507
+ * told "no such tool" for something we had just told it about. That is a confusing
508
+ * failure to debug and an easy one to blame on the model.
509
+ *
510
+ * Taking one snapshot and using it for both halves makes the turn internally
511
+ * consistent: what was offered stays callable. If the underlying server really has
512
+ * gone, the call fails at dispatch with a real error the model can act on, which is
513
+ * an honest outcome rather than a contradiction.
514
+ *
515
+ * It also pins the exact bytes sent as `tools` for the turn, which is what keeps the
516
+ * cached prompt prefix intact across the turn's steps.
517
+ */
518
+ snapshot(readOnlyOnly = false) {
519
+ const defs = this.allowedCatalog().filter((d) => !readOnlyOnly || d.readOnly);
520
+ // Bind each def to the connection that owned it AT SNAPSHOT TIME, so dispatch does
521
+ // not go looking through a pool that may have changed underneath us.
522
+ const owners = new Map();
523
+ for (const connection of this.connections.values()) {
524
+ for (const def of connection.tools())
525
+ owners.set(mcpToolName(def.server, def.name), connection);
526
+ }
527
+ const deferred = shouldDefer(defs.length);
528
+ // Under the threshold everything is exposed; over it, only what has been activated.
529
+ const exposed = () => (deferred ? defs.filter((d) => this.activated.has(mcpToolName(d.server, d.name))) : defs);
530
+ return {
531
+ deferred,
532
+ catalog: defs,
533
+ toolCount: defs.length,
534
+ exposedSchemas: () => schemasFor(exposed(), readOnlyOnly),
535
+ tokens: () => estimateCatalogTokens(exposed()),
536
+ asTool: (name) => {
537
+ const def = defs.find((d) => mcpToolName(d.server, d.name) === name);
538
+ const connection = owners.get(name);
539
+ if (!def || !connection)
540
+ return undefined;
541
+ return buildTool(name, def, connection, () => this.spillRoot());
542
+ },
543
+ };
544
+ }
545
+ /**
546
+ * Search the catalog and ACTIVATE what matches, so those tools are advertised from
547
+ * the next step onward.
548
+ *
549
+ * Activation is sticky for the session on purpose. A tool the model reached for once
550
+ * it will likely reach for again, and re-hiding it would mean another search round
551
+ * trip and another change to the advertised tool list — which is the thing that costs
552
+ * a prompt-cache prefix. Paying once and keeping it is cheaper than paying repeatedly.
553
+ */
554
+ /**
555
+ * Find deferred tools matching a query. Deliberately does NOT advertise them.
556
+ *
557
+ * It used to, and that was the expensive half of deferral: adding a found tool to the
558
+ * `tools` array changes the bytes the provider hashes, so every request after a search
559
+ * paid to rewrite the whole cached prefix — tools, system AND messages. The saving was
560
+ * a few hundred tokens of schema; the bill was the entire prefix. The caller now hands
561
+ * the model the full schema in the search RESULT instead (see renderResults), which is
562
+ * an appended message the cache does not care about, and `asTool` dispatches against
563
+ * the allowed catalog rather than against what was advertised.
564
+ */
565
+ searchCatalogTools(query) {
566
+ return searchCatalog(query, this.allowedCatalog());
567
+ }
568
+ /** Tool names currently advertised out of a deferred catalog (for diagnostics). */
569
+ activatedNames() {
570
+ return [...this.activated].sort();
571
+ }
572
+ /**
573
+ * Roughly what the live catalog costs in prompt tokens.
574
+ *
575
+ * The compaction bars measure the transcript only, so without this correction a large
576
+ * MCP catalog makes every threshold fire that much too late.
577
+ */
578
+ estimatedTokens() {
579
+ // What is SENT, not what exists: a deferred catalog costs only its activated part,
580
+ // and counting the hidden remainder would make the compaction bars fire early for
581
+ // tokens the model never receives.
582
+ return this.snapshot().tokens();
583
+ }
584
+ /** Every tool across every connected server, in a stable order. */
585
+ catalog() {
586
+ return sortCatalog([...this.connections.values()].filter((c) => c.isConnected()).flatMap((c) => [...c.tools()]));
587
+ }
588
+ /**
589
+ * The catalog minus anything the user has blocked. ONE chokepoint for both governance
590
+ * gates, so a blocked tool cannot be advertised, searched, activated or dispatched —
591
+ * there is no path that consults the raw catalog instead.
592
+ */
593
+ allowedCatalog() {
594
+ return this.catalog().filter((d) => {
595
+ const name = mcpToolName(d.server, d.name);
596
+ return !this.quarantined.has(name) && !this.forbidden.has(name);
597
+ });
598
+ }
599
+ /** How many tools are currently offered (for status lines and budgeting). */
600
+ toolCount() {
601
+ return this.allowedCatalog().length;
602
+ }
603
+ /** Schemas for every MCP tool. `readOnlyOnly` drops mutating tools, mirroring the
604
+ * built-in registry's behaviour in plan mode and read-only sub-agents. */
605
+ toolSchemas(readOnlyOnly = false) {
606
+ return schemasFor(this.allowedCatalog(), readOnlyOnly);
607
+ }
608
+ /**
609
+ * Resolve an advertised MCP name to a runnable Tool, or undefined if we do not serve
610
+ * it. Returning undefined (rather than a tool that errors) is what lets the engine's
611
+ * lookup fall through to its normal "no such tool" path.
612
+ */
613
+ asTool(name) {
614
+ const parsed = parseMcpToolName(name);
615
+ if (!parsed)
616
+ return undefined;
617
+ // Resolve against the ALLOWED catalog, not the raw connections. Reading the
618
+ // connections directly was a hole: a forbidden or quarantined tool stayed
619
+ // dispatchable through this path even though it was gone from every list. A gate
620
+ // that closes three of four doors is not a gate.
621
+ const def = this.allowedCatalog().find((d) => mcpToolName(d.server, d.name) === name);
622
+ if (!def)
623
+ return undefined;
624
+ // Config names are normalized on the way INTO a tool name, so a server called
625
+ // "acme.tools" is looked up by its normalized form here.
626
+ const connection = [...this.connections.values()].find((c) => c.isConnected() && mcpToolName(c.config.name, "x") === mcpToolName(parsed.server, "x"));
627
+ if (!connection)
628
+ return undefined;
629
+ return buildTool(name, def, connection, () => this.spillRoot());
630
+ }
631
+ async dispose() {
632
+ const closing = [...this.connections.values()].map((c) => c.close().catch(() => { }));
633
+ this.connections.clear();
634
+ live.delete(this);
635
+ await Promise.all(closing);
636
+ }
637
+ /**
638
+ * Teardown for a process-exit handler, where nothing can be awaited.
639
+ *
640
+ * `close(true)` reaps a shelled server with a blocking kill; the synchronous
641
+ * part of each close runs before this returns, which is all the exit handler
642
+ * gets. The returned promises are deliberately dropped.
643
+ */
644
+ disposeSync() {
645
+ for (const c of this.connections.values())
646
+ void c.close(true).catch(() => { });
647
+ this.connections.clear();
648
+ live.delete(this);
649
+ }
650
+ }
651
+ /**
652
+ * Does this config name refer to the server the model named?
653
+ *
654
+ * The model only ever sees NORMALIZED names (`acme_tools` for a server configured as
655
+ * `acme.tools`), because that is what survives being embedded in a tool name. So a
656
+ * resource or prompt call naming its server has to be matched through the same
657
+ * normalization, or every server with a dot in its name becomes unreachable.
658
+ */
659
+ function matchesServer(configName, requested) {
660
+ return configName === requested || normalizeServerName(configName) === normalizeServerName(requested);
661
+ }
662
+ /** A short, filename-ish label for a resource URI, used when spilling it to disk. */
663
+ function resourceLabel(uri) {
664
+ const tail = uri.split(/[/?#]/).filter(Boolean).pop() ?? "resource";
665
+ return tail.slice(0, 32);
666
+ }
667
+ /** Kill any servers still running when the process exits. Child processes do not die
668
+ * with their parent on every platform, and an orphaned server holds a port or a lock. */
669
+ function registerCleanup() {
670
+ if (cleanupRegistered)
671
+ return;
672
+ cleanupRegistered = true;
673
+ process.once("exit", () => {
674
+ // Synchronous kill: an async one never reaches the OS from here.
675
+ for (const manager of [...live])
676
+ manager.disposeSync();
677
+ });
678
+ }
679
+ //# sourceMappingURL=manager.js.map