@astralyn/pi 1.0.5 → 1.2.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 (436) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/bundle/chunks/anthropic-messages-NVP5OPIM.js +6 -0
  3. package/dist/bundle/chunks/anthropic.js +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-LDW4FGAQ.js → azure-openai-responses-IBTOZ5MH.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  6. package/dist/bundle/chunks/chunk-4XZYHGVN.js +11 -0
  7. package/dist/bundle/chunks/{chunk-GIG5IHES.js → chunk-5KJH3GGH.js} +1 -1
  8. package/dist/bundle/chunks/chunk-6VYRLG5F.js +2 -0
  9. package/dist/bundle/chunks/{chunk-UCXU7KLO.js → chunk-7HJ6CHUB.js} +1 -1
  10. package/dist/bundle/chunks/chunk-7UBIBLPN.js +2 -0
  11. package/dist/bundle/chunks/{chunk-IE3Z7ESB.js → chunk-A5HU4TE6.js} +418 -362
  12. package/dist/bundle/chunks/{chunk-HDHRZ7GT.js → chunk-B2UOWO4A.js} +1 -1
  13. package/dist/bundle/chunks/chunk-J75WV3FL.js +2 -0
  14. package/dist/bundle/chunks/{chunk-A6ALEEY5.js → chunk-LHSPN2IO.js} +1 -1
  15. package/dist/bundle/chunks/chunk-Q74S7ILH.js +2 -0
  16. package/dist/bundle/chunks/chunk-QDDSFJ35.js +2 -0
  17. package/dist/bundle/chunks/{chunk-IFELM7MW.js → chunk-UQECUCUH.js} +1 -1
  18. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-HGOVFZDC.js +2 -0
  19. package/dist/bundle/chunks/codemode-worker.js +18 -10
  20. package/dist/bundle/chunks/github-copilot.js +1 -1
  21. package/dist/bundle/chunks/{google-generative-ai-S5A5IBU4.js → google-generative-ai-M44TSMAN.js} +1 -1
  22. package/dist/bundle/chunks/{google-vertex-UBRQFDB4.js → google-vertex-3C4OWLFX.js} +1 -1
  23. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  24. package/dist/bundle/chunks/{llama-cpp-classify-R7DQLTQN.js → llama-cpp-classify-H3IZDR5O.js} +2 -2
  25. package/dist/bundle/chunks/mistral-conversations-I5PL4VCZ.js +5 -0
  26. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  27. package/dist/bundle/chunks/openai-codex-responses-O5EUDL54.js +10 -0
  28. package/dist/bundle/chunks/openai-codex.js +1 -1
  29. package/dist/bundle/chunks/{openai-completions-H4VMO7AU.js → openai-completions-X3SSG5BQ.js} +1 -1
  30. package/dist/bundle/chunks/openai-decisions-TVCDIPZX.js +5 -0
  31. package/dist/bundle/chunks/{openai-responses-G4PGNM2H.js → openai-responses-NYPNKVDV.js} +1 -1
  32. package/dist/bundle/chunks/{openrouter-images-YP3YVB54.js → openrouter-images-TDLJV5N4.js} +1 -1
  33. package/dist/bundle/chunks/{pi-messages-HZZ7GEEW.js → pi-messages-ZXNERWJG.js} +1 -1
  34. package/dist/bundle/chunks/typesafe-system-one-LWPJYTJP.js +2 -0
  35. package/dist/bundle/chunks/{virtual-modules-46MSJGKO.js → virtual-modules-OZTPKGU7.js} +1 -1
  36. package/dist/bundle/cli.js +1 -1
  37. package/dist/bundle/index.js +1 -1
  38. package/dist/bundle/rpc-entry.js +1 -1
  39. package/dist/cli/args.d.ts +1 -1
  40. package/dist/cli/args.d.ts.map +1 -1
  41. package/dist/cli/args.js +13 -1
  42. package/dist/cli/args.js.map +1 -1
  43. package/dist/core/agent-session.d.ts +26 -7
  44. package/dist/core/agent-session.d.ts.map +1 -1
  45. package/dist/core/agent-session.js +190 -183
  46. package/dist/core/agent-session.js.map +1 -1
  47. package/dist/core/auth-storage.d.ts.map +1 -1
  48. package/dist/core/auth-storage.js +20 -13
  49. package/dist/core/auth-storage.js.map +1 -1
  50. package/dist/core/bash-executor.d.ts.map +1 -1
  51. package/dist/core/bash-executor.js +40 -45
  52. package/dist/core/bash-executor.js.map +1 -1
  53. package/dist/core/bug-report.d.ts +9 -1
  54. package/dist/core/bug-report.d.ts.map +1 -1
  55. package/dist/core/compaction/utils.d.ts +6 -0
  56. package/dist/core/compaction/utils.d.ts.map +1 -1
  57. package/dist/core/compaction/utils.js +7 -8
  58. package/dist/core/compaction/utils.js.map +1 -1
  59. package/dist/core/context-snapshot.d.ts +4 -1
  60. package/dist/core/context-snapshot.d.ts.map +1 -1
  61. package/dist/core/context-snapshot.js +45 -0
  62. package/dist/core/context-snapshot.js.map +1 -1
  63. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  64. package/dist/core/export-html/tool-renderer.js +2 -0
  65. package/dist/core/export-html/tool-renderer.js.map +1 -1
  66. package/dist/core/extensions/index.d.ts +1 -1
  67. package/dist/core/extensions/index.d.ts.map +1 -1
  68. package/dist/core/extensions/index.js.map +1 -1
  69. package/dist/core/extensions/runner.d.ts +4 -2
  70. package/dist/core/extensions/runner.d.ts.map +1 -1
  71. package/dist/core/extensions/runner.js +2 -3
  72. package/dist/core/extensions/runner.js.map +1 -1
  73. package/dist/core/extensions/types.d.ts +42 -7
  74. package/dist/core/extensions/types.d.ts.map +1 -1
  75. package/dist/core/extensions/types.js.map +1 -1
  76. package/dist/core/key-id-schema.d.ts +6 -0
  77. package/dist/core/key-id-schema.d.ts.map +1 -0
  78. package/dist/core/key-id-schema.js +16 -0
  79. package/dist/core/key-id-schema.js.map +1 -0
  80. package/dist/core/keybinding-registry.d.ts +4 -3
  81. package/dist/core/keybinding-registry.d.ts.map +1 -1
  82. package/dist/core/keybinding-registry.js +4 -3
  83. package/dist/core/keybinding-registry.js.map +1 -1
  84. package/dist/core/keybindings-schema.d.ts +4 -0
  85. package/dist/core/keybindings-schema.d.ts.map +1 -0
  86. package/dist/core/keybindings-schema.js +16 -0
  87. package/dist/core/keybindings-schema.js.map +1 -0
  88. package/dist/core/keybindings.d.ts +258 -81
  89. package/dist/core/keybindings.d.ts.map +1 -1
  90. package/dist/core/keybindings.js +27 -24
  91. package/dist/core/keybindings.js.map +1 -1
  92. package/dist/core/model-config.d.ts +713 -180
  93. package/dist/core/model-config.d.ts.map +1 -1
  94. package/dist/core/model-config.js +18 -179
  95. package/dist/core/model-config.js.map +1 -1
  96. package/dist/core/model-runtime.d.ts.map +1 -1
  97. package/dist/core/model-runtime.js +13 -7
  98. package/dist/core/model-runtime.js.map +1 -1
  99. package/dist/core/nested-tool-calls.d.ts +1 -0
  100. package/dist/core/nested-tool-calls.d.ts.map +1 -1
  101. package/dist/core/nested-tool-calls.js +1 -0
  102. package/dist/core/nested-tool-calls.js.map +1 -1
  103. package/dist/core/package-manager.d.ts.map +1 -1
  104. package/dist/core/package-manager.js +3 -3
  105. package/dist/core/package-manager.js.map +1 -1
  106. package/dist/core/project-trust.d.ts.map +1 -1
  107. package/dist/core/project-trust.js +2 -1
  108. package/dist/core/project-trust.js.map +1 -1
  109. package/dist/core/remote-catalog-provider.d.ts +12 -1
  110. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  111. package/dist/core/remote-catalog-provider.js +82 -24
  112. package/dist/core/remote-catalog-provider.js.map +1 -1
  113. package/dist/core/resource-loader.d.ts.map +1 -1
  114. package/dist/core/resource-loader.js +7 -2
  115. package/dist/core/resource-loader.js.map +1 -1
  116. package/dist/core/sdk.d.ts +4 -0
  117. package/dist/core/sdk.d.ts.map +1 -1
  118. package/dist/core/sdk.js +17 -5
  119. package/dist/core/sdk.js.map +1 -1
  120. package/dist/core/settings-defaults.d.ts +65 -0
  121. package/dist/core/settings-defaults.d.ts.map +1 -0
  122. package/dist/core/settings-defaults.js +66 -0
  123. package/dist/core/settings-defaults.js.map +1 -0
  124. package/dist/core/settings-manager.d.ts +15 -147
  125. package/dist/core/settings-manager.d.ts.map +1 -1
  126. package/dist/core/settings-manager.js +85 -57
  127. package/dist/core/settings-manager.js.map +1 -1
  128. package/dist/core/settings-schema.d.ts +231 -0
  129. package/dist/core/settings-schema.d.ts.map +1 -0
  130. package/dist/core/settings-schema.js +294 -0
  131. package/dist/core/settings-schema.js.map +1 -0
  132. package/dist/core/tasks/session.d.ts +9 -0
  133. package/dist/core/tasks/session.d.ts.map +1 -1
  134. package/dist/core/tasks/session.js +21 -0
  135. package/dist/core/tasks/session.js.map +1 -1
  136. package/dist/core/tools/render-utils.d.ts +18 -0
  137. package/dist/core/tools/render-utils.d.ts.map +1 -1
  138. package/dist/core/tools/render-utils.js +46 -0
  139. package/dist/core/tools/render-utils.js.map +1 -1
  140. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  141. package/dist/core/tools/renderers/bash.js +21 -45
  142. package/dist/core/tools/renderers/bash.js.map +1 -1
  143. package/dist/core/tools/renderers/edit.js +1 -1
  144. package/dist/core/tools/renderers/edit.js.map +1 -1
  145. package/dist/extensions/btw/keybindings.d.ts +2 -2
  146. package/dist/extensions/btw/keybindings.d.ts.map +1 -1
  147. package/dist/extensions/btw/keybindings.js.map +1 -1
  148. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  149. package/dist/extensions/codemode/execute.js +115 -40
  150. package/dist/extensions/codemode/execute.js.map +1 -1
  151. package/dist/extensions/codemode/index.d.ts.map +1 -1
  152. package/dist/extensions/codemode/index.js +3 -7
  153. package/dist/extensions/codemode/index.js.map +1 -1
  154. package/dist/extensions/codemode/renderer.d.ts +2 -2
  155. package/dist/extensions/codemode/renderer.d.ts.map +1 -1
  156. package/dist/extensions/codemode/renderer.js +9 -3
  157. package/dist/extensions/codemode/renderer.js.map +1 -1
  158. package/dist/extensions/codemode/tool.d.ts +6 -12
  159. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  160. package/dist/extensions/codemode/tool.js +46 -34
  161. package/dist/extensions/codemode/tool.js.map +1 -1
  162. package/dist/extensions/index.js +2 -2
  163. package/dist/extensions/index.js.map +1 -1
  164. package/dist/extensions/list-keybindings.d.ts +2 -2
  165. package/dist/extensions/list-keybindings.d.ts.map +1 -1
  166. package/dist/extensions/list-keybindings.js.map +1 -1
  167. package/dist/extensions/llama/provider.d.ts.map +1 -1
  168. package/dist/extensions/llama/provider.js +34 -11
  169. package/dist/extensions/llama/provider.js.map +1 -1
  170. package/dist/extensions/mcp/cli.js +22 -31
  171. package/dist/extensions/mcp/cli.js.map +1 -1
  172. package/dist/extensions/mcp/index.d.ts.map +1 -1
  173. package/dist/extensions/mcp/index.js +187 -95
  174. package/dist/extensions/mcp/index.js.map +1 -1
  175. package/dist/extensions/mcp/oauth.d.ts +5 -3
  176. package/dist/extensions/mcp/oauth.d.ts.map +1 -1
  177. package/dist/extensions/mcp/oauth.js +36 -10
  178. package/dist/extensions/mcp/oauth.js.map +1 -1
  179. package/dist/extensions/mcp/resources.d.ts +0 -6
  180. package/dist/extensions/mcp/resources.d.ts.map +1 -1
  181. package/dist/extensions/mcp/resources.js +1 -1
  182. package/dist/extensions/mcp/resources.js.map +1 -1
  183. package/dist/extensions/mcp/runtime.d.ts +3 -6
  184. package/dist/extensions/mcp/runtime.d.ts.map +1 -1
  185. package/dist/extensions/mcp/runtime.js +4 -39
  186. package/dist/extensions/mcp/runtime.js.map +1 -1
  187. package/dist/extensions/mcp/tools.d.ts +2 -3
  188. package/dist/extensions/mcp/tools.d.ts.map +1 -1
  189. package/dist/extensions/mcp/tools.js +62 -6
  190. package/dist/extensions/mcp/tools.js.map +1 -1
  191. package/dist/extensions/mcp/ui.d.ts +3 -3
  192. package/dist/extensions/mcp/ui.d.ts.map +1 -1
  193. package/dist/extensions/mcp/ui.js +10 -2
  194. package/dist/extensions/mcp/ui.js.map +1 -1
  195. package/dist/extensions/provider/keybindings.d.ts +2 -2
  196. package/dist/extensions/provider/keybindings.d.ts.map +1 -1
  197. package/dist/extensions/provider/keybindings.js.map +1 -1
  198. package/dist/extensions/search/code-search.d.ts +27 -0
  199. package/dist/extensions/search/code-search.d.ts.map +1 -0
  200. package/dist/extensions/search/code-search.js +120 -0
  201. package/dist/extensions/search/code-search.js.map +1 -0
  202. package/dist/extensions/search/command.d.ts +5 -0
  203. package/dist/extensions/search/command.d.ts.map +1 -0
  204. package/dist/extensions/search/command.js +69 -0
  205. package/dist/extensions/search/command.js.map +1 -0
  206. package/dist/extensions/search/credential.d.ts +25 -0
  207. package/dist/extensions/search/credential.d.ts.map +1 -0
  208. package/dist/extensions/search/credential.js +117 -0
  209. package/dist/extensions/search/credential.js.map +1 -0
  210. package/dist/extensions/search/devin.d.ts +34 -0
  211. package/dist/extensions/search/devin.d.ts.map +1 -0
  212. package/dist/extensions/search/devin.js +210 -0
  213. package/dist/extensions/search/devin.js.map +1 -0
  214. package/dist/extensions/search/index.d.ts +5 -0
  215. package/dist/extensions/search/index.d.ts.map +1 -0
  216. package/dist/extensions/search/index.js +113 -0
  217. package/dist/extensions/search/index.js.map +1 -0
  218. package/dist/extensions/search/panel.d.ts +53 -0
  219. package/dist/extensions/search/panel.d.ts.map +1 -0
  220. package/dist/extensions/search/panel.js +157 -0
  221. package/dist/extensions/search/panel.js.map +1 -0
  222. package/dist/extensions/search/prompt.d.ts +14 -0
  223. package/dist/extensions/search/prompt.d.ts.map +1 -0
  224. package/dist/extensions/search/prompt.js +221 -0
  225. package/dist/extensions/search/prompt.js.map +1 -0
  226. package/dist/extensions/search/render.d.ts +28 -0
  227. package/dist/extensions/search/render.d.ts.map +1 -0
  228. package/dist/extensions/search/render.js +86 -0
  229. package/dist/extensions/search/render.js.map +1 -0
  230. package/dist/extensions/search/workspace.d.ts +39 -0
  231. package/dist/extensions/search/workspace.d.ts.map +1 -0
  232. package/dist/extensions/search/workspace.js +363 -0
  233. package/dist/extensions/search/workspace.js.map +1 -0
  234. package/dist/extensions/tool-search/index.d.ts.map +1 -1
  235. package/dist/extensions/tool-search/index.js +1 -6
  236. package/dist/extensions/tool-search/index.js.map +1 -1
  237. package/dist/extensions/tool-search/tool.d.ts +0 -2
  238. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  239. package/dist/extensions/tool-search/tool.js +2 -4
  240. package/dist/extensions/tool-search/tool.js.map +1 -1
  241. package/dist/index.d.ts +1 -1
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js.map +1 -1
  244. package/dist/main.js +3 -3
  245. package/dist/main.js.map +1 -1
  246. package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
  247. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  248. package/dist/modes/interactive/components/bash-execution.js +14 -14
  249. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  250. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
  251. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/branch-summary-message.js +5 -2
  253. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  254. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
  255. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  256. package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
  257. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  258. package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
  259. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  260. package/dist/modes/interactive/components/custom-entry.js +7 -2
  261. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  262. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  263. package/dist/modes/interactive/components/custom-message.js +1 -0
  264. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  265. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  266. package/dist/modes/interactive/components/settings-selector.js +4 -3
  267. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  268. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
  269. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  270. package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
  271. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  272. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  273. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  274. package/dist/modes/interactive/components/status-indicator.js +2 -2
  275. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  276. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  277. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  278. package/dist/modes/interactive/interactive-mode.js +63 -64
  279. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  280. package/dist/modes/interactive/program-status-reporter.d.ts +34 -0
  281. package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -0
  282. package/dist/modes/interactive/program-status-reporter.js +107 -0
  283. package/dist/modes/interactive/program-status-reporter.js.map +1 -0
  284. package/dist/modes/interactive/tasks/host-hooks.d.ts +14 -0
  285. package/dist/modes/interactive/tasks/host-hooks.d.ts.map +1 -0
  286. package/dist/modes/interactive/tasks/host-hooks.js +31 -0
  287. package/dist/modes/interactive/tasks/host-hooks.js.map +1 -0
  288. package/dist/modes/interactive/theme/dark.json +1 -1
  289. package/dist/modes/interactive/theme/light.json +1 -1
  290. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  291. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  292. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  293. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  294. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  295. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  296. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  297. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  298. package/dist/modes/interactive/theme/theme.d.ts +13 -16
  299. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  300. package/dist/modes/interactive/theme/theme.js +23 -31
  301. package/dist/modes/interactive/theme/theme.js.map +1 -1
  302. package/dist/modes/interactive/tool-view/tool-execution.d.ts +5 -0
  303. package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +1 -1
  304. package/dist/modes/interactive/tool-view/tool-execution.js +7 -0
  305. package/dist/modes/interactive/tool-view/tool-execution.js.map +1 -1
  306. package/dist/modes/print-mode.d.ts.map +1 -1
  307. package/dist/modes/print-mode.js +4 -0
  308. package/dist/modes/print-mode.js.map +1 -1
  309. package/dist/package-manager-cli.d.ts.map +1 -1
  310. package/dist/package-manager-cli.js +24 -0
  311. package/dist/package-manager-cli.js.map +1 -1
  312. package/dist/utils/ansi.d.ts +8 -0
  313. package/dist/utils/ansi.d.ts.map +1 -1
  314. package/dist/utils/ansi.js +30 -11
  315. package/dist/utils/ansi.js.map +1 -1
  316. package/dist/utils/clipboard.d.ts.map +1 -1
  317. package/dist/utils/clipboard.js +18 -12
  318. package/dist/utils/clipboard.js.map +1 -1
  319. package/dist/utils/image-resize-core.d.ts +12 -0
  320. package/dist/utils/image-resize-core.d.ts.map +1 -1
  321. package/dist/utils/image-resize-core.js +5 -0
  322. package/dist/utils/image-resize-core.js.map +1 -1
  323. package/dist/utils/image-resize-worker.js +3 -2
  324. package/dist/utils/image-resize-worker.js.map +1 -1
  325. package/dist/utils/image-resize.d.ts.map +1 -1
  326. package/dist/utils/image-resize.js +9 -10
  327. package/dist/utils/image-resize.js.map +1 -1
  328. package/docs/bundled/README.md +1 -1
  329. package/docs/bundled/extensions/provider.md +1 -1
  330. package/docs/bundled/extensions/question.md +1 -1
  331. package/docs/bundled/extensions/search.md +51 -0
  332. package/docs/bundled/extensions/statusline.md +15 -41
  333. package/docs/codemode.md +24 -4
  334. package/docs/compaction.md +2 -2
  335. package/docs/docs.json +0 -4
  336. package/docs/environment-variables.md +3 -1
  337. package/docs/extensions.md +15 -5
  338. package/docs/json.md +2 -2
  339. package/docs/keybindings.md +1 -0
  340. package/docs/llama-cpp.md +8 -1
  341. package/docs/mcp.md +1 -1
  342. package/docs/models.md +5 -1
  343. package/docs/providers.md +33 -0
  344. package/docs/rpc.md +6 -3
  345. package/docs/session-format.md +1 -0
  346. package/docs/settings.md +5 -5
  347. package/docs/terminal-setup.md +14 -0
  348. package/docs/themes.md +4 -2
  349. package/docs/tui.md +1 -1
  350. package/docs/usage.md +7 -3
  351. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  352. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  353. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  354. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  355. package/examples/extensions/gondolin/package-lock.json +2 -2
  356. package/examples/extensions/gondolin/package.json +1 -1
  357. package/examples/extensions/overlay-test.ts +2 -2
  358. package/examples/extensions/plan-mode/index.ts +6 -2
  359. package/examples/extensions/sandbox/package-lock.json +2 -2
  360. package/examples/extensions/sandbox/package.json +1 -1
  361. package/examples/extensions/with-deps/package-lock.json +2 -2
  362. package/examples/extensions/with-deps/package.json +1 -1
  363. package/npm-shrinkwrap.json +54 -102
  364. package/package.json +10 -20
  365. package/schemas/keybindings.schema.json +453 -0
  366. package/schemas/models.schema.json +1157 -0
  367. package/schemas/settings.schema.json +782 -0
  368. package/schemas/theme.schema.json +365 -0
  369. package/dist/bundle/chunks/anthropic-messages-YIOJIMVL.js +0 -6
  370. package/dist/bundle/chunks/chunk-57XI7YJH.js +0 -2
  371. package/dist/bundle/chunks/chunk-JI4WGISU.js +0 -2
  372. package/dist/bundle/chunks/chunk-TCQ4HHQD.js +0 -2
  373. package/dist/bundle/chunks/chunk-TXJO4AJ7.js +0 -11
  374. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-OWU5SM6J.js +0 -2
  375. package/dist/bundle/chunks/mistral-conversations-A2N5FSOV.js +0 -5
  376. package/dist/bundle/chunks/openai-codex-responses-Q6JHCWLE.js +0 -10
  377. package/dist/bundle/chunks/typesafe-system-one-KYKKNU3E.js +0 -2
  378. package/dist/extensions/web-search/auth.d.ts +0 -12
  379. package/dist/extensions/web-search/auth.d.ts.map +0 -1
  380. package/dist/extensions/web-search/auth.js +0 -90
  381. package/dist/extensions/web-search/auth.js.map +0 -1
  382. package/dist/extensions/web-search/constants.d.ts +0 -13
  383. package/dist/extensions/web-search/constants.d.ts.map +0 -1
  384. package/dist/extensions/web-search/constants.js +0 -26
  385. package/dist/extensions/web-search/constants.js.map +0 -1
  386. package/dist/extensions/web-search/execute.d.ts +0 -9
  387. package/dist/extensions/web-search/execute.d.ts.map +0 -1
  388. package/dist/extensions/web-search/execute.js +0 -96
  389. package/dist/extensions/web-search/execute.js.map +0 -1
  390. package/dist/extensions/web-search/format.d.ts +0 -21
  391. package/dist/extensions/web-search/format.d.ts.map +0 -1
  392. package/dist/extensions/web-search/format.js +0 -117
  393. package/dist/extensions/web-search/format.js.map +0 -1
  394. package/dist/extensions/web-search/index.d.ts +0 -6
  395. package/dist/extensions/web-search/index.d.ts.map +0 -1
  396. package/dist/extensions/web-search/index.js +0 -50
  397. package/dist/extensions/web-search/index.js.map +0 -1
  398. package/dist/extensions/web-search/providers/deepseek.d.ts +0 -13
  399. package/dist/extensions/web-search/providers/deepseek.d.ts.map +0 -1
  400. package/dist/extensions/web-search/providers/deepseek.js +0 -110
  401. package/dist/extensions/web-search/providers/deepseek.js.map +0 -1
  402. package/dist/extensions/web-search/providers/http.d.ts +0 -4
  403. package/dist/extensions/web-search/providers/http.d.ts.map +0 -1
  404. package/dist/extensions/web-search/providers/http.js +0 -42
  405. package/dist/extensions/web-search/providers/http.js.map +0 -1
  406. package/dist/extensions/web-search/providers/minimax.d.ts +0 -12
  407. package/dist/extensions/web-search/providers/minimax.d.ts.map +0 -1
  408. package/dist/extensions/web-search/providers/minimax.js +0 -61
  409. package/dist/extensions/web-search/providers/minimax.js.map +0 -1
  410. package/dist/extensions/web-search/render.d.ts +0 -15
  411. package/dist/extensions/web-search/render.d.ts.map +0 -1
  412. package/dist/extensions/web-search/render.js +0 -142
  413. package/dist/extensions/web-search/render.js.map +0 -1
  414. package/dist/extensions/web-search/results.d.ts +0 -35
  415. package/dist/extensions/web-search/results.d.ts.map +0 -1
  416. package/dist/extensions/web-search/results.js +0 -167
  417. package/dist/extensions/web-search/results.js.map +0 -1
  418. package/dist/extensions/web-search/schema.d.ts +0 -10
  419. package/dist/extensions/web-search/schema.d.ts.map +0 -1
  420. package/dist/extensions/web-search/schema.js +0 -36
  421. package/dist/extensions/web-search/schema.js.map +0 -1
  422. package/dist/extensions/web-search/types.d.ts +0 -51
  423. package/dist/extensions/web-search/types.d.ts.map +0 -1
  424. package/dist/extensions/web-search/types.js +0 -5
  425. package/dist/extensions/web-search/types.js.map +0 -1
  426. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  427. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  428. package/dist/modes/interactive/theme/theme-json.js +0 -132
  429. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  430. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  431. package/docs/bundled/extensions/web-search.md +0 -46
  432. package/examples/plugins/pi-example-plugin/README.md +0 -38
  433. package/examples/plugins/pi-example-plugin/package.json +0 -10
  434. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  435. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  436. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
@@ -131,13 +131,14 @@ export function renderServersSection(servers) {
131
131
  return [intro, ...lines, ...omitted(listed.length - kept)].join("\n");
132
132
  }
133
133
  /**
134
- * Whether a codemode script needs the server: it names the server's namespace, or searches,
135
- * enumerates, or describes tools or namespaces, which may name the server in other forms.
134
+ * Whether a call that reaches `reach` (see `ToolDefinition.reachesTools`) needs the server: it
135
+ * reaches every tool, or one of its words contains the server's namespace.
136
136
  */
137
- function scriptNeedsServer(code, server) {
138
- if (/\b(searchTools|describeNamespace|describeTool|ALL_TOOLS)\b/.test(code))
137
+ function reachesServer(reach, server) {
138
+ if (reach === "all")
139
139
  return true;
140
- return code.includes(mcpNamespace(server));
140
+ const namespace = mcpNamespace(server);
141
+ return reach.some((word) => word.includes(namespace));
141
142
  }
142
143
  /** Short state for lists and the startup report. `withError` appends the first line of a failure. */
143
144
  function describeState(server, withError = true) {
@@ -152,10 +153,8 @@ function describeState(server, withError = true) {
152
153
  case "failed":
153
154
  return withError ? `failed: ${firstLine(connection.error ?? "unknown error")}` : "failed";
154
155
  case "connected": {
155
- const { tools, resources } = connection;
156
- const count = resources.length;
157
- const resourceCount = count > 0 ? ` · ${count} resource${count === 1 ? "" : "s"}` : "";
158
- return `connected · ${tools.length} tool${tools.length === 1 ? "" : "s"}${resourceCount}`;
156
+ const { tools, hasResources } = connection;
157
+ return `connected · ${tools.length} tool${tools.length === 1 ? "" : "s"}${hasResources ? " · resources" : ""}`;
159
158
  }
160
159
  case "connecting":
161
160
  return "connecting…";
@@ -197,8 +196,6 @@ export function createMcpExtension(options = {}) {
197
196
  let projectConfig;
198
197
  /** Registered servers that `mcp.json` overrides, shown in `/mcp`. */
199
198
  let overridden = [];
200
- /** Between session_start and session_shutdown. Registrations before that are read on session_start. */
201
- let sessionActive = false;
202
199
  let autoEnableCodemode = true;
203
200
  /** Whether the "codemode tools unreachable" warning was shown since the session started. */
204
201
  let warnedUnreachable = false;
@@ -206,8 +203,6 @@ export function createMcpExtension(options = {}) {
206
203
  /** Whether a prompt already waited for the startup connections since the session started. */
207
204
  let waitedForStartup = false;
208
205
  const startupWaitMs = options.startupWaitMs ?? DEFAULT_STARTUP_WAIT_MS;
209
- /** Bumped on every session start and shutdown so a runtime load that resolves late is dropped. */
210
- let generation = 0;
211
206
  /** Working directory of the session, for stdio servers. */
212
207
  let sessionCwd = process.cwd();
213
208
  let credentials = options.credentials;
@@ -219,6 +214,23 @@ export function createMcpExtension(options = {}) {
219
214
  ((entry, patch) => updateMcpServerConfig(entry.override ?? entry.source, entry.name, patch, {
220
215
  override: entry.override !== undefined,
221
216
  }));
217
+ /**
218
+ * Lifetime of the current session: aborted on session_shutdown, and before the first session_start
219
+ * (registrations before that are read on session_start). Work captures `session.signal` when it
220
+ * starts and drops late results once it aborted, for example a runtime load that resolves after
221
+ * shutdown. Work that outlives the command that started it also stops on the signal and is
222
+ * registered with `track`, so shutdown can wait for its cleanup.
223
+ */
224
+ let session = new AbortController();
225
+ session.abort();
226
+ /** Work registered with `track`: manager actions opening or closing connections, and sign-ins. */
227
+ const backgroundActions = new Set();
228
+ const track = (work) => {
229
+ const task = work.then(() => undefined, () => undefined);
230
+ backgroundActions.add(task);
231
+ void task.finally(() => backgroundActions.delete(task));
232
+ return work;
233
+ };
222
234
  const listeners = new Set();
223
235
  const emitChange = () => {
224
236
  for (const listener of listeners)
@@ -274,6 +286,8 @@ export function createMcpExtension(options = {}) {
274
286
  const description = entry.config.description?.trim();
275
287
  const namespace = {
276
288
  name: mcpNamespace(server),
289
+ // `describeNamespace("docs")` finds `mcp__docs`.
290
+ aliases: [server],
277
291
  ...(description ? { description } : {}),
278
292
  ...(connection.instructions ? { instructions: connection.instructions } : {}),
279
293
  };
@@ -301,8 +315,22 @@ export function createMcpExtension(options = {}) {
301
315
  exposure: getMcpToolExposure(entry.config, tool.name),
302
316
  namespace,
303
317
  timeoutMs: connection.timeoutMs,
304
- getClient: async () => connection,
305
- readableResources: () => resourceServers().includes(connection),
318
+ getClient: async () => {
319
+ // A prepared call can outlive this definition's connection while the readiness
320
+ // hook waits for a disable/re-enable. Resolve the replacement at execution time.
321
+ const current = findServer(server);
322
+ if (!current || !isEnabled(current))
323
+ throw new Error(`MCP server "${server}" is disabled.`);
324
+ const client = current.connection;
325
+ if (!client)
326
+ throw new Error(`MCP server "${server}" is still starting.`);
327
+ if (getMcpToolExposure(current.entry.config, tool.name) === "hidden" ||
328
+ (client.state === "connected" && !client.tools.some((offered) => offered.name === tool.name))) {
329
+ throw new Error(`MCP tool "${server}/${tool.name}" is no longer available.`);
330
+ }
331
+ return client;
332
+ },
333
+ readableResources: () => resourceServers().some((current) => current.name === server),
306
334
  });
307
335
  definitions.set(definition.name, definition);
308
336
  pi.registerTool(definition);
@@ -352,19 +380,6 @@ export function createMcpExtension(options = {}) {
352
380
  pi.setActiveTools(pi.getActiveTools().filter((name) => !names.has(name)));
353
381
  }
354
382
  };
355
- /**
356
- * Which of the tools are the bundled codemode and tool_search tools. Other extensions' tools of
357
- * the same names cannot reach MCP tools. The owning extensions identify their tools through the
358
- * event bus, so this extension does not import their internals.
359
- */
360
- const discoverOrchestrators = (tools) => {
361
- const candidates = new Map(tools.map((tool) => [tool.name, tool.parameters]));
362
- pi.events.emit("pi:discover-tool-orchestrators", candidates);
363
- return {
364
- codemode: candidates.get(CODEMODE_TOOL_NAME) === true,
365
- toolSearch: candidates.get(TOOL_SEARCH_TOOL_NAME) === true,
366
- };
367
- };
368
383
  /**
369
384
  * Tools that are not declared to the model are reached through the codemode tool (scripts call
370
385
  * them) or the tool_search tool (it declares them). Either reaches every such tool. Activate the
@@ -384,8 +399,12 @@ export function createMcpExtension(options = {}) {
384
399
  const needsToolSearch = exposures.has("deferred");
385
400
  if (!needsCodemode && !needsToolSearch)
386
401
  return;
387
- // Other extensions' tools of the same names cannot reach MCP tools, so never activate them.
388
- const { codemode: hasCodemode, toolSearch: hasToolSearch } = discoverOrchestrators(pi.getAllTools());
402
+ // Only a tool of these names that declares `reachesTools` can reach MCP tools; another
403
+ // extension's tool of the same name without it is never activated.
404
+ const tools = pi.getAllTools();
405
+ const hasReachingTool = (name) => tools.some((tool) => tool.name === name && tool.reachesTools);
406
+ const hasCodemode = hasReachingTool(CODEMODE_TOOL_NAME);
407
+ const hasToolSearch = hasReachingTool(TOOL_SEARCH_TOOL_NAME);
389
408
  const active = pi.getActiveTools();
390
409
  const activate = [];
391
410
  if (needsCodemode && hasCodemode && autoEnableCodemode && !active.includes(CODEMODE_TOOL_NAME)) {
@@ -434,8 +453,10 @@ export function createMcpExtension(options = {}) {
434
453
  ensureDiscoveryActive(ctx);
435
454
  };
436
455
  /** Create the server's connection, loading the MCP runtime on first use. */
437
- const createConnection = async (server) => {
456
+ const createConnection = async (server, isCurrent) => {
438
457
  const runtime = await loadMcpRuntime();
458
+ if (!isCurrent())
459
+ return undefined;
439
460
  const connection = new runtime.McpServerConnection({
440
461
  entry: server.entry,
441
462
  cwd: sessionCwd,
@@ -443,8 +464,16 @@ export function createMcpExtension(options = {}) {
443
464
  credentials: getCredentials(runtime),
444
465
  providerToken: async (provider) => modelRegistry?.getApiKeyForProvider(provider),
445
466
  log: getServerLog(runtime),
446
- onTools: registerTools,
447
- onChange: onConnectionChange,
467
+ onTools: (connection) => {
468
+ if (isCurrent())
469
+ registerTools(connection);
470
+ },
471
+ onChange: (connection) => {
472
+ if (isCurrent())
473
+ onConnectionChange(connection);
474
+ else
475
+ tokensAtSignIn.delete(connection);
476
+ },
448
477
  });
449
478
  server.connection = connection;
450
479
  emitChange();
@@ -452,37 +481,48 @@ export function createMcpExtension(options = {}) {
452
481
  };
453
482
  /**
454
483
  * Connect the server in the background. `server.ready` settles when it connected or failed;
455
- * failures show in its state. It starts after `after`; `isCurrent` stops it when the session ended
484
+ * failures show in its state. It starts after `after`, unless disabled, replaced, or shut down
456
485
  * meanwhile. The returned promise rejects when the MCP runtime cannot be loaded.
457
486
  */
458
- const startConnection = (server, isCurrent, after) => {
487
+ const startConnection = (server, after) => {
488
+ const { signal } = session;
489
+ const attempt = Symbol();
490
+ server.attempt = attempt;
491
+ const isCurrent = () => !signal.aborted && server.attempt === attempt && isEnabled(server) && servers.includes(server);
459
492
  const ready = (async () => {
460
493
  await after;
461
494
  if (!isCurrent())
462
495
  return;
463
- const connection = await createConnection(server);
464
- if (!isCurrent())
496
+ const connection = await createConnection(server, isCurrent);
497
+ if (!connection || !isCurrent())
465
498
  return;
466
499
  await connection.getClient().catch(() => undefined);
467
500
  })();
468
501
  server.ready = ready.catch(() => undefined);
469
502
  return ready;
470
503
  };
471
- /** Wait for servers still connecting, until they settle or `signal` aborts. */
504
+ /** Wait for the latest connection attempts, including ones queued while waiting, or an abort. */
472
505
  const waitForServers = async (waiting, signal) => {
473
- const ready = waiting.flatMap((server) => (server.ready ? [server.ready] : []));
474
- if (ready.length === 0 || signal?.aborted)
475
- return;
476
- let onAbort;
477
- await Promise.race([
478
- Promise.all(ready),
479
- new Promise((resolve) => {
480
- onAbort = () => resolve();
481
- signal?.addEventListener("abort", onAbort, { once: true });
482
- }),
483
- ]);
484
- if (onAbort)
485
- signal?.removeEventListener("abort", onAbort);
506
+ for (;;) {
507
+ const ready = waiting.flatMap((server) => isEnabled(server) && servers.includes(server) && server.ready ? [{ server, ready: server.ready }] : []);
508
+ if (ready.length === 0 || signal?.aborted)
509
+ return;
510
+ let onAbort;
511
+ await Promise.race([
512
+ Promise.all(ready.map((attempt) => attempt.ready)),
513
+ new Promise((resolve) => {
514
+ onAbort = () => resolve();
515
+ signal?.addEventListener("abort", onAbort, { once: true });
516
+ }),
517
+ ]);
518
+ if (onAbort)
519
+ signal?.removeEventListener("abort", onAbort);
520
+ if (signal?.aborted ||
521
+ ready.every((attempt) => !isEnabled(attempt.server) ||
522
+ !servers.includes(attempt.server) ||
523
+ attempt.ready === attempt.server.ready))
524
+ return;
525
+ }
486
526
  };
487
527
  /**
488
528
  * One message for everything that needs the user after startup, or only for `only`, servers
@@ -517,20 +557,23 @@ export function createMcpExtension(options = {}) {
517
557
  server.entry = { ...entry, config: { ...entry.config, ...patch } };
518
558
  return undefined;
519
559
  };
520
- const signIn = async (server, prompt) => {
560
+ /** Sign in, then reconnect. Returns an error message on failure. `cancel` or the session's end stops the sign-in. */
561
+ const signIn = async (server, prompt, cancel) => {
521
562
  const connection = server.connection;
522
563
  const url = connection?.oauthUrl;
523
564
  if (!connection || !url)
524
565
  return `MCP server "${server.entry.name}" does not use OAuth.`;
525
566
  const runtime = await loadMcpRuntime();
567
+ const signal = cancel ? AbortSignal.any([session.signal, cancel]) : session.signal;
526
568
  try {
527
- await runtime.signInMcpServer({
569
+ await track(runtime.signInMcpServer({
528
570
  serverUrl: url,
529
571
  store: getCredentials(runtime).forServer(server.entry.name, url),
530
572
  settings: connection.oauthSettings(),
531
573
  challenge: connection.challenge,
532
574
  prompt,
533
- });
575
+ signal,
576
+ }));
534
577
  }
535
578
  catch (error) {
536
579
  if (error instanceof runtime.McpSignInCancelledError)
@@ -560,8 +603,14 @@ export function createMcpExtension(options = {}) {
560
603
  const connection = server.connection;
561
604
  if (!connection)
562
605
  return `MCP server "${server.entry.name}" is disabled.`;
563
- try {
606
+ const previous = server.ready;
607
+ const ready = (async () => {
608
+ await previous;
564
609
  await connection.reconnect();
610
+ })();
611
+ server.ready = ready.catch(() => undefined);
612
+ try {
613
+ await ready;
565
614
  return undefined;
566
615
  }
567
616
  catch (error) {
@@ -575,13 +624,26 @@ export function createMcpExtension(options = {}) {
575
624
  return failed;
576
625
  if (!enabled) {
577
626
  const connection = server.connection;
627
+ server.attempt = undefined;
578
628
  server.connection = undefined;
629
+ if (connection) {
630
+ // A reconnect may already have detached its old client while awaiting transport
631
+ // shutdown. Closing the connection alone does not wait for that reconnect.
632
+ server.closing = Promise.all([connection.close(), server.ready]).then(() => undefined);
633
+ }
634
+ const closing = server.closing;
579
635
  hideTools(server.entry.name);
580
636
  emitChange();
581
- await connection?.close();
637
+ try {
638
+ await closing;
639
+ }
640
+ finally {
641
+ if (server.closing === closing)
642
+ server.closing = undefined;
643
+ }
582
644
  return undefined;
583
645
  }
584
- await startConnection(server, () => true);
646
+ await startConnection(server, server.closing);
585
647
  return undefined;
586
648
  };
587
649
  const setExposure = (server, exposure) => {
@@ -729,11 +791,13 @@ export function createMcpExtension(options = {}) {
729
791
  return undefined;
730
792
  return setExposure(server, choice);
731
793
  };
732
- /** Sign in with the manager view's sign-in screen, which shows the URL with a copy key. */
794
+ /** Sign in with the manager view's sign-in screen, which shows the URL with a copy key and cancels with Esc. */
733
795
  const signInWithUi = (ui, server) => {
734
796
  const title = `Sign in to ${server.entry.name}`;
797
+ const cancel = new AbortController();
798
+ const status = (message) => ui.status(title, message, () => cancel.abort());
735
799
  let authorizationUrl = "";
736
- ui.status(title, "Contacting the authorization server…");
800
+ status("Contacting the authorization server…");
737
801
  return signIn(server, {
738
802
  showAuthorizationUrl: (url) => {
739
803
  authorizationUrl = url.href;
@@ -741,22 +805,38 @@ export function createMcpExtension(options = {}) {
741
805
  },
742
806
  promptForRedirectUrl: async (signal) => {
743
807
  const value = await ui.redirectUrl(title, authorizationUrl, signal);
744
- ui.status(title, "Connecting…");
808
+ status("Connecting…");
745
809
  return value;
746
810
  },
747
- });
811
+ }, cancel.signal);
812
+ };
813
+ /** Keep the subscribed menu usable while a connection opens or closes. */
814
+ const runInBackground = (ctx, server, operation) => {
815
+ const ready = operation();
816
+ const attempt = server.attempt;
817
+ const { signal } = session;
818
+ const finish = (message) => {
819
+ if (signal.aborted || server.attempt !== attempt || !servers.includes(server))
820
+ return;
821
+ server.message = message;
822
+ ensureDiscoveryActive(ctx);
823
+ emitChange();
824
+ };
825
+ void track(ready.then(finish, (error) => finish(errorMessage(error))));
748
826
  };
749
827
  const runAction = async (ui, ctx, server, action) => {
750
- const { name } = server.entry;
828
+ const { signal } = session;
751
829
  let message;
752
830
  switch (action) {
753
831
  case "signin":
754
832
  message = await signInWithUi(ui, server);
755
833
  break;
756
834
  case "reconnect":
757
- // A failure shows as the connection's state and error.
758
- ui.status(`MCP server ${name}`, "Reconnecting…");
759
- await reconnect(server);
835
+ // Connection state and error already report failures, including required sign-ins.
836
+ runInBackground(ctx, server, async () => {
837
+ await reconnect(server);
838
+ return undefined;
839
+ });
760
840
  break;
761
841
  case "signout":
762
842
  await signOut(server);
@@ -772,11 +852,13 @@ export function createMcpExtension(options = {}) {
772
852
  case "enable-project":
773
853
  case "disable-project": {
774
854
  const enable = action.startsWith("enable");
775
- ui.status(`MCP server ${name}`, enable ? "Connecting…" : "Disconnecting…");
776
- message = await setEnabled(server, enable, action.endsWith("-project"));
855
+ runInBackground(ctx, server, () => setEnabled(server, enable, action.endsWith("-project")));
777
856
  break;
778
857
  }
779
858
  }
859
+ // The session ended meanwhile (for example during a sign-in), which made ctx stale.
860
+ if (signal.aborted)
861
+ return;
780
862
  server.message = message;
781
863
  ensureDiscoveryActive(ctx);
782
864
  emitChange();
@@ -860,6 +942,7 @@ export function createMcpExtension(options = {}) {
860
942
  ctx.ui.notify(`Signing in to MCP server "${name}" requires interactive mode.`, "error");
861
943
  return;
862
944
  }
945
+ const { signal } = session;
863
946
  let failure;
864
947
  if (ctx.mode === "tui") {
865
948
  await showMcpManager(ctx, async (ui) => {
@@ -875,6 +958,9 @@ export function createMcpExtension(options = {}) {
875
958
  promptForRedirectUrl: (signal) => ctx.ui.input(`Waiting for sign-in to "${name}". If the browser cannot reach this machine, paste the URL it was redirected to.`, "http://127.0.0.1:.../callback?code=...", { signal }),
876
959
  });
877
960
  }
961
+ // The session ended meanwhile, which cancelled the sign-in and made ctx stale.
962
+ if (signal.aborted)
963
+ return;
878
964
  if (failure) {
879
965
  ctx.ui.notify(failure, failure === "Sign-in cancelled." ? "info" : "error");
880
966
  return;
@@ -891,8 +977,8 @@ export function createMcpExtension(options = {}) {
891
977
  waitedForStartup = false;
892
978
  sessionCwd = ctx.cwd;
893
979
  modelRegistry = ctx.modelRegistry;
894
- const current = ++generation;
895
- sessionActive = true;
980
+ session = new AbortController();
981
+ const { signal } = session;
896
982
  configuredEntries = loaded.servers;
897
983
  const registered = registeredServers();
898
984
  overridden = registered.overridden;
@@ -909,10 +995,9 @@ export function createMcpExtension(options = {}) {
909
995
  // The MCP client loads only now, so sessions without servers never pay for it. Waiting one
910
996
  // event loop turn lets the first render happen before loading and connecting.
911
997
  const runtime = new Promise((resolve) => setImmediate(resolve)).then(() => loadMcpRuntime());
912
- const isCurrent = () => current === generation;
913
- pending = Promise.all(enabled.map((server) => startConnection(server, isCurrent, runtime)))
998
+ pending = Promise.all(enabled.map((server) => startConnection(server, runtime)))
914
999
  .then(() => {
915
- if (isCurrent())
1000
+ if (!signal.aborted)
916
1001
  reportProblems(ctx);
917
1002
  })
918
1003
  .catch((error) => {
@@ -956,25 +1041,31 @@ export function createMcpExtension(options = {}) {
956
1041
  else
957
1042
  delete sections[MCP_SERVERS_SECTION];
958
1043
  });
959
- // A codemode script waits for the servers it names, or for every server when it searches or
960
- // enumerates tools, so their tools are registered before the script runs. tool_search and the
961
- // resource tools reach every server, so they wait for all of them.
1044
+ // A tool that reaches other tools (`reachesTools`) waits for the servers it may reach, so their
1045
+ // tools are registered before it runs: a codemode script for the servers it names, or for every
1046
+ // server when it searches or enumerates tools, and tool_search for every server. The resource
1047
+ // tools reach every server, so they wait for all of them. Direct MCP calls wait only for their
1048
+ // own server, including a reconnect still closing its old transport.
962
1049
  pi.on("tool_call", async (event, ctx) => {
963
1050
  const tool = pi.getAllTools().find((candidate) => candidate.name === event.toolName);
964
1051
  if (!tool)
965
1052
  return;
966
- const pendingServers = servers.filter((server) => isEnabled(server) && server.connection?.state !== "connected" && server.ready);
967
- if (pendingServers.length === 0)
1053
+ // Readiness can be pending while the display state is still connected: reconnect first
1054
+ // closes the old transport, then opens a new one. Settled promises return immediately.
1055
+ const readyServers = servers.filter((server) => isEnabled(server) && server.ready);
1056
+ if (readyServers.length === 0)
968
1057
  return;
969
1058
  let waiting = [];
970
- const orchestrator = discoverOrchestrators([tool]);
971
- if (orchestrator.codemode) {
972
- const { code } = event.input;
973
- const source = typeof code === "string" ? code : "";
974
- waiting = pendingServers.filter((server) => scriptNeedsServer(source, server.entry.name));
1059
+ const reach = tool.reachesTools?.(event.input);
1060
+ if (reach !== undefined) {
1061
+ waiting = readyServers.filter((server) => reachesServer(reach, server.entry.name));
975
1062
  }
976
- else if (orchestrator.toolSearch || RESOURCE_TOOL_NAMES.has(tool.name)) {
977
- waiting = pendingServers;
1063
+ else if (RESOURCE_TOOL_NAMES.has(tool.name)) {
1064
+ waiting = readyServers;
1065
+ }
1066
+ else {
1067
+ const owner = toolOwners.get(event.toolName)?.split("\0", 1)[0];
1068
+ waiting = readyServers.filter((server) => server.entry.name === owner);
978
1069
  }
979
1070
  await waitForServers(waiting, ctx.signal);
980
1071
  });
@@ -985,9 +1076,9 @@ export function createMcpExtension(options = {}) {
985
1076
  });
986
1077
  // Servers registered or unregistered during the session connect or disconnect right away.
987
1078
  pi.on("mcp_servers_change", async (_event, ctx) => {
988
- if (!sessionActive)
1079
+ const { signal } = session;
1080
+ if (signal.aborted)
989
1081
  return;
990
- const current = generation;
991
1082
  const registered = registeredServers();
992
1083
  overridden = registered.overridden;
993
1084
  const next = new Map(registered.servers.map((server) => [server.entry.name, server]));
@@ -1003,11 +1094,11 @@ export function createMcpExtension(options = {}) {
1003
1094
  ensureDiscoveryActive(ctx);
1004
1095
  await Promise.all(removed.map((server) => server.connection?.close()));
1005
1096
  const connecting = added.filter(isEnabled);
1006
- if (current !== generation || connecting.length === 0)
1097
+ if (signal.aborted || connecting.length === 0)
1007
1098
  return;
1008
1099
  try {
1009
- await Promise.all(connecting.map((server) => startConnection(server, () => current === generation)));
1010
- if (current !== generation) {
1100
+ await Promise.all(connecting.map((server) => startConnection(server)));
1101
+ if (signal.aborted) {
1011
1102
  await Promise.all(connecting.map((server) => server.connection?.close()));
1012
1103
  return;
1013
1104
  }
@@ -1016,17 +1107,16 @@ export function createMcpExtension(options = {}) {
1016
1107
  ctx.ui.notify(`MCP failed to load: ${errorMessage(error)}`, "error");
1017
1108
  return;
1018
1109
  }
1019
- if (current !== generation)
1110
+ if (signal.aborted)
1020
1111
  return;
1021
1112
  reportProblems(ctx, connecting);
1022
1113
  });
1023
1114
  pi.on("session_shutdown", async () => {
1024
- sessionActive = false;
1025
- generation++;
1115
+ session.abort();
1026
1116
  const closing = connections();
1027
1117
  servers = [];
1028
1118
  emitChange();
1029
- await Promise.all(closing.map((connection) => connection.close()));
1119
+ await Promise.all([...backgroundActions, ...closing.map((connection) => connection.close())]);
1030
1120
  });
1031
1121
  pi.registerCommand("mcp", {
1032
1122
  description: "Manage MCP servers: sign in, reconnect, enable or disable, and change exposure",
@@ -1052,19 +1142,21 @@ export function createMcpExtension(options = {}) {
1052
1142
  return items.length > 0 ? items : null;
1053
1143
  },
1054
1144
  handler: async (args, ctx) => {
1055
- await pending;
1056
1145
  const [action, name, ...extra] = args.trim().split(/\s+/).filter(Boolean);
1057
1146
  if (action === undefined) {
1058
1147
  if (ctx.mode === "tui")
1059
1148
  await showMcpManager(ctx, (ui) => manage(ui, ctx));
1060
- else
1149
+ else {
1150
+ await pending;
1061
1151
  ctx.ui.notify(formatStatus(), "info");
1152
+ }
1062
1153
  return;
1063
1154
  }
1064
1155
  if (extra.length > 0) {
1065
1156
  ctx.ui.notify(MCP_USAGE, "warning");
1066
1157
  return;
1067
1158
  }
1159
+ await pending;
1068
1160
  switch (action) {
1069
1161
  case "login": {
1070
1162
  const server = await pickServer(name, ctx, oauthPick);