@ashx-j/lunr 0.2.26 → 0.2.27

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 (639) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/LICENSE +22 -0
  3. package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
  4. package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-thinking.js +1 -1
  6. package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
  8. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  9. package/dist/builtin-extensions/ashxj-tui.js +6 -10
  10. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  11. package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
  12. package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
  13. package/dist/builtin-extensions/lunr-cron.js +94 -108
  14. package/dist/builtin-extensions/lunr-cron.js.map +1 -1
  15. package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
  16. package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
  17. package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
  18. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
  19. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
  20. package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
  21. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
  22. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
  23. package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
  24. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
  25. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
  26. package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
  27. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-intercom/index.js +2 -2
  29. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  30. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
  31. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
  33. package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
  34. package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
  35. package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
  36. package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
  37. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
  38. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
  40. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
  41. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
  42. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
  44. package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
  45. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
  46. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
  47. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
  48. package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
  49. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
  50. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
  52. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
  53. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
  54. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
  56. package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
  57. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
  58. package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
  60. package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
  61. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
  62. package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
  64. package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
  65. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
  66. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
  67. package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
  68. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
  69. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
  70. package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
  71. package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
  73. package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
  74. package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
  75. package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
  76. package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
  77. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
  78. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
  79. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
  80. package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
  81. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
  82. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
  83. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
  84. package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
  85. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
  86. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
  87. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
  88. package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
  89. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
  90. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
  91. package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
  92. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
  93. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
  94. package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
  95. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
  96. package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
  97. package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
  98. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
  99. package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
  101. package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
  102. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
  103. package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
  104. package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
  105. package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
  106. package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
  107. package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
  108. package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
  109. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
  110. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
  111. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
  112. package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
  113. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
  114. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
  115. package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
  116. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  117. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
  118. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  119. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
  120. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  121. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +15 -31
  122. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  123. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  124. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
  125. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  126. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  127. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +10 -2
  128. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  129. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  130. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
  131. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  132. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  133. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +106 -24
  134. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  135. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  136. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +25 -5
  137. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  138. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
  139. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  140. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
  141. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  142. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
  143. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  144. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
  145. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  146. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
  147. package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
  148. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
  149. package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
  150. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  151. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
  152. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  153. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
  154. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  155. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +17 -8
  156. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  157. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
  158. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
  159. package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
  160. package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
  161. package/dist/builtin-extensions/pi-tps.js +20 -17
  162. package/dist/builtin-extensions/pi-tps.js.map +1 -1
  163. package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
  164. package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
  165. package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
  166. package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
  167. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
  168. package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
  169. package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
  170. package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
  171. package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
  172. package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
  173. package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
  174. package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
  175. package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
  176. package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
  177. package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
  178. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
  179. package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
  180. package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
  181. package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
  182. package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
  183. package/dist/builtin-extensions/pi-web-access/index.js +48 -73
  184. package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
  185. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
  186. package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
  187. package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
  188. package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
  189. package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
  190. package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
  191. package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
  192. package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
  193. package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
  194. package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
  195. package/dist/builtin-extensions/simple-pi-memory.js +1 -1
  196. package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
  197. package/dist/cli/install-cli.d.ts.map +1 -1
  198. package/dist/cli/install-cli.js +38 -30
  199. package/dist/cli/install-cli.js.map +1 -1
  200. package/dist/core/agent-session-runtime.d.ts +13 -0
  201. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  202. package/dist/core/agent-session-runtime.js +75 -11
  203. package/dist/core/agent-session-runtime.js.map +1 -1
  204. package/dist/core/agent-session-services.d.ts +1 -0
  205. package/dist/core/agent-session-services.d.ts.map +1 -1
  206. package/dist/core/agent-session-services.js +1 -0
  207. package/dist/core/agent-session-services.js.map +1 -1
  208. package/dist/core/agent-session.d.ts +40 -6
  209. package/dist/core/agent-session.d.ts.map +1 -1
  210. package/dist/core/agent-session.js +309 -76
  211. package/dist/core/agent-session.js.map +1 -1
  212. package/dist/core/browser/runtime.d.ts +2 -0
  213. package/dist/core/browser/runtime.d.ts.map +1 -1
  214. package/dist/core/browser/runtime.js +80 -45
  215. package/dist/core/browser/runtime.js.map +1 -1
  216. package/dist/core/browser/setup.d.ts.map +1 -1
  217. package/dist/core/browser/setup.js +12 -1
  218. package/dist/core/browser/setup.js.map +1 -1
  219. package/dist/core/computer-use/adapter.d.ts.map +1 -1
  220. package/dist/core/computer-use/adapter.js +7 -2
  221. package/dist/core/computer-use/adapter.js.map +1 -1
  222. package/dist/core/computer-use/image.d.ts.map +1 -1
  223. package/dist/core/computer-use/image.js +17 -5
  224. package/dist/core/computer-use/image.js.map +1 -1
  225. package/dist/core/computer-use/schemas.d.ts.map +1 -1
  226. package/dist/core/computer-use/schemas.js +23 -6
  227. package/dist/core/computer-use/schemas.js.map +1 -1
  228. package/dist/core/computer-use/workflow.d.ts.map +1 -1
  229. package/dist/core/computer-use/workflow.js +459 -119
  230. package/dist/core/computer-use/workflow.js.map +1 -1
  231. package/dist/core/cron/jobs.d.ts +26 -8
  232. package/dist/core/cron/jobs.d.ts.map +1 -1
  233. package/dist/core/cron/jobs.js +357 -232
  234. package/dist/core/cron/jobs.js.map +1 -1
  235. package/dist/core/cron/origin-context.d.ts +1 -0
  236. package/dist/core/cron/origin-context.d.ts.map +1 -1
  237. package/dist/core/cron/origin-context.js.map +1 -1
  238. package/dist/core/cron/scheduler.d.ts +22 -17
  239. package/dist/core/cron/scheduler.d.ts.map +1 -1
  240. package/dist/core/cron/scheduler.js +153 -73
  241. package/dist/core/cron/scheduler.js.map +1 -1
  242. package/dist/core/extensions/index.d.ts +1 -1
  243. package/dist/core/extensions/index.d.ts.map +1 -1
  244. package/dist/core/extensions/index.js.map +1 -1
  245. package/dist/core/extensions/runner.d.ts +1 -0
  246. package/dist/core/extensions/runner.d.ts.map +1 -1
  247. package/dist/core/extensions/runner.js +8 -0
  248. package/dist/core/extensions/runner.js.map +1 -1
  249. package/dist/core/extensions/types.d.ts +8 -0
  250. package/dist/core/extensions/types.d.ts.map +1 -1
  251. package/dist/core/extensions/types.js.map +1 -1
  252. package/dist/core/footer-data-provider.d.ts +4 -0
  253. package/dist/core/footer-data-provider.d.ts.map +1 -1
  254. package/dist/core/footer-data-provider.js +22 -1
  255. package/dist/core/footer-data-provider.js.map +1 -1
  256. package/dist/core/large-subagent-launch.d.ts +1 -1
  257. package/dist/core/large-subagent-launch.d.ts.map +1 -1
  258. package/dist/core/large-subagent-launch.js +7 -7
  259. package/dist/core/large-subagent-launch.js.map +1 -1
  260. package/dist/core/model-registry.d.ts +2 -0
  261. package/dist/core/model-registry.d.ts.map +1 -1
  262. package/dist/core/model-registry.js +5 -0
  263. package/dist/core/model-registry.js.map +1 -1
  264. package/dist/core/model-runtime.d.ts +4 -1
  265. package/dist/core/model-runtime.d.ts.map +1 -1
  266. package/dist/core/model-runtime.js +40 -4
  267. package/dist/core/model-runtime.js.map +1 -1
  268. package/dist/core/package-manager.d.ts.map +1 -1
  269. package/dist/core/package-manager.js.map +1 -1
  270. package/dist/core/permissions.d.ts +3 -3
  271. package/dist/core/permissions.d.ts.map +1 -1
  272. package/dist/core/permissions.js +11 -35
  273. package/dist/core/permissions.js.map +1 -1
  274. package/dist/core/plan-mode.d.ts +5 -13
  275. package/dist/core/plan-mode.d.ts.map +1 -1
  276. package/dist/core/plan-mode.js +126 -116
  277. package/dist/core/plan-mode.js.map +1 -1
  278. package/dist/core/project-trust.d.ts.map +1 -1
  279. package/dist/core/project-trust.js +1 -1
  280. package/dist/core/project-trust.js.map +1 -1
  281. package/dist/core/prompt-resource-bridge.d.ts +12 -0
  282. package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
  283. package/dist/core/prompt-resource-bridge.js +9 -0
  284. package/dist/core/prompt-resource-bridge.js.map +1 -0
  285. package/dist/core/protected-targets.d.ts +6 -0
  286. package/dist/core/protected-targets.d.ts.map +1 -0
  287. package/dist/core/protected-targets.js +45 -0
  288. package/dist/core/protected-targets.js.map +1 -0
  289. package/dist/core/resource-loader.d.ts.map +1 -1
  290. package/dist/core/resource-loader.js +7 -0
  291. package/dist/core/resource-loader.js.map +1 -1
  292. package/dist/core/rollback.d.ts +19 -3
  293. package/dist/core/rollback.d.ts.map +1 -1
  294. package/dist/core/rollback.js +172 -56
  295. package/dist/core/rollback.js.map +1 -1
  296. package/dist/core/runtime-credentials.d.ts +3 -0
  297. package/dist/core/runtime-credentials.d.ts.map +1 -1
  298. package/dist/core/runtime-credentials.js +14 -1
  299. package/dist/core/runtime-credentials.js.map +1 -1
  300. package/dist/core/sdk.d.ts +3 -0
  301. package/dist/core/sdk.d.ts.map +1 -1
  302. package/dist/core/sdk.js +1 -0
  303. package/dist/core/sdk.js.map +1 -1
  304. package/dist/core/session-manager.d.ts +6 -0
  305. package/dist/core/session-manager.d.ts.map +1 -1
  306. package/dist/core/session-manager.js +95 -32
  307. package/dist/core/session-manager.js.map +1 -1
  308. package/dist/core/session-ownership.d.ts.map +1 -1
  309. package/dist/core/session-ownership.js +8 -3
  310. package/dist/core/session-ownership.js.map +1 -1
  311. package/dist/core/session-retention.d.ts.map +1 -1
  312. package/dist/core/session-retention.js +18 -7
  313. package/dist/core/session-retention.js.map +1 -1
  314. package/dist/core/session-startup-settings.d.ts +10 -0
  315. package/dist/core/session-startup-settings.d.ts.map +1 -0
  316. package/dist/core/session-startup-settings.js +70 -0
  317. package/dist/core/session-startup-settings.js.map +1 -0
  318. package/dist/core/subscriptions.d.ts +16 -7
  319. package/dist/core/subscriptions.d.ts.map +1 -1
  320. package/dist/core/subscriptions.js +96 -55
  321. package/dist/core/subscriptions.js.map +1 -1
  322. package/dist/core/trust-manager.d.ts +2 -1
  323. package/dist/core/trust-manager.d.ts.map +1 -1
  324. package/dist/core/trust-manager.js +6 -1
  325. package/dist/core/trust-manager.js.map +1 -1
  326. package/dist/core/update-check.d.ts.map +1 -1
  327. package/dist/core/update-check.js.map +1 -1
  328. package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
  329. package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
  330. package/dist/core/usage-adapters/kimi-coding.js +2 -2
  331. package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
  332. package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
  333. package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
  334. package/dist/core/usage-adapters/openai-codex.js +3 -3
  335. package/dist/core/usage-adapters/openai-codex.js.map +1 -1
  336. package/dist/core/usage-adapters/xai.d.ts +2 -2
  337. package/dist/core/usage-adapters/xai.d.ts.map +1 -1
  338. package/dist/core/usage-adapters/xai.js +3 -3
  339. package/dist/core/usage-adapters/xai.js.map +1 -1
  340. package/dist/core/usage-adapters/zai.d.ts +2 -2
  341. package/dist/core/usage-adapters/zai.d.ts.map +1 -1
  342. package/dist/core/usage-adapters/zai.js +2 -2
  343. package/dist/core/usage-adapters/zai.js.map +1 -1
  344. package/dist/core/usage-service.d.ts +9 -2
  345. package/dist/core/usage-service.d.ts.map +1 -1
  346. package/dist/core/usage-service.js +95 -50
  347. package/dist/core/usage-service.js.map +1 -1
  348. package/dist/gateway/agent-bridge.d.ts +9 -1
  349. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  350. package/dist/gateway/agent-bridge.js +91 -37
  351. package/dist/gateway/agent-bridge.js.map +1 -1
  352. package/dist/gateway/buttons.d.ts +8 -1
  353. package/dist/gateway/buttons.d.ts.map +1 -1
  354. package/dist/gateway/buttons.js +76 -18
  355. package/dist/gateway/buttons.js.map +1 -1
  356. package/dist/gateway/commands.d.ts +1 -0
  357. package/dist/gateway/commands.d.ts.map +1 -1
  358. package/dist/gateway/commands.js +10 -4
  359. package/dist/gateway/commands.js.map +1 -1
  360. package/dist/gateway/cron.d.ts +12 -11
  361. package/dist/gateway/cron.d.ts.map +1 -1
  362. package/dist/gateway/cron.js +65 -56
  363. package/dist/gateway/cron.js.map +1 -1
  364. package/dist/gateway/index.d.ts.map +1 -1
  365. package/dist/gateway/index.js +2 -1
  366. package/dist/gateway/index.js.map +1 -1
  367. package/dist/gateway/mobile-commands.d.ts +1 -0
  368. package/dist/gateway/mobile-commands.d.ts.map +1 -1
  369. package/dist/gateway/mobile-commands.js +56 -14
  370. package/dist/gateway/mobile-commands.js.map +1 -1
  371. package/dist/gateway/presenter.d.ts +12 -2
  372. package/dist/gateway/presenter.d.ts.map +1 -1
  373. package/dist/gateway/presenter.js +73 -38
  374. package/dist/gateway/presenter.js.map +1 -1
  375. package/dist/gateway/router.d.ts +2 -2
  376. package/dist/gateway/router.d.ts.map +1 -1
  377. package/dist/gateway/router.js +17 -2
  378. package/dist/gateway/router.js.map +1 -1
  379. package/dist/gateway/service.d.ts +2 -0
  380. package/dist/gateway/service.d.ts.map +1 -1
  381. package/dist/gateway/service.js +153 -0
  382. package/dist/gateway/service.js.map +1 -1
  383. package/dist/main.d.ts.map +1 -1
  384. package/dist/main.js +86 -33
  385. package/dist/main.js.map +1 -1
  386. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/assistant-message.js +6 -4
  388. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  389. package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/copyable-text.js +3 -2
  391. package/dist/modes/interactive/components/copyable-text.js.map +1 -1
  392. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/extension-editor.js +1 -1
  394. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  395. package/dist/modes/interactive/interactive-mode.d.ts +2 -2
  396. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  397. package/dist/modes/interactive/interactive-mode.js +96 -109
  398. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  399. package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
  400. package/dist/modes/interactive/smooth-streaming.js.map +1 -1
  401. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  402. package/dist/modes/rpc/rpc-mode.js +2 -1
  403. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  404. package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
  405. package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
  406. package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
  407. package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-OX6KELNX.js} +8 -7
  408. package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
  409. package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
  410. package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
  411. package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
  412. package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
  413. package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
  414. package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
  415. package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
  416. package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
  417. package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
  418. package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-AYHH7YSD.js} +177 -144
  419. package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
  420. package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
  421. package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
  422. package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-C3P6Q3B2.js} +455 -278
  423. package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
  424. package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
  425. package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
  426. package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
  427. package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
  428. package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
  429. package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
  430. package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
  431. package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
  432. package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
  433. package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
  434. package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
  435. package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
  436. package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
  437. package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
  438. package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
  439. package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
  440. package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
  441. package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
  442. package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
  443. package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
  444. package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
  445. package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
  446. package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JWEVT363.js} +37 -29
  447. package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
  448. package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
  449. package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-MQPKUHRN.js} +162 -82
  450. package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
  451. package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
  452. package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
  453. package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-QNVMKNNN.js} +18 -14
  454. package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
  455. package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
  456. package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
  457. package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
  458. package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
  459. package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
  460. package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
  461. package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
  462. package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
  463. package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
  464. package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
  465. package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
  466. package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
  467. package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
  468. package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
  469. package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
  470. package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-ZDJUR45D.js} +427 -126
  471. package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
  472. package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
  473. package/dist/node-runtime/cli-runtime.js +4 -4
  474. package/dist/node-runtime/{command-FSBU2EVZ.js → command-3HMSY6W2.js} +5 -5
  475. package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
  476. package/dist/node-runtime/{commands-NAAL7KNT.js → commands-OCSSEEWJ.js} +16 -16
  477. package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
  478. package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
  479. package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
  480. package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
  481. package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.js} +4 -4
  482. package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
  483. package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
  484. package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
  485. package/dist/node-runtime/{gateway-DQM333YV.js → gateway-6623C2U3.js} +51 -42
  486. package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
  487. package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
  488. package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
  489. package/dist/node-runtime/{goal-NJEE5CTU.js → goal-AIHEDUYA.js} +5 -5
  490. package/dist/node-runtime/index.js +50 -48
  491. package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
  492. package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
  493. package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -0
  494. package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
  495. package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
  496. package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
  497. package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
  498. package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-LKVA5CZZ.js} +93 -80
  499. package/dist/node-runtime/main.js +42 -41
  500. package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-XT3NQXLD.js} +12 -12
  501. package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
  502. package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
  503. package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
  504. package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
  505. package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
  506. package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
  507. package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
  508. package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
  509. package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
  510. package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-LT3CTO4E.js} +98 -106
  511. package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
  512. package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
  513. package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
  514. package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
  515. package/dist/node-runtime/rpc-entry.js +42 -41
  516. package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
  517. package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
  518. package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
  519. package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
  520. package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
  521. package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
  522. package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
  523. package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
  524. package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
  525. package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
  526. package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
  527. package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
  528. package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-Q5F7DBVM.js} +119 -58
  529. package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
  530. package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
  531. package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
  532. package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
  533. package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
  534. package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
  535. package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
  536. package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
  537. package/dist/utils/clipboard-image.d.ts.map +1 -1
  538. package/dist/utils/clipboard-image.js.map +1 -1
  539. package/dist/utils/exif-orientation.d.ts.map +1 -1
  540. package/dist/utils/exif-orientation.js +25 -10
  541. package/dist/utils/exif-orientation.js.map +1 -1
  542. package/dist/utils/image-resize-core.d.ts.map +1 -1
  543. package/dist/utils/image-resize-core.js +2 -0
  544. package/dist/utils/image-resize-core.js.map +1 -1
  545. package/dist/utils/paths.d.ts +4 -0
  546. package/dist/utils/paths.d.ts.map +1 -1
  547. package/dist/utils/paths.js +33 -2
  548. package/dist/utils/paths.js.map +1 -1
  549. package/dist/utils/sanitize-terminal-text.d.ts +7 -0
  550. package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
  551. package/dist/utils/sanitize-terminal-text.js +67 -0
  552. package/dist/utils/sanitize-terminal-text.js.map +1 -0
  553. package/docs/browser.md +1 -1
  554. package/docs/custom-provider.md +3 -3
  555. package/docs/extensions.md +8 -4
  556. package/docs/features.md +15 -7
  557. package/docs/keybindings.md +1 -1
  558. package/docs/models.md +5 -5
  559. package/docs/packages.md +1 -1
  560. package/docs/providers.md +2 -2
  561. package/docs/rpc.md +7 -7
  562. package/docs/security.md +10 -5
  563. package/docs/sessions.md +2 -2
  564. package/docs/skills.md +1 -1
  565. package/docs/terminal-setup.md +7 -7
  566. package/docs/themes.md +1 -1
  567. package/docs/usage.md +1 -1
  568. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  569. package/examples/extensions/bash-spawn-hook.ts +1 -1
  570. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  571. package/examples/extensions/claude-rules.ts +1 -1
  572. package/examples/extensions/commands.ts +1 -1
  573. package/examples/extensions/custom-compaction.ts +1 -1
  574. package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
  575. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  576. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  577. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  578. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  579. package/examples/extensions/doom-overlay/index.ts +1 -1
  580. package/examples/extensions/git-merge-and-resolve.ts +2 -2
  581. package/examples/extensions/gondolin/index.ts +1 -1
  582. package/examples/extensions/gondolin/package-lock.json +2 -2
  583. package/examples/extensions/gondolin/package.json +1 -1
  584. package/examples/extensions/hidden-thinking-label.ts +1 -1
  585. package/examples/extensions/inline-bash.ts +2 -2
  586. package/examples/extensions/input-transform-streaming.ts +2 -2
  587. package/examples/extensions/input-transform.ts +2 -2
  588. package/examples/extensions/interactive-shell.ts +1 -1
  589. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  590. package/examples/extensions/mac-system-theme.ts +1 -1
  591. package/examples/extensions/minimal-mode.ts +1 -1
  592. package/examples/extensions/modal-editor.ts +1 -1
  593. package/examples/extensions/model-status.ts +1 -1
  594. package/examples/extensions/notify.ts +3 -3
  595. package/examples/extensions/overlay-qa-tests.ts +1 -1
  596. package/examples/extensions/overlay-test.ts +1 -1
  597. package/examples/extensions/pirate.ts +1 -1
  598. package/examples/extensions/preset.ts +3 -3
  599. package/examples/extensions/project-trust.ts +4 -4
  600. package/examples/extensions/prompt-customizer.ts +1 -1
  601. package/examples/extensions/rainbow-editor.ts +1 -1
  602. package/examples/extensions/rpc-demo.ts +1 -1
  603. package/examples/extensions/sandbox/index.ts +7 -7
  604. package/examples/extensions/sandbox/package-lock.json +2 -2
  605. package/examples/extensions/sandbox/package.json +1 -1
  606. package/examples/extensions/shutdown-command.ts +5 -5
  607. package/examples/extensions/ssh.ts +2 -2
  608. package/examples/extensions/subagent/README.md +3 -3
  609. package/examples/extensions/subagent/index.ts +2 -2
  610. package/examples/extensions/titlebar-spinner.ts +1 -1
  611. package/examples/extensions/tool-override.ts +1 -1
  612. package/examples/extensions/tools.ts +1 -1
  613. package/examples/extensions/with-deps/package-lock.json +2 -2
  614. package/examples/extensions/with-deps/package.json +1 -1
  615. package/examples/extensions/working-indicator.ts +1 -1
  616. package/examples/extensions/working-message-test.ts +1 -1
  617. package/examples/sdk/01-minimal.ts +1 -1
  618. package/examples/sdk/03-custom-prompt.ts +1 -1
  619. package/examples/sdk/04-skills.ts +1 -1
  620. package/examples/sdk/06-extensions.ts +2 -2
  621. package/examples/sdk/08-prompt-templates.ts +1 -1
  622. package/install-lock/package-lock.json +24 -24
  623. package/install-lock/package.json +2 -2
  624. package/npm-shrinkwrap.json +60 -24
  625. package/package.json +9 -9
  626. package/dist/modes/interactive/components/exit-card.d.ts +0 -22
  627. package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
  628. package/dist/modes/interactive/components/exit-card.js +0 -69
  629. package/dist/modes/interactive/components/exit-card.js.map +0 -1
  630. package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
  631. package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
  632. package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
  633. package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
  634. package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
  635. package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
  636. package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
  637. package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
  638. package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
  639. package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
@@ -44,6 +44,10 @@ export declare class LspManager {
44
44
  private _sessionId;
45
45
  private _lombokJarPath;
46
46
  private _shuttingDown;
47
+ private readonly stop;
48
+ private readonly pendingClients;
49
+ private readonly restartTimers;
50
+ private shutdownPromise;
47
51
  private _restartAttempts;
48
52
  private _restartBackoff;
49
53
  private static readonly MAX_RESTART_ATTEMPTS;
@@ -108,6 +112,9 @@ export declare class LspManager {
108
112
  */
109
113
  startEagerly(languageIds: string[]): void;
110
114
  private ensureWorkspace;
115
+ /** Keep starts registered until their owned clients have either attached or closed. */
116
+ private trackStart;
117
+ private waitForStart;
111
118
  private startServer;
112
119
  /**
113
120
  * Post-initialization hook for language-specific setup.
@@ -129,6 +136,7 @@ export declare class LspManager {
129
136
  private handleUnexpectedExit;
130
137
  /** Shut down all clients (disconnect from daemons, kill direct servers) */
131
138
  shutdownAll(): Promise<void>;
139
+ private shutdownClients;
132
140
  /**
133
141
  * Restart a specific language server. Shuts down the existing client
134
142
  * (and kills the daemon if shared), then starts a fresh server.
@@ -1 +1 @@
1
- {"version":3,"file":"lsp-manager.d.ts","sourceRoot":"","sources":["../../../../src/builtin-extensions/pi-lsp-extension/src/lsp-manager.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAMH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,iBAAiB,EAA4B,MAAM,yBAAyB,CAAC;AAI3F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpF;AAgBD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAK;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAQ;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAS;IAE/C,YAAY,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,SAAS,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAS5J;IAED,iCAAiC;IACjC,IAAI,SAAS,IAAI,iBAAiB,CAEjC;IAED,qFAAqF;IACrF,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAItD;IAED,2CAA2C;IAC3C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAE9D;IAED,kEAAkE;IAClE,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElC;IAED,4DAA4D;IAC5D,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa;IAqDrB,uEAAuE;IACvE,OAAO,CAAC,4BAA4B;IAYpC,mCAAmC;IACnC,sBAAsB,IAAI,MAAM,EAAE,CAEjC;IAED,2CAA2C;IAC3C,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElD;IAED,iCAAiC;IACjC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGnC;IAED,+DAA+D;IAC/D,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpC;IAED;;;OAGG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAMrD;IAED;;;OAGG;IACG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAIlE;IAED;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY7C;IAED,wDAAwD;IACxD,OAAO,CAAC,oBAAoB;IAoB5B;;;;OAIG;IACG,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA0BxE;IAED;;OAEG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5C;IAED;;;;OAIG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,CAkBxC;YAMa,eAAe;YAkBf,WAAW;IAsIzB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAUvB,kDAAkD;IAClD,OAAO,CAAC,aAAa;IAMrB,mDAAmD;IACnD,OAAO,CAAC,aAAa;YAgBP,WAAW;IAwDzB,mDAAmD;IACnD,SAAS,IAAI,YAAY,EAAE,CAoB1B;IAED;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAoC5B,2EAA2E;IACrE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CASjC;IAED;;;;OAIG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrD;IAED,oDAAoD;IACpD,OAAO,CAAC,UAAU;CAiBnB","sourcesContent":["// @ts-nocheck\n/**\n * LSP Manager — manages multiple LSP server instances, one per language.\n *\n * Lazily starts servers on first use. Auto-detects language from file extension.\n * Uses a WorkspaceProvider for workspace detection, multi-root folders, and daemon state.\n */\n\nimport { resolve, join, dirname } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { existsSync, readFileSync, readdirSync, unlinkSync } from \"node:fs\";\nimport { spawn as spawnChild } from \"node:child_process\";\nimport { LspClient } from \"./lsp-client.js\";\nimport { type WorkspaceProvider, DefaultWorkspaceProvider } from \"./workspace-provider.js\";\nimport { getLanguageIdFromPath } from \"./shared/language-map.js\";\nimport { DAEMON_SOCKET_READY_DELAY_MS, DAEMON_RETRY_INTERVAL_MS, DAEMON_MAX_RETRIES } from \"./shared/timing.js\";\n\nexport interface ServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n /** LSP initializationOptions passed during the initialize handshake */\n initializationOptions?: Record<string, unknown>;\n /** Settings returned by workspace/configuration handler (keyed by section) */\n settings?: Record<string, unknown>;\n}\n\n/** Lifecycle callbacks for UI notifications */\nexport interface LspManagerCallbacks {\n onWorkspaceSetupStart?: () => void;\n onWorkspaceSetupEnd?: (success: boolean, duration: number) => void;\n onServerStart?: (languageId: string, command: string) => void;\n onServerReady?: (languageId: string) => void;\n onServerError?: (languageId: string, error: string) => void;\n onServerCrash?: (languageId: string, restarting: boolean, attempt: number) => void;\n}\n\n/** Default server configurations for common languages */\nconst DEFAULT_SERVERS: Record<string, ServerConfig> = {\n typescript: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n javascript: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n typescriptreact: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n javascriptreact: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n rust: { command: \"rust-analyzer\", args: [] },\n python: { command: \"pyright-langserver\", args: [\"--stdio\"] },\n go: { command: \"gopls\", args: [\"serve\"] },\n java: { command: \"jdtls\", args: [] },\n};\n\n// File extension → language ID mapping is in shared/language-map.ts\n\nexport interface ServerStatus {\n languageId: string;\n command: string;\n running: boolean;\n diagnosticsCount: number;\n /** True if using a shared daemon (vs direct spawn) */\n shared: boolean;\n}\n\nexport class LspManager {\n private clients: Map<string, LspClient> = new Map();\n private serverConfigs: Map<string, ServerConfig>;\n private rootDir: string;\n private startingServers: Map<string, Promise<LspClient>> = new Map();\n private _workspace: WorkspaceProvider;\n private _workspaceReady = false;\n private _workspaceReadying: Promise<boolean> | null = null;\n private _callbacks: LspManagerCallbacks;\n private _sessionId: string;\n private _lombokJarPath: string | null = null;\n private _shuttingDown = false;\n private _restartAttempts: Map<string, number> = new Map();\n private _restartBackoff: Map<string, number> = new Map();\n\n private static readonly MAX_RESTART_ATTEMPTS = 3;\n private static readonly INITIAL_BACKOFF_MS = 1000;\n private static readonly MAX_BACKOFF_MS = 30000;\n\n constructor(rootDir: string, customConfigs?: Record<string, ServerConfig>, callbacks?: LspManagerCallbacks, sessionId?: string, workspace?: WorkspaceProvider) {\n this.rootDir = resolve(rootDir);\n this.serverConfigs = new Map(Object.entries({\n ...DEFAULT_SERVERS,\n ...customConfigs,\n }));\n this._workspace = workspace ?? new DefaultWorkspaceProvider();\n this._callbacks = callbacks ?? {};\n this._sessionId = sessionId ?? `${process.pid}-${Date.now()}`;\n }\n\n /** Get the workspace provider */\n get workspace(): WorkspaceProvider {\n return this._workspace;\n }\n\n /** Replace the workspace provider (e.g. when an external extension registers one) */\n setWorkspaceProvider(provider: WorkspaceProvider): void {\n this._workspace = provider;\n this._workspaceReady = false;\n this._workspaceReadying = null;\n }\n\n /** Update or add a server configuration */\n setServerConfig(languageId: string, config: ServerConfig): void {\n this.serverConfigs.set(languageId, config);\n }\n\n /** Set an explicit path to a Lombok jar for Java/jdtls support */\n setLombokJar(jarPath: string): void {\n this._lombokJarPath = resolve(this.rootDir, jarPath);\n }\n\n /** Get the currently configured Lombok jar path (if any) */\n getLombokJar(): string | null {\n return this.findLombokJar();\n }\n\n /**\n * Auto-detect Lombok jar.\n * Searches: explicit path, LOMBOK_JAR env var, workspace root env/ directories.\n */\n private findLombokJar(): string | null {\n // 1. Explicit path set via setLombokJar()\n if (this._lombokJarPath) {\n if (existsSync(this._lombokJarPath)) return this._lombokJarPath;\n }\n\n // 2. LOMBOK_JAR environment variable\n const envJar = process.env.LOMBOK_JAR;\n if (envJar) {\n const resolved = resolve(this.rootDir, envJar);\n if (existsSync(resolved)) return resolved;\n }\n\n // 3. Auto-detect from workspace root (e.g. Brazil workspace env/ directory)\n // Search both workspace root and cwd in case they differ\n const searchRoots = [this.rootDir];\n const wsRoot = this._workspace.workspaceRoot;\n if (wsRoot && wsRoot !== this.rootDir) {\n searchRoots.unshift(wsRoot);\n }\n\n for (const root of searchRoots) {\n const envDir = join(root, \"env\");\n if (!existsSync(envDir)) continue;\n\n // env/Lombok-{version}/runtime/lib/lombok-*.jar\n try {\n const lombokDirs = readdirSync(envDir).filter(d => d.startsWith(\"Lombok-\"));\n for (const dir of lombokDirs) {\n const libDir = join(envDir, dir, \"runtime\", \"lib\");\n if (existsSync(libDir)) {\n const jars = readdirSync(libDir).filter(f => f.startsWith(\"lombok-\") && f.endsWith(\".jar\"));\n if (jars.length > 0) return join(libDir, jars[0]);\n }\n }\n } catch { /* ignore */ }\n\n // env/gradle-cache-2/org/projectlombok/lombok/{version}/lombok-{version}.jar\n const gradleLombok = join(envDir, \"gradle-cache-2\", \"org\", \"projectlombok\", \"lombok\");\n if (existsSync(gradleLombok)) {\n try {\n const versions = readdirSync(gradleLombok);\n for (const ver of versions) {\n const jarPath = join(gradleLombok, ver, `lombok-${ver}.jar`);\n if (existsSync(jarPath)) return jarPath;\n }\n } catch { /* ignore */ }\n }\n }\n\n return null;\n }\n\n /** Build initializationOptions for Java (jdtls) with Lombok support */\n private getJavaInitializationOptions(): Record<string, unknown> | undefined {\n const lombokJar = this.findLombokJar();\n if (!lombokJar) return undefined;\n\n // jdtls expects flat dotted keys in settings, not nested objects\n return {\n settings: {\n \"java.jdt.ls.vmargs\": `-javaagent:${lombokJar}`,\n },\n };\n }\n\n /** Get all configured languages */\n getConfiguredLanguages(): string[] {\n return [...this.serverConfigs.keys()];\n }\n\n /** Resolve a file path to a language ID */\n getLanguageId(filePath: string): string | undefined {\n return getLanguageIdFromPath(filePath);\n }\n\n /** Get a file URI from a path */\n getFileUri(filePath: string): string {\n const abs = resolve(this.rootDir, filePath);\n return pathToFileURL(abs).toString();\n }\n\n /** Resolve an absolute path from potentially relative input */\n resolvePath(filePath: string): string {\n return resolve(this.rootDir, filePath);\n }\n\n /**\n * Get the LSP client for a language ONLY if it's already running.\n * Does not start a new server. Returns null if no client is active.\n */\n getRunningClient(languageId: string): LspClient | null {\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) {\n return existing;\n }\n return null;\n }\n\n /**\n * Get the LSP client for a file, starting the server if needed.\n * Returns null if no server is configured for this file type.\n */\n async getClientForFile(filePath: string): Promise<LspClient | null> {\n const languageId = this.getLanguageId(filePath);\n if (!languageId) return null;\n return this.getClientForLanguage(languageId);\n }\n\n /**\n * Get a user-friendly message about why no client is available for a file.\n */\n getUnavailableReason(filePath: string): string {\n const languageId = this.getLanguageId(filePath);\n if (!languageId) return `No LSP server configured for file type: ${filePath}`;\n const waitHint = this.getExpectedStartTime(languageId);\n if (this.startingServers.has(languageId)) {\n return `LSP server for ${languageId} is starting. ${waitHint} Retry shortly.`;\n }\n const existing = this.clients.get(languageId);\n if (existing && !existing.initialized && !existing.disposed) {\n return `LSP server for ${languageId} is initializing. ${waitHint} Retry shortly.`;\n }\n return `No LSP server available for: ${filePath}. If you just opened this project, call any LSP tool on a file to trigger server startup.`;\n }\n\n /** Estimated startup time hint for a language server */\n private getExpectedStartTime(languageId: string): string {\n switch (languageId) {\n case \"java\":\n return \"This typically takes 1-5 minutes for Java (project indexing).\";\n case \"rust\":\n return \"This typically takes 30s-2min for Rust (cargo metadata + indexing).\";\n case \"typescript\":\n case \"javascript\":\n case \"typescriptreact\":\n case \"javascriptreact\":\n return \"This typically takes 10-30s for TypeScript/JavaScript.\";\n case \"python\":\n return \"This typically takes 10-30s for Python.\";\n case \"go\":\n return \"This typically takes 10-30s for Go.\";\n default:\n return \"This may take a few seconds to a minute.\";\n }\n }\n\n /**\n * Get the LSP client for a language, starting the server if needed.\n * In Brazil workspaces, connects to a shared daemon (or spawns one).\n * Returns null if no server is configured for this language.\n */\n async getClientForLanguage(languageId: string): Promise<LspClient | null> {\n // Already running?\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) {\n return existing;\n }\n\n // Already starting? Don't block — return null so tools can show \"starting\" message\n const starting = this.startingServers.get(languageId);\n if (starting) return null;\n\n const config = this.serverConfigs.get(languageId);\n if (!config) return null;\n\n // Kick off server start in the background — don't await\n const startPromise = this.startServer(languageId, config);\n this.startingServers.set(languageId, startPromise);\n\n // Fire-and-forget: clean up on completion or failure\n startPromise.catch((err) => {\n this.startingServers.delete(languageId);\n const message = `Failed to start LSP server for ${languageId}: ${err.message}`;\n this._callbacks.onServerError?.(languageId, message);\n });\n\n return null; // Server not ready yet\n }\n\n /**\n * Check if a server is currently starting up for a language.\n */\n isServerStarting(languageId: string): boolean {\n return this.startingServers.has(languageId);\n }\n\n /**\n * Eagerly start LSP servers for the given languages in the background.\n * Unlike getClientForLanguage (which returns null immediately), this method\n * is fire-and-forget — intended for session_start auto-start.\n */\n startEagerly(languageIds: string[]): void {\n for (const languageId of languageIds) {\n // Skip if already running or starting\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) continue;\n if (this.startingServers.has(languageId)) continue;\n\n const config = this.serverConfigs.get(languageId);\n if (!config) continue;\n\n const startPromise = this.startServer(languageId, config);\n this.startingServers.set(languageId, startPromise);\n\n startPromise.catch((err) => {\n this.startingServers.delete(languageId);\n this._callbacks.onServerError?.(languageId, `Auto-start failed: ${err.message}`);\n });\n }\n }\n\n /**\n * Ensure workspace provider is ready (one-time per session).\n * Deduplicates concurrent calls.\n */\n private async ensureWorkspace(): Promise<void> {\n if (this._workspaceReady) return;\n if (this._workspaceReadying) {\n await this._workspaceReadying;\n return;\n }\n this._callbacks.onWorkspaceSetupStart?.();\n const start = Date.now();\n this._workspaceReadying = this._workspace.ensureReady(this._sessionId);\n try {\n const success = await this._workspaceReadying;\n this._callbacks.onWorkspaceSetupEnd?.(success, Date.now() - start);\n } finally {\n this._workspaceReady = true;\n this._workspaceReadying = null;\n }\n }\n\n private async startServer(languageId: string, config: ServerConfig): Promise<LspClient> {\n // Ensure workspace provider is ready (one-time setup)\n await this.ensureWorkspace();\n\n const stateDir = this._workspace.stateDir;\n const folders = this._workspace.getWorkspaceFolders();\n const workspaceFolders = folders.length > 0 ? folders : undefined;\n\n // Build language-specific initializationOptions (e.g. Lombok for Java)\n // Use config-level initializationOptions if provided, otherwise fall back to language-specific defaults\n const initializationOptions = config.initializationOptions\n ?? (languageId === \"java\" ? this.getJavaInitializationOptions() : undefined);\n\n // For Java with Lombok, inject --jvm-arg so jdtls launches with -javaagent\n let effectiveArgs = config.args;\n if (languageId === \"java\") {\n const lombokJar = this.findLombokJar();\n if (lombokJar) {\n effectiveArgs = [`--jvm-arg=-javaagent:${lombokJar}`, ...config.args];\n }\n }\n\n // Callback for auto-restart on unexpected exit\n const onUnexpectedExit = (code: number | null) => this.handleUnexpectedExit(languageId, code);\n // Try connecting to an existing daemon socket first\n const socketPath = this.getSocketPath(languageId);\n if (socketPath && this.isDaemonAlive(languageId)) {\n this._callbacks.onServerStart?.(languageId, `${config.command} (shared)`);\n try {\n const client = new LspClient({\n command: config.command,\n args: effectiveArgs,\n rootDir: this.rootDir,\n languageId,\n socketPath,\n initializationOptions,\n settings: config.settings,\n onUnexpectedExit,\n });\n await client.start();\n this.clients.set(languageId, client);\n this.startingServers.delete(languageId);\n this._callbacks.onServerReady?.(languageId);\n this._restartAttempts.delete(languageId);\n this._restartBackoff.delete(languageId);\n this.triggerPostInit(languageId, client);\n return client;\n } catch {\n // Daemon may be stale — fall through to spawn new one\n }\n }\n\n // No existing daemon — spawn one (or start direct if no state directory)\n this._callbacks.onServerStart?.(languageId, config.command);\n\n if (stateDir) {\n // Spawn daemon and connect via socket\n try {\n await this.spawnDaemon(languageId, config, effectiveArgs, workspaceFolders, initializationOptions);\n // Small delay to let daemon start listening\n await new Promise((r) => setTimeout(r, DAEMON_SOCKET_READY_DELAY_MS));\n\n const daemonSocket = this.getSocketPath(languageId)!;\n\n // Retry connection (daemon may still be initializing jdtls which can take minutes)\n let lastErr: Error | null = null;\n for (let attempt = 0; attempt < DAEMON_MAX_RETRIES; attempt++) {\n try {\n const client = new LspClient({\n command: config.command,\n args: effectiveArgs,\n rootDir: this.rootDir,\n languageId,\n socketPath: daemonSocket,\n initializationOptions,\n settings: config.settings,\n onUnexpectedExit,\n });\n await client.start();\n this.clients.set(languageId, client);\n this.startingServers.delete(languageId);\n this._callbacks.onServerReady?.(languageId);\n this._restartAttempts.delete(languageId);\n this._restartBackoff.delete(languageId);\n this.triggerPostInit(languageId, client);\n return client;\n } catch (err: any) {\n lastErr = err;\n // Check if daemon is still alive before retrying\n if (!this.isDaemonAlive(languageId)) {\n throw new Error(`Daemon for ${languageId} died during startup: ${err.message}`);\n }\n await new Promise((r) => setTimeout(r, DAEMON_RETRY_INTERVAL_MS));\n }\n }\n throw lastErr ?? new Error(\"Failed to connect to daemon\");\n } catch (err: any) {\n // Fall back to direct mode — log the daemon failure\n this._callbacks.onServerError?.(languageId, `Daemon mode failed, falling back to direct: ${err.message}`);\n this.startingServers.delete(languageId);\n // Don't throw — try direct mode below\n }\n }\n\n // Direct mode (no state directory for daemon, or daemon failed)\n const client = new LspClient({\n command: config.command,\n args: effectiveArgs,\n rootDir: this.rootDir,\n languageId,\n env: config.env,\n workspaceFolders,\n initializationOptions,\n settings: config.settings,\n onUnexpectedExit,\n });\n\n try {\n await client.start();\n this.clients.set(languageId, client);\n this.startingServers.delete(languageId);\n this._callbacks.onServerReady?.(languageId);\n this._restartAttempts.delete(languageId);\n this._restartBackoff.delete(languageId);\n this.triggerPostInit(languageId, client);\n return client;\n } catch (err: any) {\n this.startingServers.delete(languageId);\n const message = `Failed to start LSP server for ${languageId} (${config.command}): ${err.message}`;\n this._callbacks.onServerError?.(languageId, message);\n throw new Error(message);\n }\n }\n\n /**\n * Post-initialization hook for language-specific setup.\n * For Java/jdtls: triggers a workspace build so diagnostics refresh\n * after Lombok annotation processing completes.\n */\n private triggerPostInit(languageId: string, client: LspClient): void {\n if (languageId === \"java\") {\n // java/buildWorkspace forces jdtls to rebuild and re-publish diagnostics.\n // The boolean parameter means \"full build\" (true) vs incremental (false).\n client.sendRequest(\"java/buildWorkspace\", true).catch(() => {\n // Not critical — diagnostics will just be stale until a file changes\n });\n }\n }\n\n /** Get the socket path for a language's daemon */\n private getSocketPath(languageId: string): string | null {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return null;\n return join(stateDir, \"sockets\", `lsp-${languageId}.sock`);\n }\n\n /** Check if a daemon is alive for this language */\n private isDaemonAlive(languageId: string): boolean {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return false;\n const pidPath = join(stateDir, \"sockets\", `lsp-${languageId}.pid`);\n try {\n if (!existsSync(pidPath)) return false;\n const pid = parseInt(readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return false;\n process.kill(pid, 0); // throws if not alive\n return true;\n } catch {\n return false;\n }\n }\n\n /** Spawn an LSP daemon as a detached background process */\n private async spawnDaemon(\n languageId: string,\n config: ServerConfig,\n effectiveArgs: string[],\n workspaceFolders?: { uri: string; name: string }[],\n initializationOptions?: Record<string, unknown>,\n ): Promise<void> {\n const socketPath = this.getSocketPath(languageId)!;\n const daemonScript = new URL(\"./lsp-daemon.ts\", import.meta.url).pathname;\n const launcherScript = new URL(\"./lsp-daemon-launcher.cjs\", import.meta.url).pathname;\n\n // Resolve jiti from the running process's module context\n let jitiPath: string;\n try {\n jitiPath = require.resolve(\"jiti\");\n } catch {\n try {\n jitiPath = require.resolve(\"@mariozechner/jiti\");\n } catch {\n throw new Error(\"Cannot resolve jiti for daemon spawn — jiti not found in module path\");\n }\n }\n\n const env: Record<string, string> = {\n ...process.env as Record<string, string>,\n ...(config.env ?? {}),\n LSP_ROOT_DIR: this.rootDir,\n LSP_LANGUAGE_ID: languageId,\n };\n\n if (workspaceFolders && workspaceFolders.length > 0) {\n env.LSP_WORKSPACE_FOLDERS = JSON.stringify(workspaceFolders);\n }\n\n if (initializationOptions) {\n env.LSP_INITIALIZATION_OPTIONS = JSON.stringify(initializationOptions);\n }\n\n if (config.settings) {\n env.LSP_SETTINGS = JSON.stringify(config.settings);\n }\n\n const child = spawnChild(\n process.execPath, // node\n [launcherScript, jitiPath, daemonScript, socketPath, config.command, ...effectiveArgs],\n {\n cwd: this.rootDir,\n env,\n detached: true,\n stdio: \"ignore\",\n },\n );\n\n child.unref(); // let daemon outlive this process\n }\n\n /** Get status of all configured/running servers */\n getStatus(): ServerStatus[] {\n const statuses: ServerStatus[] = [];\n for (const [languageId, config] of this.serverConfigs) {\n const client = this.clients.get(languageId);\n let diagnosticsCount = 0;\n if (client) {\n for (const diags of client.getAllDiagnostics().values()) {\n diagnosticsCount += diags.length;\n }\n }\n const daemonAlive = this.isDaemonAlive(languageId);\n statuses.push({\n languageId,\n command: config.command,\n running: client?.initialized === true && !client.disposed,\n diagnosticsCount,\n shared: daemonAlive,\n });\n }\n return statuses;\n }\n\n /**\n * Handle an unexpected server exit. Attempts auto-restart with exponential backoff.\n * Gives up after MAX_RESTART_ATTEMPTS failures per language per session.\n */\n private handleUnexpectedExit(languageId: string, code: number | null): void {\n if (this._shuttingDown) return;\n\n // Clean up the dead client\n this.clients.delete(languageId);\n this.startingServers.delete(languageId);\n\n const attempts = this._restartAttempts.get(languageId) ?? 0;\n if (attempts >= LspManager.MAX_RESTART_ATTEMPTS) {\n this._callbacks.onServerError?.(languageId, `LSP server for ${languageId} crashed ${attempts} times — giving up auto-restart`);\n this._callbacks.onServerCrash?.(languageId, false, attempts);\n return;\n }\n\n const backoff = this._restartBackoff.get(languageId) ?? LspManager.INITIAL_BACKOFF_MS;\n this._restartAttempts.set(languageId, attempts + 1);\n this._restartBackoff.set(languageId, Math.min(backoff * 2, LspManager.MAX_BACKOFF_MS));\n\n this._callbacks.onServerCrash?.(languageId, true, attempts + 1);\n\n setTimeout(() => {\n if (this._shuttingDown) return;\n const config = this.serverConfigs.get(languageId);\n if (!config) return;\n\n const startPromise = this.startServer(languageId, config);\n this.startingServers.set(languageId, startPromise);\n startPromise.catch((err) => {\n this.startingServers.delete(languageId);\n this._callbacks.onServerError?.(languageId, `Auto-restart failed for ${languageId}: ${err.message}`);\n // Trigger another restart attempt (recursive backoff)\n this.handleUnexpectedExit(languageId, null);\n });\n }, backoff);\n }\n\n /** Shut down all clients (disconnect from daemons, kill direct servers) */\n async shutdownAll(): Promise<void> {\n this._workspace.shutdown();\n this._shuttingDown = true;\n const shutdowns = [...this.clients.values()].map((client) =>\n client.shutdown().catch(() => {})\n );\n await Promise.all(shutdowns);\n this.clients.clear();\n this.startingServers.clear();\n }\n\n /**\n * Restart a specific language server. Shuts down the existing client\n * (and kills the daemon if shared), then starts a fresh server.\n * Returns once the new server is initialized, or throws on failure.\n */\n async restartServer(languageId: string): Promise<void> {\n // Shut down existing client\n const existing = this.clients.get(languageId);\n if (existing) {\n await existing.shutdown().catch(() => {});\n this.clients.delete(languageId);\n }\n\n // Kill the daemon if one is running (so we get a fresh server with new config)\n this.killDaemon(languageId);\n\n // Wait for pending starts to clear\n const pending = this.startingServers.get(languageId);\n if (pending) {\n await pending.catch(() => {});\n this.startingServers.delete(languageId);\n }\n\n // Start fresh\n const config = this.serverConfigs.get(languageId);\n if (!config) throw new Error(`No server configured for ${languageId}`);\n\n await this.startServer(languageId, config);\n }\n\n /** Kill a running daemon for a language (if any) */\n private killDaemon(languageId: string): void {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return;\n const pidPath = join(stateDir, \"sockets\", `lsp-${languageId}.pid`);\n try {\n if (!existsSync(pidPath)) return;\n const pid = parseInt(readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return;\n process.kill(pid, \"SIGTERM\");\n // Clean up socket and pid files\n const socketPath = join(stateDir, \"sockets\", `lsp-${languageId}.sock`);\n try { unlinkSync(socketPath); } catch {}\n try { unlinkSync(pidPath); } catch {}\n } catch {\n // Process may already be dead\n }\n }\n}\n"]}
1
+ {"version":3,"file":"lsp-manager.d.ts","sourceRoot":"","sources":["../../../../src/builtin-extensions/pi-lsp-extension/src/lsp-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n/**\n * LSP Manager — manages multiple LSP server instances, one per language.\n *\n * Lazily starts servers on first use. Auto-detects language from file extension.\n * Uses a WorkspaceProvider for workspace detection, multi-root folders, and daemon state.\n */\n\nimport { resolve, join, dirname } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { existsSync, readFileSync, readdirSync, unlinkSync } from \"node:fs\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { spawn as spawnChild } from \"node:child_process\";\nimport { LspClient } from \"./lsp-client.js\";\nimport { type WorkspaceProvider, DefaultWorkspaceProvider } from \"./workspace-provider.js\";\nimport { getLanguageIdFromPath } from \"./shared/language-map.js\";\nimport { DAEMON_SOCKET_READY_DELAY_MS, DAEMON_RETRY_INTERVAL_MS, DAEMON_MAX_RETRIES } from \"./shared/timing.js\";\n\nexport interface ServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n /** LSP initializationOptions passed during the initialize handshake */\n initializationOptions?: Record<string, unknown>;\n /** Settings returned by workspace/configuration handler (keyed by section) */\n settings?: Record<string, unknown>;\n}\n\n/** Lifecycle callbacks for UI notifications */\nexport interface LspManagerCallbacks {\n onWorkspaceSetupStart?: () => void;\n onWorkspaceSetupEnd?: (success: boolean, duration: number) => void;\n onServerStart?: (languageId: string, command: string) => void;\n onServerReady?: (languageId: string) => void;\n onServerError?: (languageId: string, error: string) => void;\n onServerCrash?: (languageId: string, restarting: boolean, attempt: number) => void;\n}\n\n/** Default server configurations for common languages */\nconst DEFAULT_SERVERS: Record<string, ServerConfig> = {\n typescript: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n javascript: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n typescriptreact: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n javascriptreact: { command: \"typescript-language-server\", args: [\"--stdio\"] },\n rust: { command: \"rust-analyzer\", args: [] },\n python: { command: \"pyright-langserver\", args: [\"--stdio\"] },\n go: { command: \"gopls\", args: [\"serve\"] },\n java: { command: \"jdtls\", args: [] },\n};\n\n// File extension → language ID mapping is in shared/language-map.ts\n\nexport interface ServerStatus {\n languageId: string;\n command: string;\n running: boolean;\n diagnosticsCount: number;\n /** True if using a shared daemon (vs direct spawn) */\n shared: boolean;\n}\n\nexport class LspManager {\n private clients: Map<string, LspClient> = new Map();\n private serverConfigs: Map<string, ServerConfig>;\n private rootDir: string;\n private startingServers: Map<string, Promise<LspClient>> = new Map();\n private _workspace: WorkspaceProvider;\n private _workspaceReady = false;\n private _workspaceReadying: Promise<boolean> | null = null;\n private _callbacks: LspManagerCallbacks;\n private _sessionId: string;\n private _lombokJarPath: string | null = null;\n private _shuttingDown = false;\n private readonly stop = new AbortController();\n private readonly pendingClients = new Set<LspClient>();\n private readonly restartTimers = new Set<ReturnType<typeof setTimeout>>();\n private shutdownPromise: Promise<void> | null = null;\n private _restartAttempts: Map<string, number> = new Map();\n private _restartBackoff: Map<string, number> = new Map();\n\n private static readonly MAX_RESTART_ATTEMPTS = 3;\n private static readonly INITIAL_BACKOFF_MS = 1000;\n private static readonly MAX_BACKOFF_MS = 30000;\n\n constructor(rootDir: string, customConfigs?: Record<string, ServerConfig>, callbacks?: LspManagerCallbacks, sessionId?: string, workspace?: WorkspaceProvider) {\n this.rootDir = resolve(rootDir);\n this.serverConfigs = new Map(Object.entries({\n ...DEFAULT_SERVERS,\n ...customConfigs,\n }));\n this._workspace = workspace ?? new DefaultWorkspaceProvider();\n this._callbacks = callbacks ?? {};\n this._sessionId = sessionId ?? `${process.pid}-${Date.now()}`;\n }\n\n /** Get the workspace provider */\n get workspace(): WorkspaceProvider {\n return this._workspace;\n }\n\n /** Replace the workspace provider (e.g. when an external extension registers one) */\n setWorkspaceProvider(provider: WorkspaceProvider): void {\n this._workspace = provider;\n this._workspaceReady = false;\n this._workspaceReadying = null;\n }\n\n /** Update or add a server configuration */\n setServerConfig(languageId: string, config: ServerConfig): void {\n this.serverConfigs.set(languageId, config);\n }\n\n /** Set an explicit path to a Lombok jar for Java/jdtls support */\n setLombokJar(jarPath: string): void {\n this._lombokJarPath = resolve(this.rootDir, jarPath);\n }\n\n /** Get the currently configured Lombok jar path (if any) */\n getLombokJar(): string | null {\n return this.findLombokJar();\n }\n\n /**\n * Auto-detect Lombok jar.\n * Searches: explicit path, LOMBOK_JAR env var, workspace root env/ directories.\n */\n private findLombokJar(): string | null {\n // 1. Explicit path set via setLombokJar()\n if (this._lombokJarPath) {\n if (existsSync(this._lombokJarPath)) return this._lombokJarPath;\n }\n\n // 2. LOMBOK_JAR environment variable\n const envJar = process.env.LOMBOK_JAR;\n if (envJar) {\n const resolved = resolve(this.rootDir, envJar);\n if (existsSync(resolved)) return resolved;\n }\n\n // 3. Auto-detect from workspace root (e.g. Brazil workspace env/ directory)\n // Search both workspace root and cwd in case they differ\n const searchRoots = [this.rootDir];\n const wsRoot = this._workspace.workspaceRoot;\n if (wsRoot && wsRoot !== this.rootDir) {\n searchRoots.unshift(wsRoot);\n }\n\n for (const root of searchRoots) {\n const envDir = join(root, \"env\");\n if (!existsSync(envDir)) continue;\n\n // env/Lombok-{version}/runtime/lib/lombok-*.jar\n try {\n const lombokDirs = readdirSync(envDir).filter(d => d.startsWith(\"Lombok-\"));\n for (const dir of lombokDirs) {\n const libDir = join(envDir, dir, \"runtime\", \"lib\");\n if (existsSync(libDir)) {\n const jars = readdirSync(libDir).filter(f => f.startsWith(\"lombok-\") && f.endsWith(\".jar\"));\n if (jars.length > 0) return join(libDir, jars[0]);\n }\n }\n } catch { /* ignore */ }\n\n // env/gradle-cache-2/org/projectlombok/lombok/{version}/lombok-{version}.jar\n const gradleLombok = join(envDir, \"gradle-cache-2\", \"org\", \"projectlombok\", \"lombok\");\n if (existsSync(gradleLombok)) {\n try {\n const versions = readdirSync(gradleLombok);\n for (const ver of versions) {\n const jarPath = join(gradleLombok, ver, `lombok-${ver}.jar`);\n if (existsSync(jarPath)) return jarPath;\n }\n } catch { /* ignore */ }\n }\n }\n\n return null;\n }\n\n /** Build initializationOptions for Java (jdtls) with Lombok support */\n private getJavaInitializationOptions(): Record<string, unknown> | undefined {\n const lombokJar = this.findLombokJar();\n if (!lombokJar) return undefined;\n\n // jdtls expects flat dotted keys in settings, not nested objects\n return {\n settings: {\n \"java.jdt.ls.vmargs\": `-javaagent:${lombokJar}`,\n },\n };\n }\n\n /** Get all configured languages */\n getConfiguredLanguages(): string[] {\n return [...this.serverConfigs.keys()];\n }\n\n /** Resolve a file path to a language ID */\n getLanguageId(filePath: string): string | undefined {\n return getLanguageIdFromPath(filePath);\n }\n\n /** Get a file URI from a path */\n getFileUri(filePath: string): string {\n const abs = resolve(this.rootDir, filePath);\n return pathToFileURL(abs).toString();\n }\n\n /** Resolve an absolute path from potentially relative input */\n resolvePath(filePath: string): string {\n return resolve(this.rootDir, filePath);\n }\n\n /**\n * Get the LSP client for a language ONLY if it's already running.\n * Does not start a new server. Returns null if no client is active.\n */\n getRunningClient(languageId: string): LspClient | null {\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) {\n return existing;\n }\n return null;\n }\n\n /**\n * Get the LSP client for a file, starting the server if needed.\n * Returns null if no server is configured for this file type.\n */\n async getClientForFile(filePath: string): Promise<LspClient | null> {\n const languageId = this.getLanguageId(filePath);\n if (!languageId) return null;\n return this.getClientForLanguage(languageId);\n }\n\n /**\n * Get a user-friendly message about why no client is available for a file.\n */\n getUnavailableReason(filePath: string): string {\n const languageId = this.getLanguageId(filePath);\n if (!languageId) return `No LSP server configured for file type: ${filePath}`;\n const waitHint = this.getExpectedStartTime(languageId);\n if (this.startingServers.has(languageId)) {\n return `LSP server for ${languageId} is starting. ${waitHint} Retry shortly.`;\n }\n const existing = this.clients.get(languageId);\n if (existing && !existing.initialized && !existing.disposed) {\n return `LSP server for ${languageId} is initializing. ${waitHint} Retry shortly.`;\n }\n return `No LSP server available for: ${filePath}. If you just opened this project, call any LSP tool on a file to trigger server startup.`;\n }\n\n /** Estimated startup time hint for a language server */\n private getExpectedStartTime(languageId: string): string {\n switch (languageId) {\n case \"java\":\n return \"This typically takes 1-5 minutes for Java (project indexing).\";\n case \"rust\":\n return \"This typically takes 30s-2min for Rust (cargo metadata + indexing).\";\n case \"typescript\":\n case \"javascript\":\n case \"typescriptreact\":\n case \"javascriptreact\":\n return \"This typically takes 10-30s for TypeScript/JavaScript.\";\n case \"python\":\n return \"This typically takes 10-30s for Python.\";\n case \"go\":\n return \"This typically takes 10-30s for Go.\";\n default:\n return \"This may take a few seconds to a minute.\";\n }\n }\n\n /**\n * Get the LSP client for a language, starting the server if needed.\n * In Brazil workspaces, connects to a shared daemon (or spawns one).\n * Returns null if no server is configured for this language.\n */\n async getClientForLanguage(languageId: string): Promise<LspClient | null> {\n if (this._shuttingDown) return null;\n // Already running?\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) {\n return existing;\n }\n\n // Already starting? Don't block — return null so tools can show \"starting\" message\n const starting = this.startingServers.get(languageId);\n if (starting) return null;\n\n const config = this.serverConfigs.get(languageId);\n if (!config) return null;\n\n // Kick off server start in the background — don't await\n const startPromise = this.trackStart(languageId, config);\n\n // Fire-and-forget: clean up on completion or failure\n startPromise.catch((err) => {\n if (this._shuttingDown) return;\n const message = `Failed to start LSP server for ${languageId}: ${err.message}`;\n this._callbacks.onServerError?.(languageId, message);\n });\n\n return null; // Server not ready yet\n }\n\n /**\n * Check if a server is currently starting up for a language.\n */\n isServerStarting(languageId: string): boolean {\n return this.startingServers.has(languageId);\n }\n\n /**\n * Eagerly start LSP servers for the given languages in the background.\n * Unlike getClientForLanguage (which returns null immediately), this method\n * is fire-and-forget — intended for session_start auto-start.\n */\n startEagerly(languageIds: string[]): void {\n if (this._shuttingDown) return;\n for (const languageId of languageIds) {\n // Skip if already running or starting\n const existing = this.clients.get(languageId);\n if (existing && existing.initialized && !existing.disposed) continue;\n if (this.startingServers.has(languageId)) continue;\n\n const config = this.serverConfigs.get(languageId);\n if (!config) continue;\n\n const startPromise = this.trackStart(languageId, config);\n\n startPromise.catch((err) => {\n if (this._shuttingDown) return;\n this._callbacks.onServerError?.(languageId, `Auto-start failed: ${err.message}`);\n });\n }\n }\n\n /**\n * Ensure workspace provider is ready (one-time per session).\n * Deduplicates concurrent calls.\n */\n private async ensureWorkspace(): Promise<void> {\n if (this._workspaceReady) return;\n if (this._workspaceReadying) {\n await this.waitForStart(this._workspaceReadying);\n return;\n }\n this._callbacks.onWorkspaceSetupStart?.();\n this.stop.signal.throwIfAborted();\n const start = Date.now();\n this._workspaceReadying = this._workspace.ensureReady(this._sessionId);\n try {\n const success = await this.waitForStart(this._workspaceReadying);\n this._callbacks.onWorkspaceSetupEnd?.(success, Date.now() - start);\n } finally {\n this._workspaceReady = true;\n this._workspaceReadying = null;\n }\n }\n\n /** Keep starts registered until their owned clients have either attached or closed. */\n private trackStart(languageId: string, config: ServerConfig): Promise<LspClient> {\n this.stop.signal.throwIfAborted();\n const existing = this.startingServers.get(languageId);\n if (existing) return existing;\n const pending = Promise.resolve().then(() => this.startServer(languageId, config)).finally(() => {\n if (this.startingServers.get(languageId) === pending) this.startingServers.delete(languageId);\n });\n this.startingServers.set(languageId, pending);\n return pending;\n }\n\n private waitForStart<T>(pending: Promise<T>): Promise<T> {\n const signal = this.stop.signal;\n signal.throwIfAborted();\n return new Promise<T>((resolve, reject) => {\n const aborted = () => reject(signal.reason);\n signal.addEventListener(\"abort\", aborted, { once: true });\n pending.then(resolve, reject).finally(() => signal.removeEventListener(\"abort\", aborted));\n });\n }\n\n private async startServer(languageId: string, config: ServerConfig): Promise<LspClient> {\n this.stop.signal.throwIfAborted();\n await this.ensureWorkspace();\n this.stop.signal.throwIfAborted();\n\n const stateDir = this._workspace.stateDir;\n const folders = this._workspace.getWorkspaceFolders();\n const workspaceFolders = folders.length > 0 ? folders : undefined;\n\n // Build language-specific initializationOptions (e.g. Lombok for Java)\n // Use config-level initializationOptions if provided, otherwise fall back to language-specific defaults\n const initializationOptions = config.initializationOptions\n ?? (languageId === \"java\" ? this.getJavaInitializationOptions() : undefined);\n\n // For Java with Lombok, inject --jvm-arg so jdtls launches with -javaagent\n let effectiveArgs = config.args;\n if (languageId === \"java\") {\n const lombokJar = this.findLombokJar();\n if (lombokJar) {\n effectiveArgs = [`--jvm-arg=-javaagent:${lombokJar}`, ...config.args];\n }\n }\n\n const onUnexpectedExit = (code: number | null) => this.handleUnexpectedExit(languageId, code);\n const connectClient = async (socketPath?: string): Promise<LspClient> => {\n this.stop.signal.throwIfAborted();\n const client = new LspClient({\n command: config.command,\n args: effectiveArgs,\n rootDir: this.rootDir,\n languageId,\n socketPath,\n env: config.env,\n workspaceFolders,\n initializationOptions,\n settings: config.settings,\n onUnexpectedExit,\n });\n this.pendingClients.add(client);\n try {\n await this.waitForStart(client.start());\n this.stop.signal.throwIfAborted();\n if (!client.initialized || client.disposed) throw new Error(\"LSP client closed during startup\");\n this.clients.set(languageId, client);\n this._callbacks.onServerReady?.(languageId);\n this._restartAttempts.delete(languageId);\n this._restartBackoff.delete(languageId);\n this.triggerPostInit(languageId, client);\n return client;\n } catch (error) {\n await client.shutdown();\n throw error;\n } finally {\n this.pendingClients.delete(client);\n }\n };\n\n const socketPath = this.getSocketPath(languageId);\n if (socketPath && this.isDaemonAlive(languageId)) {\n this._callbacks.onServerStart?.(languageId, `${config.command} (shared)`);\n try {\n return await connectClient(socketPath);\n } catch {\n // A stale daemon can fall back, but session cancellation cannot.\n this.stop.signal.throwIfAborted();\n }\n }\n\n this.stop.signal.throwIfAborted();\n this._callbacks.onServerStart?.(languageId, config.command);\n if (stateDir) {\n try {\n await this.spawnDaemon(languageId, config, effectiveArgs, workspaceFolders, initializationOptions);\n await delay(DAEMON_SOCKET_READY_DELAY_MS, undefined, { signal: this.stop.signal });\n const daemonSocket = this.getSocketPath(languageId)!;\n let lastErr: unknown;\n for (let attempt = 0; attempt < DAEMON_MAX_RETRIES; attempt++) {\n this.stop.signal.throwIfAborted();\n try {\n return await connectClient(daemonSocket);\n } catch (error) {\n this.stop.signal.throwIfAborted();\n lastErr = error;\n if (!this.isDaemonAlive(languageId)) throw error;\n await delay(DAEMON_RETRY_INTERVAL_MS, undefined, { signal: this.stop.signal });\n }\n }\n throw lastErr ?? new Error(\"Failed to connect to daemon\");\n } catch (error) {\n this.stop.signal.throwIfAborted();\n const message = error instanceof Error ? error.message : String(error);\n this._callbacks.onServerError?.(languageId, `Daemon mode failed, falling back to direct: ${message}`);\n }\n }\n return connectClient();\n }\n\n /**\n * Post-initialization hook for language-specific setup.\n * For Java/jdtls: triggers a workspace build so diagnostics refresh\n * after Lombok annotation processing completes.\n */\n private triggerPostInit(languageId: string, client: LspClient): void {\n if (languageId === \"java\") {\n // java/buildWorkspace forces jdtls to rebuild and re-publish diagnostics.\n // The boolean parameter means \"full build\" (true) vs incremental (false).\n client.sendRequest(\"java/buildWorkspace\", true).catch(() => {\n // Not critical — diagnostics will just be stale until a file changes\n });\n }\n }\n\n /** Get the socket path for a language's daemon */\n private getSocketPath(languageId: string): string | null {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return null;\n return join(stateDir, \"sockets\", `lsp-${languageId}.sock`);\n }\n\n /** Check if a daemon is alive for this language */\n private isDaemonAlive(languageId: string): boolean {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return false;\n const pidPath = join(stateDir, \"sockets\", `lsp-${languageId}.pid`);\n try {\n if (!existsSync(pidPath)) return false;\n const pid = parseInt(readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return false;\n process.kill(pid, 0); // throws if not alive\n return true;\n } catch {\n return false;\n }\n }\n\n /** Spawn an LSP daemon as a detached background process */\n private async spawnDaemon(\n languageId: string,\n config: ServerConfig,\n effectiveArgs: string[],\n workspaceFolders?: { uri: string; name: string }[],\n initializationOptions?: Record<string, unknown>,\n ): Promise<void> {\n this.stop.signal.throwIfAborted();\n const socketPath = this.getSocketPath(languageId)!;\n const daemonScript = new URL(\"./lsp-daemon.ts\", import.meta.url).pathname;\n const launcherScript = new URL(\"./lsp-daemon-launcher.cjs\", import.meta.url).pathname;\n\n // Resolve jiti from the running process's module context\n let jitiPath: string;\n try {\n jitiPath = require.resolve(\"jiti\");\n } catch {\n try {\n jitiPath = require.resolve(\"@mariozechner/jiti\");\n } catch {\n throw new Error(\"Cannot resolve jiti for daemon spawn — jiti not found in module path\");\n }\n }\n\n const env: Record<string, string> = {\n ...process.env as Record<string, string>,\n ...(config.env ?? {}),\n LSP_ROOT_DIR: this.rootDir,\n LSP_LANGUAGE_ID: languageId,\n };\n\n if (workspaceFolders && workspaceFolders.length > 0) {\n env.LSP_WORKSPACE_FOLDERS = JSON.stringify(workspaceFolders);\n }\n\n if (initializationOptions) {\n env.LSP_INITIALIZATION_OPTIONS = JSON.stringify(initializationOptions);\n }\n\n if (config.settings) {\n env.LSP_SETTINGS = JSON.stringify(config.settings);\n }\n\n const child = spawnChild(\n process.execPath, // node\n [launcherScript, jitiPath, daemonScript, socketPath, config.command, ...effectiveArgs],\n {\n cwd: this.rootDir,\n env,\n detached: true,\n stdio: \"ignore\",\n },\n );\n\n // The launcher becomes shared infrastructure after spawn. Own its startup\n // error event, but do not kill a daemon when this session disconnects.\n child.unref(); // let daemon outlive this process\n await this.waitForStart(new Promise<void>((resolve, reject) => {\n child.on(\"error\", (error) => {\n reject(error);\n if (!this._shuttingDown) this._callbacks.onServerError?.(languageId, error.message);\n });\n child.once(\"spawn\", resolve);\n }));\n }\n\n /** Get status of all configured/running servers */\n getStatus(): ServerStatus[] {\n const statuses: ServerStatus[] = [];\n for (const [languageId, config] of this.serverConfigs) {\n const client = this.clients.get(languageId);\n let diagnosticsCount = 0;\n if (client) {\n for (const diags of client.getAllDiagnostics().values()) {\n diagnosticsCount += diags.length;\n }\n }\n const daemonAlive = this.isDaemonAlive(languageId);\n statuses.push({\n languageId,\n command: config.command,\n running: client?.initialized === true && !client.disposed,\n diagnosticsCount,\n shared: daemonAlive,\n });\n }\n return statuses;\n }\n\n /**\n * Handle an unexpected server exit. Attempts auto-restart with exponential backoff.\n * Gives up after MAX_RESTART_ATTEMPTS failures per language per session.\n */\n private handleUnexpectedExit(languageId: string, code: number | null): void {\n if (this._shuttingDown) return;\n\n // Clean up the dead client\n this.clients.delete(languageId);\n\n const attempts = this._restartAttempts.get(languageId) ?? 0;\n if (attempts >= LspManager.MAX_RESTART_ATTEMPTS) {\n this._callbacks.onServerError?.(languageId, `LSP server for ${languageId} crashed ${attempts} times — giving up auto-restart`);\n this._callbacks.onServerCrash?.(languageId, false, attempts);\n return;\n }\n\n const backoff = this._restartBackoff.get(languageId) ?? LspManager.INITIAL_BACKOFF_MS;\n this._restartAttempts.set(languageId, attempts + 1);\n this._restartBackoff.set(languageId, Math.min(backoff * 2, LspManager.MAX_BACKOFF_MS));\n\n this._callbacks.onServerCrash?.(languageId, true, attempts + 1);\n\n const timer = setTimeout(() => {\n this.restartTimers.delete(timer);\n if (this._shuttingDown) return;\n const config = this.serverConfigs.get(languageId);\n if (!config) return;\n\n const startPromise = this.trackStart(languageId, config);\n startPromise.catch((err) => {\n if (this._shuttingDown) return;\n this._callbacks.onServerError?.(languageId, `Auto-restart failed for ${languageId}: ${err.message}`);\n // Trigger another restart attempt (recursive backoff)\n this.handleUnexpectedExit(languageId, null);\n });\n }, backoff);\n this.restartTimers.add(timer);\n }\n\n /** Shut down all clients (disconnect from daemons, kill direct servers) */\n shutdownAll(): Promise<void> {\n if (this.shutdownPromise) return this.shutdownPromise;\n this._shuttingDown = true;\n this.stop.abort(new Error(\"LSP manager shut down\"));\n for (const timer of this.restartTimers) clearTimeout(timer);\n this.restartTimers.clear();\n this.shutdownPromise = this.shutdownClients();\n return this.shutdownPromise;\n }\n\n private async shutdownClients(): Promise<void> {\n try {\n this._workspace.shutdown();\n } finally {\n const clients = new Set([...this.clients.values(), ...this.pendingClients]);\n await Promise.allSettled([...clients].map((client) => client.shutdown()));\n await Promise.allSettled([...this.startingServers.values()]);\n this.clients.clear();\n this.startingServers.clear();\n }\n }\n\n /**\n * Restart a specific language server. Shuts down the existing client\n * (and kills the daemon if shared), then starts a fresh server.\n * Returns once the new server is initialized, or throws on failure.\n */\n async restartServer(languageId: string): Promise<void> {\n this.stop.signal.throwIfAborted();\n // A pending start can attach while we wait. Close that client before replacement.\n const pending = this.startingServers.get(languageId);\n if (pending) await pending.catch(() => {});\n this.stop.signal.throwIfAborted();\n\n const existing = this.clients.get(languageId);\n if (existing) {\n await existing.shutdown().catch(() => {});\n if (this.clients.get(languageId) === existing) this.clients.delete(languageId);\n }\n this.stop.signal.throwIfAborted();\n\n // Explicit restart retains its existing daemon replacement behavior.\n this.killDaemon(languageId);\n\n // Start fresh\n const config = this.serverConfigs.get(languageId);\n if (!config) throw new Error(`No server configured for ${languageId}`);\n\n await this.trackStart(languageId, config);\n }\n\n /** Kill a running daemon for a language (if any) */\n private killDaemon(languageId: string): void {\n const stateDir = this._workspace.stateDir;\n if (!stateDir) return;\n const pidPath = join(stateDir, \"sockets\", `lsp-${languageId}.pid`);\n try {\n if (!existsSync(pidPath)) return;\n const pid = parseInt(readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return;\n process.kill(pid, \"SIGTERM\");\n // Clean up socket and pid files\n const socketPath = join(stateDir, \"sockets\", `lsp-${languageId}.sock`);\n try { unlinkSync(socketPath); } catch {}\n try { unlinkSync(pidPath); } catch {}\n } catch {\n // Process may already be dead\n }\n }\n}\n"]}
@@ -8,6 +8,7 @@
8
8
  import { resolve, join } from "node:path";
9
9
  import { pathToFileURL } from "node:url";
10
10
  import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
11
+ import { setTimeout as delay } from "node:timers/promises";
11
12
  import { spawn as spawnChild } from "node:child_process";
12
13
  import { LspClient } from "./lsp-client.js";
13
14
  import { DefaultWorkspaceProvider } from "./workspace-provider.js";
@@ -36,6 +37,10 @@ export class LspManager {
36
37
  _sessionId;
37
38
  _lombokJarPath = null;
38
39
  _shuttingDown = false;
40
+ stop = new AbortController();
41
+ pendingClients = new Set();
42
+ restartTimers = new Set();
43
+ shutdownPromise = null;
39
44
  _restartAttempts = new Map();
40
45
  _restartBackoff = new Map();
41
46
  static MAX_RESTART_ATTEMPTS = 3;
@@ -223,6 +228,8 @@ export class LspManager {
223
228
  * Returns null if no server is configured for this language.
224
229
  */
225
230
  async getClientForLanguage(languageId) {
231
+ if (this._shuttingDown)
232
+ return null;
226
233
  // Already running?
227
234
  const existing = this.clients.get(languageId);
228
235
  if (existing && existing.initialized && !existing.disposed) {
@@ -236,11 +243,11 @@ export class LspManager {
236
243
  if (!config)
237
244
  return null;
238
245
  // Kick off server start in the background — don't await
239
- const startPromise = this.startServer(languageId, config);
240
- this.startingServers.set(languageId, startPromise);
246
+ const startPromise = this.trackStart(languageId, config);
241
247
  // Fire-and-forget: clean up on completion or failure
242
248
  startPromise.catch((err) => {
243
- this.startingServers.delete(languageId);
249
+ if (this._shuttingDown)
250
+ return;
244
251
  const message = `Failed to start LSP server for ${languageId}: ${err.message}`;
245
252
  this._callbacks.onServerError?.(languageId, message);
246
253
  });
@@ -258,6 +265,8 @@ export class LspManager {
258
265
  * is fire-and-forget — intended for session_start auto-start.
259
266
  */
260
267
  startEagerly(languageIds) {
268
+ if (this._shuttingDown)
269
+ return;
261
270
  for (const languageId of languageIds) {
262
271
  // Skip if already running or starting
263
272
  const existing = this.clients.get(languageId);
@@ -268,10 +277,10 @@ export class LspManager {
268
277
  const config = this.serverConfigs.get(languageId);
269
278
  if (!config)
270
279
  continue;
271
- const startPromise = this.startServer(languageId, config);
272
- this.startingServers.set(languageId, startPromise);
280
+ const startPromise = this.trackStart(languageId, config);
273
281
  startPromise.catch((err) => {
274
- this.startingServers.delete(languageId);
282
+ if (this._shuttingDown)
283
+ return;
275
284
  this._callbacks.onServerError?.(languageId, `Auto-start failed: ${err.message}`);
276
285
  });
277
286
  }
@@ -284,14 +293,15 @@ export class LspManager {
284
293
  if (this._workspaceReady)
285
294
  return;
286
295
  if (this._workspaceReadying) {
287
- await this._workspaceReadying;
296
+ await this.waitForStart(this._workspaceReadying);
288
297
  return;
289
298
  }
290
299
  this._callbacks.onWorkspaceSetupStart?.();
300
+ this.stop.signal.throwIfAborted();
291
301
  const start = Date.now();
292
302
  this._workspaceReadying = this._workspace.ensureReady(this._sessionId);
293
303
  try {
294
- const success = await this._workspaceReadying;
304
+ const success = await this.waitForStart(this._workspaceReadying);
295
305
  this._callbacks.onWorkspaceSetupEnd?.(success, Date.now() - start);
296
306
  }
297
307
  finally {
@@ -299,9 +309,32 @@ export class LspManager {
299
309
  this._workspaceReadying = null;
300
310
  }
301
311
  }
312
+ /** Keep starts registered until their owned clients have either attached or closed. */
313
+ trackStart(languageId, config) {
314
+ this.stop.signal.throwIfAborted();
315
+ const existing = this.startingServers.get(languageId);
316
+ if (existing)
317
+ return existing;
318
+ const pending = Promise.resolve().then(() => this.startServer(languageId, config)).finally(() => {
319
+ if (this.startingServers.get(languageId) === pending)
320
+ this.startingServers.delete(languageId);
321
+ });
322
+ this.startingServers.set(languageId, pending);
323
+ return pending;
324
+ }
325
+ waitForStart(pending) {
326
+ const signal = this.stop.signal;
327
+ signal.throwIfAborted();
328
+ return new Promise((resolve, reject) => {
329
+ const aborted = () => reject(signal.reason);
330
+ signal.addEventListener("abort", aborted, { once: true });
331
+ pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", aborted));
332
+ });
333
+ }
302
334
  async startServer(languageId, config) {
303
- // Ensure workspace provider is ready (one-time setup)
335
+ this.stop.signal.throwIfAborted();
304
336
  await this.ensureWorkspace();
337
+ this.stop.signal.throwIfAborted();
305
338
  const stateDir = this._workspace.stateDir;
306
339
  const folders = this._workspace.getWorkspaceFolders();
307
340
  const workspaceFolders = folders.length > 0 ? folders : undefined;
@@ -317,114 +350,83 @@ export class LspManager {
317
350
  effectiveArgs = [`--jvm-arg=-javaagent:${lombokJar}`, ...config.args];
318
351
  }
319
352
  }
320
- // Callback for auto-restart on unexpected exit
321
353
  const onUnexpectedExit = (code) => this.handleUnexpectedExit(languageId, code);
322
- // Try connecting to an existing daemon socket first
323
- const socketPath = this.getSocketPath(languageId);
324
- if (socketPath && this.isDaemonAlive(languageId)) {
325
- this._callbacks.onServerStart?.(languageId, `${config.command} (shared)`);
354
+ const connectClient = async (socketPath) => {
355
+ this.stop.signal.throwIfAborted();
356
+ const client = new LspClient({
357
+ command: config.command,
358
+ args: effectiveArgs,
359
+ rootDir: this.rootDir,
360
+ languageId,
361
+ socketPath,
362
+ env: config.env,
363
+ workspaceFolders,
364
+ initializationOptions,
365
+ settings: config.settings,
366
+ onUnexpectedExit,
367
+ });
368
+ this.pendingClients.add(client);
326
369
  try {
327
- const client = new LspClient({
328
- command: config.command,
329
- args: effectiveArgs,
330
- rootDir: this.rootDir,
331
- languageId,
332
- socketPath,
333
- initializationOptions,
334
- settings: config.settings,
335
- onUnexpectedExit,
336
- });
337
- await client.start();
370
+ await this.waitForStart(client.start());
371
+ this.stop.signal.throwIfAborted();
372
+ if (!client.initialized || client.disposed)
373
+ throw new Error("LSP client closed during startup");
338
374
  this.clients.set(languageId, client);
339
- this.startingServers.delete(languageId);
340
375
  this._callbacks.onServerReady?.(languageId);
341
376
  this._restartAttempts.delete(languageId);
342
377
  this._restartBackoff.delete(languageId);
343
378
  this.triggerPostInit(languageId, client);
344
379
  return client;
345
380
  }
381
+ catch (error) {
382
+ await client.shutdown();
383
+ throw error;
384
+ }
385
+ finally {
386
+ this.pendingClients.delete(client);
387
+ }
388
+ };
389
+ const socketPath = this.getSocketPath(languageId);
390
+ if (socketPath && this.isDaemonAlive(languageId)) {
391
+ this._callbacks.onServerStart?.(languageId, `${config.command} (shared)`);
392
+ try {
393
+ return await connectClient(socketPath);
394
+ }
346
395
  catch {
347
- // Daemon may be stale — fall through to spawn new one
396
+ // A stale daemon can fall back, but session cancellation cannot.
397
+ this.stop.signal.throwIfAborted();
348
398
  }
349
399
  }
350
- // No existing daemon — spawn one (or start direct if no state directory)
400
+ this.stop.signal.throwIfAborted();
351
401
  this._callbacks.onServerStart?.(languageId, config.command);
352
402
  if (stateDir) {
353
- // Spawn daemon and connect via socket
354
403
  try {
355
404
  await this.spawnDaemon(languageId, config, effectiveArgs, workspaceFolders, initializationOptions);
356
- // Small delay to let daemon start listening
357
- await new Promise((r) => setTimeout(r, DAEMON_SOCKET_READY_DELAY_MS));
405
+ await delay(DAEMON_SOCKET_READY_DELAY_MS, undefined, { signal: this.stop.signal });
358
406
  const daemonSocket = this.getSocketPath(languageId);
359
- // Retry connection (daemon may still be initializing jdtls which can take minutes)
360
- let lastErr = null;
407
+ let lastErr;
361
408
  for (let attempt = 0; attempt < DAEMON_MAX_RETRIES; attempt++) {
409
+ this.stop.signal.throwIfAborted();
362
410
  try {
363
- const client = new LspClient({
364
- command: config.command,
365
- args: effectiveArgs,
366
- rootDir: this.rootDir,
367
- languageId,
368
- socketPath: daemonSocket,
369
- initializationOptions,
370
- settings: config.settings,
371
- onUnexpectedExit,
372
- });
373
- await client.start();
374
- this.clients.set(languageId, client);
375
- this.startingServers.delete(languageId);
376
- this._callbacks.onServerReady?.(languageId);
377
- this._restartAttempts.delete(languageId);
378
- this._restartBackoff.delete(languageId);
379
- this.triggerPostInit(languageId, client);
380
- return client;
411
+ return await connectClient(daemonSocket);
381
412
  }
382
- catch (err) {
383
- lastErr = err;
384
- // Check if daemon is still alive before retrying
385
- if (!this.isDaemonAlive(languageId)) {
386
- throw new Error(`Daemon for ${languageId} died during startup: ${err.message}`);
387
- }
388
- await new Promise((r) => setTimeout(r, DAEMON_RETRY_INTERVAL_MS));
413
+ catch (error) {
414
+ this.stop.signal.throwIfAborted();
415
+ lastErr = error;
416
+ if (!this.isDaemonAlive(languageId))
417
+ throw error;
418
+ await delay(DAEMON_RETRY_INTERVAL_MS, undefined, { signal: this.stop.signal });
389
419
  }
390
420
  }
391
421
  throw lastErr ?? new Error("Failed to connect to daemon");
392
422
  }
393
- catch (err) {
394
- // Fall back to direct mode — log the daemon failure
395
- this._callbacks.onServerError?.(languageId, `Daemon mode failed, falling back to direct: ${err.message}`);
396
- this.startingServers.delete(languageId);
397
- // Don't throw — try direct mode below
423
+ catch (error) {
424
+ this.stop.signal.throwIfAborted();
425
+ const message = error instanceof Error ? error.message : String(error);
426
+ this._callbacks.onServerError?.(languageId, `Daemon mode failed, falling back to direct: ${message}`);
398
427
  }
399
428
  }
400
- // Direct mode (no state directory for daemon, or daemon failed)
401
- const client = new LspClient({
402
- command: config.command,
403
- args: effectiveArgs,
404
- rootDir: this.rootDir,
405
- languageId,
406
- env: config.env,
407
- workspaceFolders,
408
- initializationOptions,
409
- settings: config.settings,
410
- onUnexpectedExit,
411
- });
412
- try {
413
- await client.start();
414
- this.clients.set(languageId, client);
415
- this.startingServers.delete(languageId);
416
- this._callbacks.onServerReady?.(languageId);
417
- this._restartAttempts.delete(languageId);
418
- this._restartBackoff.delete(languageId);
419
- this.triggerPostInit(languageId, client);
420
- return client;
421
- }
422
- catch (err) {
423
- this.startingServers.delete(languageId);
424
- const message = `Failed to start LSP server for ${languageId} (${config.command}): ${err.message}`;
425
- this._callbacks.onServerError?.(languageId, message);
426
- throw new Error(message);
427
- }
429
+ return connectClient();
428
430
  }
429
431
  /**
430
432
  * Post-initialization hook for language-specific setup.
@@ -468,6 +470,7 @@ export class LspManager {
468
470
  }
469
471
  /** Spawn an LSP daemon as a detached background process */
470
472
  async spawnDaemon(languageId, config, effectiveArgs, workspaceFolders, initializationOptions) {
473
+ this.stop.signal.throwIfAborted();
471
474
  const socketPath = this.getSocketPath(languageId);
472
475
  const daemonScript = new URL("./lsp-daemon.ts", import.meta.url).pathname;
473
476
  const launcherScript = new URL("./lsp-daemon-launcher.cjs", import.meta.url).pathname;
@@ -506,7 +509,17 @@ export class LspManager {
506
509
  detached: true,
507
510
  stdio: "ignore",
508
511
  });
512
+ // The launcher becomes shared infrastructure after spawn. Own its startup
513
+ // error event, but do not kill a daemon when this session disconnects.
509
514
  child.unref(); // let daemon outlive this process
515
+ await this.waitForStart(new Promise((resolve, reject) => {
516
+ child.on("error", (error) => {
517
+ reject(error);
518
+ if (!this._shuttingDown)
519
+ this._callbacks.onServerError?.(languageId, error.message);
520
+ });
521
+ child.once("spawn", resolve);
522
+ }));
510
523
  }
511
524
  /** Get status of all configured/running servers */
512
525
  getStatus() {
@@ -539,7 +552,6 @@ export class LspManager {
539
552
  return;
540
553
  // Clean up the dead client
541
554
  this.clients.delete(languageId);
542
- this.startingServers.delete(languageId);
543
555
  const attempts = this._restartAttempts.get(languageId) ?? 0;
544
556
  if (attempts >= LspManager.MAX_RESTART_ATTEMPTS) {
545
557
  this._callbacks.onServerError?.(languageId, `LSP server for ${languageId} crashed ${attempts} times — giving up auto-restart`);
@@ -550,30 +562,47 @@ export class LspManager {
550
562
  this._restartAttempts.set(languageId, attempts + 1);
551
563
  this._restartBackoff.set(languageId, Math.min(backoff * 2, LspManager.MAX_BACKOFF_MS));
552
564
  this._callbacks.onServerCrash?.(languageId, true, attempts + 1);
553
- setTimeout(() => {
565
+ const timer = setTimeout(() => {
566
+ this.restartTimers.delete(timer);
554
567
  if (this._shuttingDown)
555
568
  return;
556
569
  const config = this.serverConfigs.get(languageId);
557
570
  if (!config)
558
571
  return;
559
- const startPromise = this.startServer(languageId, config);
560
- this.startingServers.set(languageId, startPromise);
572
+ const startPromise = this.trackStart(languageId, config);
561
573
  startPromise.catch((err) => {
562
- this.startingServers.delete(languageId);
574
+ if (this._shuttingDown)
575
+ return;
563
576
  this._callbacks.onServerError?.(languageId, `Auto-restart failed for ${languageId}: ${err.message}`);
564
577
  // Trigger another restart attempt (recursive backoff)
565
578
  this.handleUnexpectedExit(languageId, null);
566
579
  });
567
580
  }, backoff);
581
+ this.restartTimers.add(timer);
568
582
  }
569
583
  /** Shut down all clients (disconnect from daemons, kill direct servers) */
570
- async shutdownAll() {
571
- this._workspace.shutdown();
584
+ shutdownAll() {
585
+ if (this.shutdownPromise)
586
+ return this.shutdownPromise;
572
587
  this._shuttingDown = true;
573
- const shutdowns = [...this.clients.values()].map((client) => client.shutdown().catch(() => { }));
574
- await Promise.all(shutdowns);
575
- this.clients.clear();
576
- this.startingServers.clear();
588
+ this.stop.abort(new Error("LSP manager shut down"));
589
+ for (const timer of this.restartTimers)
590
+ clearTimeout(timer);
591
+ this.restartTimers.clear();
592
+ this.shutdownPromise = this.shutdownClients();
593
+ return this.shutdownPromise;
594
+ }
595
+ async shutdownClients() {
596
+ try {
597
+ this._workspace.shutdown();
598
+ }
599
+ finally {
600
+ const clients = new Set([...this.clients.values(), ...this.pendingClients]);
601
+ await Promise.allSettled([...clients].map((client) => client.shutdown()));
602
+ await Promise.allSettled([...this.startingServers.values()]);
603
+ this.clients.clear();
604
+ this.startingServers.clear();
605
+ }
577
606
  }
578
607
  /**
579
608
  * Restart a specific language server. Shuts down the existing client
@@ -581,25 +610,26 @@ export class LspManager {
581
610
  * Returns once the new server is initialized, or throws on failure.
582
611
  */
583
612
  async restartServer(languageId) {
584
- // Shut down existing client
613
+ this.stop.signal.throwIfAborted();
614
+ // A pending start can attach while we wait. Close that client before replacement.
615
+ const pending = this.startingServers.get(languageId);
616
+ if (pending)
617
+ await pending.catch(() => { });
618
+ this.stop.signal.throwIfAborted();
585
619
  const existing = this.clients.get(languageId);
586
620
  if (existing) {
587
621
  await existing.shutdown().catch(() => { });
588
- this.clients.delete(languageId);
622
+ if (this.clients.get(languageId) === existing)
623
+ this.clients.delete(languageId);
589
624
  }
590
- // Kill the daemon if one is running (so we get a fresh server with new config)
625
+ this.stop.signal.throwIfAborted();
626
+ // Explicit restart retains its existing daemon replacement behavior.
591
627
  this.killDaemon(languageId);
592
- // Wait for pending starts to clear
593
- const pending = this.startingServers.get(languageId);
594
- if (pending) {
595
- await pending.catch(() => { });
596
- this.startingServers.delete(languageId);
597
- }
598
628
  // Start fresh
599
629
  const config = this.serverConfigs.get(languageId);
600
630
  if (!config)
601
631
  throw new Error(`No server configured for ${languageId}`);
602
- await this.startServer(languageId, config);
632
+ await this.trackStart(languageId, config);
603
633
  }
604
634
  /** Kill a running daemon for a language (if any) */
605
635
  killDaemon(languageId) {