@bastani/atomic 0.9.16-alpha.4 → 0.9.16-alpha.6

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 (404) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +11 -6
  3. package/dist/builtin/intercom/CHANGELOG.md +15 -0
  4. package/dist/builtin/intercom/README.md +6 -4
  5. package/dist/builtin/intercom/broker/broker.ts +34 -8
  6. package/dist/builtin/intercom/broker/client.ts +13 -0
  7. package/dist/builtin/intercom/broker/pending-question-index.ts +50 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +8 -0
  9. package/dist/builtin/intercom/index.bundle.mjs +2045 -1211
  10. package/dist/builtin/intercom/package.json +2 -2
  11. package/dist/builtin/intercom/skills/intercom/SKILL.md +20 -19
  12. package/dist/builtin/mcp/index.bundle.mjs +1778 -1026
  13. package/dist/builtin/mcp/package.json +2 -2
  14. package/dist/builtin/subagents/CHANGELOG.md +11 -0
  15. package/dist/builtin/subagents/README.md +3 -3
  16. package/dist/builtin/subagents/package.json +3 -3
  17. package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
  18. package/dist/builtin/subagents/src/extension/index.bundle.mjs +2645 -1506
  19. package/dist/builtin/web-access/index.bundle.mjs +1754 -1002
  20. package/dist/builtin/web-access/package.json +2 -2
  21. package/dist/builtin/workflows/CHANGELOG.md +28 -0
  22. package/dist/builtin/workflows/README.md +3 -1
  23. package/dist/builtin/workflows/builtin/goal-ledger.ts +54 -10
  24. package/dist/builtin/workflows/builtin/goal-runner.ts +57 -12
  25. package/dist/builtin/workflows/package.json +2 -2
  26. package/dist/builtin/workflows/src/extension/index.bundle.mjs +13626 -12023
  27. package/dist/builtin/workflows/src/index.bundle.mjs +2338 -1279
  28. package/dist/bun/split-loader.js +2 -1
  29. package/dist/bun/split-loader.js.map +1 -1
  30. package/dist/cli/args.d.ts.map +1 -1
  31. package/dist/cli/args.js +6 -1
  32. package/dist/cli/args.js.map +1 -1
  33. package/dist/cli/file-processor.d.ts.map +1 -1
  34. package/dist/cli/file-processor.js +2 -1
  35. package/dist/cli/file-processor.js.map +1 -1
  36. package/dist/config-package-identity.d.ts.map +1 -1
  37. package/dist/config-package-identity.js +2 -1
  38. package/dist/config-package-identity.js.map +1 -1
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +2 -1
  41. package/dist/config.js.map +1 -1
  42. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  43. package/dist/core/agent-session-auto-compaction.js +64 -28
  44. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  45. package/dist/core/agent-session-compaction.d.ts +2 -0
  46. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  47. package/dist/core/agent-session-compaction.js +25 -5
  48. package/dist/core/agent-session-compaction.js.map +1 -1
  49. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  50. package/dist/core/agent-session-custom-message-commit.js +12 -0
  51. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  52. package/dist/core/agent-session-events.d.ts.map +1 -1
  53. package/dist/core/agent-session-events.js +2 -0
  54. package/dist/core/agent-session-events.js.map +1 -1
  55. package/dist/core/agent-session-export.d.ts +3 -1
  56. package/dist/core/agent-session-export.d.ts.map +1 -1
  57. package/dist/core/agent-session-export.js +22 -2
  58. package/dist/core/agent-session-export.js.map +1 -1
  59. package/dist/core/agent-session-message-queue.d.ts +3 -0
  60. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  61. package/dist/core/agent-session-message-queue.js +10 -0
  62. package/dist/core/agent-session-message-queue.js.map +1 -1
  63. package/dist/core/agent-session-methods.d.ts +15 -9
  64. package/dist/core/agent-session-methods.d.ts.map +1 -1
  65. package/dist/core/agent-session-methods.js.map +1 -1
  66. package/dist/core/agent-session-models.d.ts +8 -13
  67. package/dist/core/agent-session-models.d.ts.map +1 -1
  68. package/dist/core/agent-session-models.js +53 -19
  69. package/dist/core/agent-session-models.js.map +1 -1
  70. package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
  71. package/dist/core/agent-session-post-tool-compaction.js +12 -0
  72. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  73. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  74. package/dist/core/agent-session-prompt.js +3 -1
  75. package/dist/core/agent-session-prompt.js.map +1 -1
  76. package/dist/core/agent-session-tool-registry.js +3 -3
  77. package/dist/core/agent-session-tool-registry.js.map +1 -1
  78. package/dist/core/agent-session-transfer.d.ts.map +1 -1
  79. package/dist/core/agent-session-transfer.js +1 -0
  80. package/dist/core/agent-session-transfer.js.map +1 -1
  81. package/dist/core/agent-session-tree.d.ts.map +1 -1
  82. package/dist/core/agent-session-tree.js +1 -1
  83. package/dist/core/agent-session-tree.js.map +1 -1
  84. package/dist/core/agent-session-types.d.ts +3 -0
  85. package/dist/core/agent-session-types.d.ts.map +1 -1
  86. package/dist/core/agent-session-types.js.map +1 -1
  87. package/dist/core/agent-session.d.ts +2 -0
  88. package/dist/core/agent-session.d.ts.map +1 -1
  89. package/dist/core/agent-session.js +2 -0
  90. package/dist/core/agent-session.js.map +1 -1
  91. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  92. package/dist/core/auth-storage-backends.js +10 -6
  93. package/dist/core/auth-storage-backends.js.map +1 -1
  94. package/dist/core/auth-storage.d.ts.map +1 -1
  95. package/dist/core/auth-storage.js +4 -3
  96. package/dist/core/auth-storage.js.map +1 -1
  97. package/dist/core/builtin-packages.d.ts.map +1 -1
  98. package/dist/core/builtin-packages.js +2 -1
  99. package/dist/core/builtin-packages.js.map +1 -1
  100. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  101. package/dist/core/compaction/branch-summarization.js +6 -0
  102. package/dist/core/compaction/branch-summarization.js.map +1 -1
  103. package/dist/core/compaction/compaction-runner.d.ts +3 -1
  104. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  105. package/dist/core/compaction/compaction-runner.js +30 -4
  106. package/dist/core/compaction/compaction-runner.js.map +1 -1
  107. package/dist/core/compaction/compaction-types.d.ts +3 -1
  108. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  109. package/dist/core/compaction/compaction-types.js.map +1 -1
  110. package/dist/core/compaction/range-planner.d.ts +3 -1
  111. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  112. package/dist/core/compaction/range-planner.js +11 -1
  113. package/dist/core/compaction/range-planner.js.map +1 -1
  114. package/dist/core/compaction/session-summarization.d.ts.map +1 -1
  115. package/dist/core/compaction/session-summarization.js +6 -0
  116. package/dist/core/compaction/session-summarization.js.map +1 -1
  117. package/dist/core/extensions/api-types.d.ts +8 -3
  118. package/dist/core/extensions/api-types.d.ts.map +1 -1
  119. package/dist/core/extensions/api-types.js.map +1 -1
  120. package/dist/core/extensions/index.d.ts +2 -2
  121. package/dist/core/extensions/index.d.ts.map +1 -1
  122. package/dist/core/extensions/index.js +1 -1
  123. package/dist/core/extensions/index.js.map +1 -1
  124. package/dist/core/extensions/loader-api.d.ts +5 -1
  125. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  126. package/dist/core/extensions/loader-api.js +124 -36
  127. package/dist/core/extensions/loader-api.js.map +1 -1
  128. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  129. package/dist/core/extensions/loader-core.js +18 -4
  130. package/dist/core/extensions/loader-core.js.map +1 -1
  131. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  132. package/dist/core/extensions/loader-runtime.js +10 -2
  133. package/dist/core/extensions/loader-runtime.js.map +1 -1
  134. package/dist/core/extensions/session-events.d.ts +11 -2
  135. package/dist/core/extensions/session-events.d.ts.map +1 -1
  136. package/dist/core/extensions/session-events.js.map +1 -1
  137. package/dist/core/extensions/tool-events.d.ts +13 -3
  138. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  139. package/dist/core/extensions/tool-events.js +3 -0
  140. package/dist/core/extensions/tool-events.js.map +1 -1
  141. package/dist/core/extensions/types.d.ts +1 -1
  142. package/dist/core/extensions/types.d.ts.map +1 -1
  143. package/dist/core/extensions/types.js +1 -1
  144. package/dist/core/extensions/types.js.map +1 -1
  145. package/dist/core/extensions/ui-types.d.ts +2 -2
  146. package/dist/core/extensions/ui-types.js.map +1 -1
  147. package/dist/core/footer-data-provider.d.ts +1 -1
  148. package/dist/core/footer-data-provider.js +1 -1
  149. package/dist/core/footer-data-provider.js.map +1 -1
  150. package/dist/core/keybindings.d.ts +17 -16
  151. package/dist/core/keybindings.d.ts.map +1 -1
  152. package/dist/core/keybindings.js +23 -6
  153. package/dist/core/keybindings.js.map +1 -1
  154. package/dist/core/model-config.d.ts.map +1 -1
  155. package/dist/core/model-config.js +2 -1
  156. package/dist/core/model-config.js.map +1 -1
  157. package/dist/core/model-resolver-initial.d.ts +1 -0
  158. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  159. package/dist/core/model-resolver-initial.js +9 -5
  160. package/dist/core/model-resolver-initial.js.map +1 -1
  161. package/dist/core/models-store.d.ts.map +1 -1
  162. package/dist/core/models-store.js +2 -1
  163. package/dist/core/models-store.js.map +1 -1
  164. package/dist/core/package-manager-git.d.ts.map +1 -1
  165. package/dist/core/package-manager-git.js +2 -1
  166. package/dist/core/package-manager-git.js.map +1 -1
  167. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  168. package/dist/core/package-manager-manifest.js +2 -1
  169. package/dist/core/package-manager-manifest.js.map +1 -1
  170. package/dist/core/package-manager-npm.d.ts.map +1 -1
  171. package/dist/core/package-manager-npm.js +4 -3
  172. package/dist/core/package-manager-npm.js.map +1 -1
  173. package/dist/core/package-manager-operations.d.ts.map +1 -1
  174. package/dist/core/package-manager-operations.js +2 -1
  175. package/dist/core/package-manager-operations.js.map +1 -1
  176. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  177. package/dist/core/package-manager-resource-collector.js +2 -1
  178. package/dist/core/package-manager-resource-collector.js.map +1 -1
  179. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  180. package/dist/core/package-manager-resource-files.js +7 -2
  181. package/dist/core/package-manager-resource-files.js.map +1 -1
  182. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  183. package/dist/core/remote-catalog-provider.js +2 -1
  184. package/dist/core/remote-catalog-provider.js.map +1 -1
  185. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  186. package/dist/core/resource-loader-context-files.js +3 -2
  187. package/dist/core/resource-loader-context-files.js.map +1 -1
  188. package/dist/core/sdk-exports.d.ts +5 -3
  189. package/dist/core/sdk-exports.d.ts.map +1 -1
  190. package/dist/core/sdk-exports.js +4 -2
  191. package/dist/core/sdk-exports.js.map +1 -1
  192. package/dist/core/sdk.d.ts.map +1 -1
  193. package/dist/core/sdk.js +7 -3
  194. package/dist/core/sdk.js.map +1 -1
  195. package/dist/core/session-manager-core.d.ts +2 -2
  196. package/dist/core/session-manager-core.d.ts.map +1 -1
  197. package/dist/core/session-manager-core.js +4 -4
  198. package/dist/core/session-manager-core.js.map +1 -1
  199. package/dist/core/session-manager-entries.d.ts +2 -2
  200. package/dist/core/session-manager-entries.d.ts.map +1 -1
  201. package/dist/core/session-manager-entries.js +4 -3
  202. package/dist/core/session-manager-entries.js.map +1 -1
  203. package/dist/core/session-manager-storage.d.ts.map +1 -1
  204. package/dist/core/session-manager-storage.js +4 -2
  205. package/dist/core/session-manager-storage.js.map +1 -1
  206. package/dist/core/session-manager-types.d.ts +2 -0
  207. package/dist/core/session-manager-types.d.ts.map +1 -1
  208. package/dist/core/session-manager-types.js.map +1 -1
  209. package/dist/core/settings-diagnostics.d.ts +4 -0
  210. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  211. package/dist/core/settings-diagnostics.js +12 -0
  212. package/dist/core/settings-diagnostics.js.map +1 -0
  213. package/dist/core/settings-manager-basic-accessors.d.ts +4 -0
  214. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  215. package/dist/core/settings-manager-basic-accessors.js +23 -0
  216. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  217. package/dist/core/settings-manager-core.d.ts.map +1 -1
  218. package/dist/core/settings-manager-core.js +10 -2
  219. package/dist/core/settings-manager-core.js.map +1 -1
  220. package/dist/core/settings-storage.d.ts.map +1 -1
  221. package/dist/core/settings-storage.js +10 -3
  222. package/dist/core/settings-storage.js.map +1 -1
  223. package/dist/core/settings-types.d.ts +2 -0
  224. package/dist/core/settings-types.d.ts.map +1 -1
  225. package/dist/core/settings-types.js.map +1 -1
  226. package/dist/core/skills.d.ts.map +1 -1
  227. package/dist/core/skills.js +51 -33
  228. package/dist/core/skills.js.map +1 -1
  229. package/dist/core/slash-commands.d.ts.map +1 -1
  230. package/dist/core/slash-commands.js +3 -2
  231. package/dist/core/slash-commands.js.map +1 -1
  232. package/dist/core/system-prompt.d.ts.map +1 -1
  233. package/dist/core/system-prompt.js +15 -3
  234. package/dist/core/system-prompt.js.map +1 -1
  235. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  236. package/dist/core/tools/ask-user-question/config.js +2 -1
  237. package/dist/core/tools/ask-user-question/config.js.map +1 -1
  238. package/dist/core/tools/bash.d.ts +13 -1
  239. package/dist/core/tools/bash.d.ts.map +1 -1
  240. package/dist/core/tools/bash.js +9 -5
  241. package/dist/core/tools/bash.js.map +1 -1
  242. package/dist/core/tools/index.d.ts +21 -5
  243. package/dist/core/tools/index.d.ts.map +1 -1
  244. package/dist/core/tools/index.js +45 -13
  245. package/dist/core/tools/index.js.map +1 -1
  246. package/dist/core/tools/powershell.d.ts +83 -0
  247. package/dist/core/tools/powershell.d.ts.map +1 -0
  248. package/dist/core/tools/powershell.js +89 -0
  249. package/dist/core/tools/powershell.js.map +1 -0
  250. package/dist/core/usage-totals.d.ts.map +1 -1
  251. package/dist/core/usage-totals.js +2 -1
  252. package/dist/core/usage-totals.js.map +1 -1
  253. package/dist/extensions/llama/client.d.ts +6 -0
  254. package/dist/extensions/llama/client.d.ts.map +1 -1
  255. package/dist/extensions/llama/client.js +12 -0
  256. package/dist/extensions/llama/client.js.map +1 -1
  257. package/dist/extensions/llama/index.d.ts.map +1 -1
  258. package/dist/extensions/llama/index.js +2 -0
  259. package/dist/extensions/llama/index.js.map +1 -1
  260. package/dist/extensions/llama/provider.d.ts +3 -1
  261. package/dist/extensions/llama/provider.d.ts.map +1 -1
  262. package/dist/extensions/llama/provider.js +25 -8
  263. package/dist/extensions/llama/provider.js.map +1 -1
  264. package/dist/index-extensions.d.ts +2 -2
  265. package/dist/index-extensions.d.ts.map +1 -1
  266. package/dist/index-extensions.js +1 -1
  267. package/dist/index-extensions.js.map +1 -1
  268. package/dist/index.d.ts +4 -3
  269. package/dist/index.d.ts.map +1 -1
  270. package/dist/index.js +4 -3
  271. package/dist/index.js.map +1 -1
  272. package/dist/main-stdio.d.ts.map +1 -1
  273. package/dist/main-stdio.js +4 -2
  274. package/dist/main-stdio.js.map +1 -1
  275. package/dist/main.d.ts.map +1 -1
  276. package/dist/main.js +10 -3
  277. package/dist/main.js.map +1 -1
  278. package/dist/migrations-config-values.d.ts.map +1 -1
  279. package/dist/migrations-config-values.js +2 -1
  280. package/dist/migrations-config-values.js.map +1 -1
  281. package/dist/migrations.d.ts.map +1 -1
  282. package/dist/migrations.js +5 -4
  283. package/dist/migrations.js.map +1 -1
  284. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  285. package/dist/modes/interactive/components/footer.js +2 -1
  286. package/dist/modes/interactive/components/footer.js.map +1 -1
  287. package/dist/modes/interactive/components/model-selector.d.ts +2 -2
  288. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  289. package/dist/modes/interactive/components/model-selector.js +27 -16
  290. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  291. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/settings-selector-items.js +67 -14
  293. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  294. package/dist/modes/interactive/components/settings-selector-submenus.d.ts +7 -1
  295. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  296. package/dist/modes/interactive/components/settings-selector-submenus.js +38 -3
  297. package/dist/modes/interactive/components/settings-selector-submenus.js.map +1 -1
  298. package/dist/modes/interactive/components/settings-selector-types.d.ts +5 -1
  299. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  300. package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
  301. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -6
  302. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  303. package/dist/modes/interactive/components/thinking-selector.js +65 -27
  304. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  305. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  306. package/dist/modes/interactive/external-editor.js +2 -1
  307. package/dist/modes/interactive/external-editor.js.map +1 -1
  308. package/dist/modes/interactive/interactive-agent-events.js +2 -0
  309. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  310. package/dist/modes/interactive/interactive-auth-login.d.ts +1 -1
  311. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  312. package/dist/modes/interactive/interactive-auth-login.js +9 -1
  313. package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
  314. package/dist/modes/interactive/interactive-autocomplete.js +9 -0
  315. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  316. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  317. package/dist/modes/interactive/interactive-deferred-startup.js +2 -0
  318. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  319. package/dist/modes/interactive/interactive-editor-actions.js +11 -10
  320. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  321. package/dist/modes/interactive/interactive-input-handling.js +6 -0
  322. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  323. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -0
  324. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  325. package/dist/modes/interactive/interactive-mode-deps.js +1 -0
  326. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  327. package/dist/modes/interactive/interactive-mode-surface.d.ts +7 -0
  328. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  329. package/dist/modes/interactive/interactive-mode-surface.js +0 -1
  330. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  331. package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
  332. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  333. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  334. package/dist/modes/interactive/interactive-model-routing.d.ts +16 -1
  335. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  336. package/dist/modes/interactive/interactive-model-routing.js +62 -3
  337. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  338. package/dist/modes/interactive/interactive-render-chat.js +18 -0
  339. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  340. package/dist/modes/interactive/interactive-selectors.js +10 -8
  341. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  342. package/dist/modes/interactive/interactive-slash-commands.js +6 -5
  343. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  344. package/dist/modes/interactive/interactive-startup.js +9 -1
  345. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  346. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  347. package/dist/modes/interactive/theme/theme-parse.js +2 -1
  348. package/dist/modes/interactive/theme/theme-parse.js.map +1 -1
  349. package/dist/modes/json-event.d.ts +15 -18
  350. package/dist/modes/json-event.d.ts.map +1 -1
  351. package/dist/modes/json-event.js +21 -17
  352. package/dist/modes/json-event.js.map +1 -1
  353. package/dist/modes/rpc/rpc-client-api.d.ts +4 -0
  354. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  355. package/dist/modes/rpc/rpc-client-api.js +3 -0
  356. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  357. package/dist/utils/frontmatter.d.ts.map +1 -1
  358. package/dist/utils/frontmatter.js +2 -1
  359. package/dist/utils/frontmatter.js.map +1 -1
  360. package/dist/utils/management-http.d.ts +2 -0
  361. package/dist/utils/management-http.d.ts.map +1 -1
  362. package/dist/utils/management-http.js +11 -7
  363. package/dist/utils/management-http.js.map +1 -1
  364. package/dist/utils/shell.d.ts +3 -0
  365. package/dist/utils/shell.d.ts.map +1 -1
  366. package/dist/utils/shell.js +41 -10
  367. package/dist/utils/shell.js.map +1 -1
  368. package/dist/utils/syntax-highlight.d.ts +1 -0
  369. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  370. package/dist/utils/syntax-highlight.js +61 -1
  371. package/dist/utils/syntax-highlight.js.map +1 -1
  372. package/dist/utils/text.d.ts +8 -0
  373. package/dist/utils/text.d.ts.map +1 -0
  374. package/dist/utils/text.js +9 -0
  375. package/dist/utils/text.js.map +1 -0
  376. package/docs/changelog.mdx +1 -1
  377. package/docs/compaction.md +16 -3
  378. package/docs/containerization.md +17 -0
  379. package/docs/custom-provider.md +1 -1
  380. package/docs/environment-variables.md +3 -3
  381. package/docs/extensions.md +22 -5
  382. package/docs/index.md +12 -10
  383. package/docs/intercom.md +5 -3
  384. package/docs/keybindings.md +1 -1
  385. package/docs/llama-cpp.md +1 -1
  386. package/docs/models.md +2 -0
  387. package/docs/quickstart.md +80 -9
  388. package/docs/rpc.md +24 -1
  389. package/docs/sdk.md +11 -0
  390. package/docs/settings.md +16 -7
  391. package/docs/skills.md +3 -2
  392. package/docs/subagents.md +7 -0
  393. package/docs/termux.md +1 -1
  394. package/docs/usage.md +4 -3
  395. package/docs/windows.md +33 -3
  396. package/docs/workflows.md +22 -10
  397. package/examples/extensions/border-status-editor.ts +1 -1
  398. package/examples/extensions/git-checkpoint.ts +1 -1
  399. package/examples/extensions/notify.ts +1 -1
  400. package/examples/extensions/titlebar-spinner.ts +1 -1
  401. package/examples/rpc-extension-ui.ts +1 -1
  402. package/examples/sdk/README.md +1 -1
  403. package/npm-shrinkwrap.json +73 -65
  404. package/package.json +11 -13
@@ -1 +1 @@
1
- {"version":3,"file":"management-http.js","sourceRoot":"","sources":["../../src/utils/management-http.ts"],"names":[],"mappings":"AAEA,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAW5E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,KAAiB,EACjB,IAAI,GAA4B,SAAS,EACzC,OAAO,GAAsB,EAAE;IAE/B,MAAM,UAAU,GACf,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC;QACvE,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC;IACpD,MAAM,YAAY,GAAG,IAAI,EAAE,MAAM,CAAC;IAClC,MAAM,aAAa,GAClB,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/G,MAAM,MAAM,GAAG,aAAa;QAC3B,CAAC,CAAC,YAAY;YACb,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;YAChD,CAAC,CAAC,aAAa;QAChB,CAAC,CAAC,YAAY,CAAC;IAEhB,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzE,MAAM,WAAW,GAAG,aAAa,IAAI,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,GAAG,UAAU,CAAC;YACzG,IAAI,CAAC,WAAW;gBAAE,OAAO,QAAQ,CAAC;YAClC,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACR,yEAAyE;gBACzE,2CAA2C;YAC5C,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IACC,YAAY,EAAE,OAAO;gBACrB,aAAa,EAAE,OAAO;gBACtB,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,aAAa,KAAK,SAAS,CAAC;gBACtF,OAAO,IAAI,UAAU,EACpB,CAAC;gBACF,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["type FetchInput = Parameters<typeof fetch>[0];\n\nconst RETRYABLE_STATUS_CODES = new Set([408, 425, 429, 500, 502, 503, 504]);\n\nexport interface FetchRetryOptions {\n\t/** Number of additional attempts after the initial request. Defaults to two. */\n\tmaxRetries?: number;\n\t/** Retry transient HTTP responses as well as transport failures. Defaults to true. */\n\tretryOnStatus?: boolean;\n\t/** Overall timeout shared by all attempts. */\n\ttimeoutMs?: number;\n}\n\n/**\n * Fetch a management HTTP resource with a bounded immediate retry.\n *\n * This is intentionally a transport-level helper for idempotent management\n * requests (version checks, catalogs, and downloads). It must not be used for\n * agent/model operations: those can fail after the HTTP request starts and are\n * retried by their semantic caller instead.\n *\n * Caller cancellation is terminal. When timeoutMs is supplied, it is the\n * overall time budget shared by all attempts.\n */\nexport async function fetchWithRetry(\n\tinput: FetchInput,\n\tinit: RequestInit | undefined = undefined,\n\toptions: FetchRetryOptions = {},\n): Promise<Response> {\n\tconst maxRetries =\n\t\toptions.maxRetries === undefined || !Number.isFinite(options.maxRetries)\n\t\t\t? 2\n\t\t\t: Math.max(0, Math.floor(options.maxRetries));\n\tconst retryOnStatus = options.retryOnStatus ?? true;\n\tconst parentSignal = init?.signal;\n\tconst timeoutSignal =\n\t\toptions.timeoutMs !== undefined && options.timeoutMs > 0 ? AbortSignal.timeout(options.timeoutMs) : undefined;\n\tconst signal = timeoutSignal\n\t\t? parentSignal\n\t\t\t? AbortSignal.any([parentSignal, timeoutSignal])\n\t\t\t: timeoutSignal\n\t\t: parentSignal;\n\n\tfor (let attempt = 0; ; attempt += 1) {\n\t\tsignal?.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(input, signal ? { ...init, signal } : init);\n\t\t\tconst shouldRetry = retryOnStatus && RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries;\n\t\t\tif (!shouldRetry) return response;\n\t\t\ttry {\n\t\t\t\tawait response.body?.cancel();\n\t\t\t} catch {\n\t\t\t\t// The response is being discarded before a retry. Nothing useful remains\n\t\t\t\t// to do if cancelling its body also fails.\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\tparentSignal?.aborted ||\n\t\t\t\ttimeoutSignal?.aborted ||\n\t\t\t\t(error instanceof Error && error.name === \"AbortError\" && timeoutSignal === undefined) ||\n\t\t\t\tattempt >= maxRetries\n\t\t\t) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"management-http.js","sourceRoot":"","sources":["../../src/utils/management-http.ts"],"names":[],"mappings":"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","sourcesContent":["type FetchInput = Parameters<typeof fetch>[0];\n\nconst RETRYABLE_STATUS_CODES = new Set([408, 425, 429, 500, 502, 503, 504]);\n\nexport interface FetchRetryOptions {\n\t/** Number of additional attempts after the initial request. Defaults to two. */\n\tmaxRetries?: number;\n\t/** Retry transient HTTP responses as well as transport failures. Defaults to true. */\n\tretryOnStatus?: boolean;\n\t/** Overall timeout shared by all attempts. */\n\ttimeoutMs?: number;\n\t/** Per-attempt timeout; timed-out attempts remain retryable. */\n\tattemptTimeoutMs?: number;\n}\n\n/**\n * Fetch a management HTTP resource with a bounded immediate retry.\n *\n * This is intentionally a transport-level helper for idempotent management\n * requests (version checks, catalogs, and downloads). It must not be used for\n * agent/model operations: those can fail after the HTTP request starts and are\n * retried by their semantic caller instead.\n *\n * Caller cancellation is terminal. When timeoutMs is supplied, it is the\n * overall time budget shared by all attempts.\n */\nexport async function fetchWithRetry(\n\tinput: FetchInput,\n\tinit: RequestInit | undefined = undefined,\n\toptions: FetchRetryOptions = {},\n): Promise<Response> {\n\tconst maxRetries =\n\t\toptions.maxRetries === undefined || !Number.isFinite(options.maxRetries)\n\t\t\t? 2\n\t\t\t: Math.max(0, Math.floor(options.maxRetries));\n\tconst retryOnStatus = options.retryOnStatus ?? true;\n\tconst parentSignal = init?.signal;\n\tconst timeoutSignal =\n\t\toptions.timeoutMs !== undefined && options.timeoutMs > 0 ? AbortSignal.timeout(options.timeoutMs) : undefined;\n\tconst attemptTimeoutMs =\n\t\toptions.attemptTimeoutMs && options.attemptTimeoutMs > 0 ? options.attemptTimeoutMs : undefined;\n\n\tfor (let attempt = 0; ; attempt += 1) {\n\t\tparentSignal?.throwIfAborted();\n\t\ttimeoutSignal?.throwIfAborted();\n\t\tconst attemptTimeoutSignal = attemptTimeoutMs ? AbortSignal.timeout(attemptTimeoutMs) : undefined;\n\t\tconst signals = [parentSignal, timeoutSignal, attemptTimeoutSignal].filter(\n\t\t\t(signal): signal is AbortSignal => signal !== undefined,\n\t\t);\n\t\tconst signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];\n\t\ttry {\n\t\t\tconst response = await fetch(input, signal ? { ...init, signal } : init);\n\t\t\tconst shouldRetry = retryOnStatus && RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries;\n\t\t\tif (!shouldRetry) return response;\n\t\t\ttry {\n\t\t\t\tawait response.body?.cancel();\n\t\t\t} catch {\n\t\t\t\t// The response is being discarded before a retry. Nothing useful remains\n\t\t\t\t// to do if cancelling its body also fails.\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst attemptTimedOut =\n\t\t\t\tattemptTimeoutSignal?.aborted === true && !parentSignal?.aborted && !timeoutSignal?.aborted;\n\t\t\tif (\n\t\t\t\tparentSignal?.aborted ||\n\t\t\t\ttimeoutSignal?.aborted ||\n\t\t\t\t(error instanceof Error &&\n\t\t\t\t\terror.name === \"AbortError\" &&\n\t\t\t\t\t!attemptTimedOut &&\n\t\t\t\t\ttimeoutSignal === undefined) ||\n\t\t\t\tattempt >= maxRetries\n\t\t\t) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -11,6 +11,9 @@ export interface ShellConfig {
11
11
  * 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh
12
12
  */
13
13
  export declare function getShellConfig(customShellPath?: string): ShellConfig;
14
+ export declare const POWERSHELL_ARGS: readonly ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command"];
15
+ export declare function getPowerShellConfig(): ShellConfig;
16
+ export declare function isPowerShellAvailable(): boolean;
14
17
  export declare function getShellEnv(): NodeJS.ProcessEnv;
15
18
  /**
16
19
  * Sanitize binary output for display/storage.
@@ -1 +1 @@
1
- {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAsDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,WAAW,CAqDpE;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAY/C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8BxD;AAqCD,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAcpG;AAQD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAyBjD"}
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAsDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,WAAW,CAqDpE;AAED,eAAO,MAAM,eAAe,YAAI,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAEpH,wBAAgB,mBAAmB,IAAI,WAAW,CAKjD;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAO/C;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAY/C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8BxD;AA4CD,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAcpG;AAQD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAmCjD"}
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { delimiter } from "node:path";
2
+ import { delimiter, join } from "node:path";
3
3
  import { Worker } from "node:worker_threads";
4
4
  import { spawn, spawnSync } from "child_process";
5
5
  import { getBinDir } from "../config.js";
@@ -14,11 +14,11 @@ function getBashShellConfig(shell) {
14
14
  /**
15
15
  * Find bash executable on PATH (cross-platform)
16
16
  */
17
- function findBashOnPath() {
17
+ function findExecutableOnPath(executable) {
18
18
  if (process.platform === "win32") {
19
19
  // Windows: Use 'where' and verify file exists (where can return non-existent paths)
20
20
  try {
21
- const result = spawnSync("where", ["bash.exe"], {
21
+ const result = spawnSync("where", [executable], {
22
22
  encoding: "utf-8",
23
23
  timeout: 5000,
24
24
  env: createChildProcessEnvironment(),
@@ -37,7 +37,7 @@ function findBashOnPath() {
37
37
  }
38
38
  // Unix: Use 'which' and trust its output (handles Termux and special filesystems)
39
39
  try {
40
- const result = spawnSync("which", ["bash"], {
40
+ const result = spawnSync("which", [executable], {
41
41
  encoding: "utf-8",
42
42
  timeout: 5000,
43
43
  env: createChildProcessEnvironment(),
@@ -86,7 +86,7 @@ export function getShellConfig(customShellPath) {
86
86
  }
87
87
  }
88
88
  // 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)
89
- const bashOnPath = findBashOnPath();
89
+ const bashOnPath = findExecutableOnPath("bash.exe");
90
90
  if (bashOnPath) {
91
91
  return getBashShellConfig(bashOnPath);
92
92
  }
@@ -100,12 +100,30 @@ export function getShellConfig(customShellPath) {
100
100
  if (existsSync("/bin/bash")) {
101
101
  return getBashShellConfig("/bin/bash");
102
102
  }
103
- const bashOnPath = findBashOnPath();
103
+ const bashOnPath = findExecutableOnPath("bash");
104
104
  if (bashOnPath) {
105
105
  return getBashShellConfig(bashOnPath);
106
106
  }
107
107
  return { shell: "sh", args: ["-c"] };
108
108
  }
109
+ export const POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command"];
110
+ export function getPowerShellConfig() {
111
+ if (process.platform !== "win32")
112
+ throw new Error("The powershell tool is only available on Windows.");
113
+ const shell = findExecutableOnPath("pwsh.exe") ?? findExecutableOnPath("powershell.exe");
114
+ if (!shell)
115
+ throw new Error("No PowerShell executable found. Install PowerShell or add it to PATH.");
116
+ return { shell, args: [...POWERSHELL_ARGS] };
117
+ }
118
+ export function isPowerShellAvailable() {
119
+ try {
120
+ getPowerShellConfig();
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
109
127
  export function getShellEnv() {
110
128
  const binDir = getBinDir();
111
129
  const pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === "path") ?? "PATH";
@@ -160,6 +178,7 @@ const PARENT_GUARDIAN_SOURCE = `
160
178
  const { parentPort, workerData } = require("node:worker_threads");
161
179
  const { spawn } = require("node:child_process");
162
180
  const { existsSync } = require("node:fs");
181
+ const { join } = require("node:path");
163
182
  const tracked = new Set();
164
183
  parentPort.on("message", ({ action, pid }) => {
165
184
  if (action === "track") tracked.add(pid);
@@ -167,7 +186,13 @@ parentPort.on("message", ({ action, pid }) => {
167
186
  });
168
187
  function killTree(pid) {
169
188
  if (process.platform === "win32") {
170
- try { spawn("taskkill", ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", detached: true, env: { ...process.env, AI_AGENT: workerData.aiAgent } }); } catch {}
189
+ // Same System32 hardening as killProcessTree: PATH is not trusted here, and an
190
+ // unconsumed async "error" from a failed spawn would take the guardian thread
191
+ // down and leak every detached child it exists to reap.
192
+ try {
193
+ const child = spawn(join(process.env.SystemRoot || "C:\\\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true, env: { ...process.env, AI_AGENT: workerData.aiAgent } });
194
+ child.once("error", () => {});
195
+ } catch {}
171
196
  return;
172
197
  }
173
198
  try { process.kill(-pid, "SIGKILL"); }
@@ -225,16 +250,22 @@ export function killTrackedDetachedChildren() {
225
250
  */
226
251
  export function killProcessTree(pid) {
227
252
  if (process.platform === "win32") {
228
- // Use taskkill on Windows to kill process tree
253
+ // Use the trusted System32 executable so cleanup does not depend on PATH.
229
254
  try {
230
- spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
255
+ const child = spawn(join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
231
256
  stdio: "ignore",
232
257
  detached: true,
258
+ // `detached` gives the child its own console on Windows unless this is
259
+ // set, so omitting it both flashes a window and pays for a console
260
+ // allocation on every tree kill. Every other spawn in the fork sets it.
261
+ windowsHide: true,
233
262
  env: createChildProcessEnvironment(),
234
263
  });
264
+ // A failed spawn emits "error" asynchronously; consume it to avoid crashing Node.
265
+ child.once("error", () => { });
235
266
  }
236
267
  catch {
237
- // Ignore errors if taskkill fails
268
+ // Ignore errors if taskkill fails.
238
269
  }
239
270
  }
240
271
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { delimiter } from \"node:path\";\nimport { Worker } from \"node:worker_threads\";\nimport { spawn, spawnSync } from \"child_process\";\nimport { getBinDir } from \"../config.ts\";\nimport { createChildProcessEnvironment } from \"./child-process.ts\";\n\nexport interface ShellConfig {\n\tshell: string;\n\targs: string[];\n\tcommandTransport?: \"argv\" | \"stdin\";\n}\n\nfunction isLegacyWslBashPath(path: string): boolean {\n\tconst normalized = path.replace(/\\//g, \"\\\\\").toLowerCase();\n\treturn /^[a-z]:\\\\windows\\\\(?:system32|sysnative)\\\\bash\\.exe$/.test(normalized);\n}\n\nfunction getBashShellConfig(shell: string): ShellConfig {\n\treturn isLegacyWslBashPath(shell) ? { shell, args: [\"-s\"], commandTransport: \"stdin\" } : { shell, args: [\"-c\"] };\n}\n\n/**\n * Find bash executable on PATH (cross-platform)\n */\nfunction findBashOnPath(): string | null {\n\tif (process.platform === \"win32\") {\n\t\t// Windows: Use 'where' and verify file exists (where can return non-existent paths)\n\t\ttry {\n\t\t\tconst result = spawnSync(\"where\", [\"bash.exe\"], {\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t\ttimeout: 5000,\n\t\t\t\tenv: createChildProcessEnvironment(),\n\t\t\t});\n\t\t\tif (result.status === 0 && result.stdout) {\n\t\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\t\tif (firstMatch && existsSync(firstMatch)) {\n\t\t\t\t\treturn firstMatch;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Unix: Use 'which' and trust its output (handles Termux and special filesystems)\n\ttry {\n\t\tconst result = spawnSync(\"which\", [\"bash\"], {\n\t\t\tencoding: \"utf-8\",\n\t\t\ttimeout: 5000,\n\t\t\tenv: createChildProcessEnvironment(),\n\t\t});\n\t\tif (result.status === 0 && result.stdout) {\n\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\tif (firstMatch) {\n\t\t\t\treturn firstMatch;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore errors\n\t}\n\treturn null;\n}\n\n/**\n * Resolve shell configuration based on platform and an optional explicit shell path.\n * Resolution order:\n * 1. User-specified shellPath\n * 2. On Windows: Git Bash in known locations, then bash on PATH\n * 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh\n */\nexport function getShellConfig(customShellPath?: string): ShellConfig {\n\t// 1. Check user-specified shell path\n\tif (customShellPath) {\n\t\tif (existsSync(customShellPath)) {\n\t\t\treturn getBashShellConfig(customShellPath);\n\t\t}\n\t\tthrow new Error(`Custom shell path not found: ${customShellPath}`);\n\t}\n\n\tif (process.platform === \"win32\") {\n\t\t// 2. Try Git Bash in known locations\n\t\tconst paths: string[] = [];\n\t\tconst programFiles = process.env.ProgramFiles;\n\t\tif (programFiles) {\n\t\t\tpaths.push(`${programFiles}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\t\tconst programFilesX86 = process.env[\"ProgramFiles(x86)\"];\n\t\tif (programFilesX86) {\n\t\t\tpaths.push(`${programFilesX86}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\n\t\tfor (const path of paths) {\n\t\t\tif (existsSync(path)) {\n\t\t\t\treturn getBashShellConfig(path);\n\t\t\t}\n\t\t}\n\n\t\t// 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)\n\t\tconst bashOnPath = findBashOnPath();\n\t\tif (bashOnPath) {\n\t\t\treturn getBashShellConfig(bashOnPath);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`No bash shell found. Options:\\n` +\n\t\t\t\t` 1. Install Git for Windows: https://git-scm.com/download/win\\n` +\n\t\t\t\t` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\\n` +\n\t\t\t\t\" 3. Set shellPath in settings.json\\n\\n\" +\n\t\t\t\t`Searched Git Bash in:\\n${paths.map((p) => ` ${p}`).join(\"\\n\")}`,\n\t\t);\n\t}\n\n\t// Unix: try /bin/bash, then bash on PATH, then fallback to sh\n\tif (existsSync(\"/bin/bash\")) {\n\t\treturn getBashShellConfig(\"/bin/bash\");\n\t}\n\n\tconst bashOnPath = findBashOnPath();\n\tif (bashOnPath) {\n\t\treturn getBashShellConfig(bashOnPath);\n\t}\n\n\treturn { shell: \"sh\", args: [\"-c\"] };\n}\n\nexport function getShellEnv(): NodeJS.ProcessEnv {\n\tconst binDir = getBinDir();\n\tconst pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === \"path\") ?? \"PATH\";\n\tconst currentPath = process.env[pathKey] ?? \"\";\n\tconst pathEntries = currentPath.split(delimiter).filter(Boolean);\n\tconst hasBinDir = pathEntries.includes(binDir);\n\tconst updatedPath = hasBinDir ? currentPath : [binDir, currentPath].filter(Boolean).join(delimiter);\n\n\treturn {\n\t\t...process.env,\n\t\t[pathKey]: updatedPath,\n\t};\n}\n\n/**\n * Sanitize binary output for display/storage.\n * Removes characters that crash string-width or cause display issues:\n * - Control characters (except tab, newline, carriage return)\n * - Lone surrogates\n * - Unicode Format characters (crash string-width due to a bug)\n * - Characters with undefined code points\n */\nexport function sanitizeBinaryOutput(str: string): string {\n\t// Use Array.from to properly iterate over code points (not code units)\n\t// This handles surrogate pairs correctly and catches edge cases where\n\t// codePointAt() might return undefined\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\t// Filter out characters that cause string-width to crash\n\t\t\t// This includes:\n\t\t\t// - Unicode format characters\n\t\t\t// - Lone surrogates (already filtered by Array.from)\n\t\t\t// - Control chars except \\t \\n \\r\n\t\t\t// - Characters with undefined code points\n\n\t\t\tconst code = char.codePointAt(0);\n\n\t\t\t// Skip if code point is undefined (edge case with invalid strings)\n\t\t\tif (code === undefined) return false;\n\n\t\t\t// Allow tab, newline, carriage return\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\n\t\t\t// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0x0d)\n\t\t\tif (code <= 0x1f) return false;\n\n\t\t\t// Filter out Unicode format characters\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n\ninterface DetachedChildGuardian {\n\tpostMessage(message: { action: \"track\" | \"untrack\"; pid: number }): void;\n\tterminate(): Promise<number>;\n\tunref(): void;\n}\n\nlet detachedChildGuardian: DetachedChildGuardian | undefined;\n\nconst PARENT_GUARDIAN_SOURCE = `\nconst { parentPort, workerData } = require(\"node:worker_threads\");\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst tracked = new Set();\nparentPort.on(\"message\", ({ action, pid }) => {\n if (action === \"track\") tracked.add(pid);\n else tracked.delete(pid);\n});\nfunction killTree(pid) {\n if (process.platform === \"win32\") {\n try { spawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], { stdio: \"ignore\", detached: true, env: { ...process.env, AI_AGENT: workerData.aiAgent } }); } catch {}\n return;\n }\n try { process.kill(-pid, \"SIGKILL\"); }\n catch { try { process.kill(pid, \"SIGKILL\"); } catch {} }\n}\nconst timer = setInterval(() => {\n let parentAlive = true;\n try { process.kill(workerData.parentPid, 0); } catch { parentAlive = false; }\n if (parentAlive && (!workerData.stopFile || !existsSync(workerData.stopFile))) return;\n for (const pid of tracked) killTree(pid);\n killTree(process.pid);\n}, 50);\ntimer.unref?.();\n`;\n\nexport function startParentProcessGuardian(parentPid: number, stopFile?: string): () => Promise<void> {\n\tif (detachedChildGuardian) return async () => {};\n\tconst guardian = new Worker(PARENT_GUARDIAN_SOURCE, {\n\t\teval: true,\n\t\tworkerData: { parentPid, stopFile, aiAgent: createChildProcessEnvironment().AI_AGENT },\n\t});\n\tdetachedChildGuardian = guardian;\n\tfor (const pid of trackedDetachedChildPids) guardian.postMessage({ action: \"track\", pid });\n\tguardian.unref();\n\treturn async () => {\n\t\tif (detachedChildGuardian !== guardian) return;\n\t\tdetachedChildGuardian = undefined;\n\t\tawait guardian.terminate();\n\t};\n}\n\n/**\n * Detached child processes must be tracked so they can be killed on parent\n * shutdown signals (SIGHUP/SIGTERM).\n */\nconst trackedDetachedChildPids = new Set<number>();\n\nexport function trackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.add(pid);\n\tdetachedChildGuardian?.postMessage({ action: \"track\", pid });\n}\n\nexport function untrackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.delete(pid);\n\tdetachedChildGuardian?.postMessage({ action: \"untrack\", pid });\n}\n\nexport function killTrackedDetachedChildren(): void {\n\tfor (const pid of trackedDetachedChildPids) {\n\t\tkillProcessTree(pid);\n\t\tdetachedChildGuardian?.postMessage({ action: \"untrack\", pid });\n\t}\n\ttrackedDetachedChildPids.clear();\n}\n\n/**\n * Kill a process and all its children (cross-platform)\n */\nexport function killProcessTree(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\t// Use taskkill on Windows to kill process tree\n\t\ttry {\n\t\t\tspawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\t\tstdio: \"ignore\",\n\t\t\t\tdetached: true,\n\t\t\t\tenv: createChildProcessEnvironment(),\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors if taskkill fails\n\t\t}\n\t} else {\n\t\t// Use SIGKILL on Unix/Linux/Mac\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\t// Fallback to killing just the child if process group kill fails\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGKILL\");\n\t\t\t} catch {\n\t\t\t\t// Process already dead\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\nimport { Worker } from \"node:worker_threads\";\nimport { spawn, spawnSync } from \"child_process\";\nimport { getBinDir } from \"../config.ts\";\nimport { createChildProcessEnvironment } from \"./child-process.ts\";\n\nexport interface ShellConfig {\n\tshell: string;\n\targs: string[];\n\tcommandTransport?: \"argv\" | \"stdin\";\n}\n\nfunction isLegacyWslBashPath(path: string): boolean {\n\tconst normalized = path.replace(/\\//g, \"\\\\\").toLowerCase();\n\treturn /^[a-z]:\\\\windows\\\\(?:system32|sysnative)\\\\bash\\.exe$/.test(normalized);\n}\n\nfunction getBashShellConfig(shell: string): ShellConfig {\n\treturn isLegacyWslBashPath(shell) ? { shell, args: [\"-s\"], commandTransport: \"stdin\" } : { shell, args: [\"-c\"] };\n}\n\n/**\n * Find bash executable on PATH (cross-platform)\n */\nfunction findExecutableOnPath(executable: string): string | null {\n\tif (process.platform === \"win32\") {\n\t\t// Windows: Use 'where' and verify file exists (where can return non-existent paths)\n\t\ttry {\n\t\t\tconst result = spawnSync(\"where\", [executable], {\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t\ttimeout: 5000,\n\t\t\t\tenv: createChildProcessEnvironment(),\n\t\t\t});\n\t\t\tif (result.status === 0 && result.stdout) {\n\t\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\t\tif (firstMatch && existsSync(firstMatch)) {\n\t\t\t\t\treturn firstMatch;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Unix: Use 'which' and trust its output (handles Termux and special filesystems)\n\ttry {\n\t\tconst result = spawnSync(\"which\", [executable], {\n\t\t\tencoding: \"utf-8\",\n\t\t\ttimeout: 5000,\n\t\t\tenv: createChildProcessEnvironment(),\n\t\t});\n\t\tif (result.status === 0 && result.stdout) {\n\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\tif (firstMatch) {\n\t\t\t\treturn firstMatch;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore errors\n\t}\n\treturn null;\n}\n\n/**\n * Resolve shell configuration based on platform and an optional explicit shell path.\n * Resolution order:\n * 1. User-specified shellPath\n * 2. On Windows: Git Bash in known locations, then bash on PATH\n * 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh\n */\nexport function getShellConfig(customShellPath?: string): ShellConfig {\n\t// 1. Check user-specified shell path\n\tif (customShellPath) {\n\t\tif (existsSync(customShellPath)) {\n\t\t\treturn getBashShellConfig(customShellPath);\n\t\t}\n\t\tthrow new Error(`Custom shell path not found: ${customShellPath}`);\n\t}\n\n\tif (process.platform === \"win32\") {\n\t\t// 2. Try Git Bash in known locations\n\t\tconst paths: string[] = [];\n\t\tconst programFiles = process.env.ProgramFiles;\n\t\tif (programFiles) {\n\t\t\tpaths.push(`${programFiles}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\t\tconst programFilesX86 = process.env[\"ProgramFiles(x86)\"];\n\t\tif (programFilesX86) {\n\t\t\tpaths.push(`${programFilesX86}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\n\t\tfor (const path of paths) {\n\t\t\tif (existsSync(path)) {\n\t\t\t\treturn getBashShellConfig(path);\n\t\t\t}\n\t\t}\n\n\t\t// 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)\n\t\tconst bashOnPath = findExecutableOnPath(\"bash.exe\");\n\t\tif (bashOnPath) {\n\t\t\treturn getBashShellConfig(bashOnPath);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`No bash shell found. Options:\\n` +\n\t\t\t\t` 1. Install Git for Windows: https://git-scm.com/download/win\\n` +\n\t\t\t\t` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\\n` +\n\t\t\t\t\" 3. Set shellPath in settings.json\\n\\n\" +\n\t\t\t\t`Searched Git Bash in:\\n${paths.map((p) => ` ${p}`).join(\"\\n\")}`,\n\t\t);\n\t}\n\n\t// Unix: try /bin/bash, then bash on PATH, then fallback to sh\n\tif (existsSync(\"/bin/bash\")) {\n\t\treturn getBashShellConfig(\"/bin/bash\");\n\t}\n\n\tconst bashOnPath = findExecutableOnPath(\"bash\");\n\tif (bashOnPath) {\n\t\treturn getBashShellConfig(bashOnPath);\n\t}\n\n\treturn { shell: \"sh\", args: [\"-c\"] };\n}\n\nexport const POWERSHELL_ARGS = [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-Command\"] as const;\n\nexport function getPowerShellConfig(): ShellConfig {\n\tif (process.platform !== \"win32\") throw new Error(\"The powershell tool is only available on Windows.\");\n\tconst shell = findExecutableOnPath(\"pwsh.exe\") ?? findExecutableOnPath(\"powershell.exe\");\n\tif (!shell) throw new Error(\"No PowerShell executable found. Install PowerShell or add it to PATH.\");\n\treturn { shell, args: [...POWERSHELL_ARGS] };\n}\n\nexport function isPowerShellAvailable(): boolean {\n\ttry {\n\t\tgetPowerShellConfig();\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function getShellEnv(): NodeJS.ProcessEnv {\n\tconst binDir = getBinDir();\n\tconst pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === \"path\") ?? \"PATH\";\n\tconst currentPath = process.env[pathKey] ?? \"\";\n\tconst pathEntries = currentPath.split(delimiter).filter(Boolean);\n\tconst hasBinDir = pathEntries.includes(binDir);\n\tconst updatedPath = hasBinDir ? currentPath : [binDir, currentPath].filter(Boolean).join(delimiter);\n\n\treturn {\n\t\t...process.env,\n\t\t[pathKey]: updatedPath,\n\t};\n}\n\n/**\n * Sanitize binary output for display/storage.\n * Removes characters that crash string-width or cause display issues:\n * - Control characters (except tab, newline, carriage return)\n * - Lone surrogates\n * - Unicode Format characters (crash string-width due to a bug)\n * - Characters with undefined code points\n */\nexport function sanitizeBinaryOutput(str: string): string {\n\t// Use Array.from to properly iterate over code points (not code units)\n\t// This handles surrogate pairs correctly and catches edge cases where\n\t// codePointAt() might return undefined\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\t// Filter out characters that cause string-width to crash\n\t\t\t// This includes:\n\t\t\t// - Unicode format characters\n\t\t\t// - Lone surrogates (already filtered by Array.from)\n\t\t\t// - Control chars except \\t \\n \\r\n\t\t\t// - Characters with undefined code points\n\n\t\t\tconst code = char.codePointAt(0);\n\n\t\t\t// Skip if code point is undefined (edge case with invalid strings)\n\t\t\tif (code === undefined) return false;\n\n\t\t\t// Allow tab, newline, carriage return\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\n\t\t\t// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0x0d)\n\t\t\tif (code <= 0x1f) return false;\n\n\t\t\t// Filter out Unicode format characters\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n\ninterface DetachedChildGuardian {\n\tpostMessage(message: { action: \"track\" | \"untrack\"; pid: number }): void;\n\tterminate(): Promise<number>;\n\tunref(): void;\n}\n\nlet detachedChildGuardian: DetachedChildGuardian | undefined;\n\nconst PARENT_GUARDIAN_SOURCE = `\nconst { parentPort, workerData } = require(\"node:worker_threads\");\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\nconst tracked = new Set();\nparentPort.on(\"message\", ({ action, pid }) => {\n if (action === \"track\") tracked.add(pid);\n else tracked.delete(pid);\n});\nfunction killTree(pid) {\n if (process.platform === \"win32\") {\n // Same System32 hardening as killProcessTree: PATH is not trusted here, and an\n // unconsumed async \"error\" from a failed spawn would take the guardian thread\n // down and leak every detached child it exists to reap.\n try {\n const child = spawn(join(process.env.SystemRoot || \"C:\\\\\\\\Windows\", \"System32\", \"taskkill.exe\"), [\"/F\", \"/T\", \"/PID\", String(pid)], { stdio: \"ignore\", detached: true, windowsHide: true, env: { ...process.env, AI_AGENT: workerData.aiAgent } });\n child.once(\"error\", () => {});\n } catch {}\n return;\n }\n try { process.kill(-pid, \"SIGKILL\"); }\n catch { try { process.kill(pid, \"SIGKILL\"); } catch {} }\n}\nconst timer = setInterval(() => {\n let parentAlive = true;\n try { process.kill(workerData.parentPid, 0); } catch { parentAlive = false; }\n if (parentAlive && (!workerData.stopFile || !existsSync(workerData.stopFile))) return;\n for (const pid of tracked) killTree(pid);\n killTree(process.pid);\n}, 50);\ntimer.unref?.();\n`;\n\nexport function startParentProcessGuardian(parentPid: number, stopFile?: string): () => Promise<void> {\n\tif (detachedChildGuardian) return async () => {};\n\tconst guardian = new Worker(PARENT_GUARDIAN_SOURCE, {\n\t\teval: true,\n\t\tworkerData: { parentPid, stopFile, aiAgent: createChildProcessEnvironment().AI_AGENT },\n\t});\n\tdetachedChildGuardian = guardian;\n\tfor (const pid of trackedDetachedChildPids) guardian.postMessage({ action: \"track\", pid });\n\tguardian.unref();\n\treturn async () => {\n\t\tif (detachedChildGuardian !== guardian) return;\n\t\tdetachedChildGuardian = undefined;\n\t\tawait guardian.terminate();\n\t};\n}\n\n/**\n * Detached child processes must be tracked so they can be killed on parent\n * shutdown signals (SIGHUP/SIGTERM).\n */\nconst trackedDetachedChildPids = new Set<number>();\n\nexport function trackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.add(pid);\n\tdetachedChildGuardian?.postMessage({ action: \"track\", pid });\n}\n\nexport function untrackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.delete(pid);\n\tdetachedChildGuardian?.postMessage({ action: \"untrack\", pid });\n}\n\nexport function killTrackedDetachedChildren(): void {\n\tfor (const pid of trackedDetachedChildPids) {\n\t\tkillProcessTree(pid);\n\t\tdetachedChildGuardian?.postMessage({ action: \"untrack\", pid });\n\t}\n\ttrackedDetachedChildPids.clear();\n}\n\n/**\n * Kill a process and all its children (cross-platform)\n */\nexport function killProcessTree(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\t// Use the trusted System32 executable so cleanup does not depend on PATH.\n\t\ttry {\n\t\t\tconst child = spawn(\n\t\t\t\tjoin(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"taskkill.exe\"),\n\t\t\t\t[\"/F\", \"/T\", \"/PID\", String(pid)],\n\t\t\t\t{\n\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\tdetached: true,\n\t\t\t\t\t// `detached` gives the child its own console on Windows unless this is\n\t\t\t\t\t// set, so omitting it both flashes a window and pays for a console\n\t\t\t\t\t// allocation on every tree kill. Every other spawn in the fork sets it.\n\t\t\t\t\twindowsHide: true,\n\t\t\t\t\tenv: createChildProcessEnvironment(),\n\t\t\t\t},\n\t\t\t);\n\t\t\t// A failed spawn emits \"error\" asynchronously; consume it to avoid crashing Node.\n\t\t\tchild.once(\"error\", () => {});\n\t\t} catch {\n\t\t\t// Ignore errors if taskkill fails.\n\t\t}\n\t} else {\n\t\t// Use SIGKILL on Unix/Linux/Mac\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\t// Fallback to killing just the child if process group kill fails\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGKILL\");\n\t\t\t} catch {\n\t\t\t\t// Process already dead\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export declare function loadAllHighlightLanguages(): Promise<void>;
1
2
  export type HighlightFormatter = (text: string) => string;
2
3
  export type HighlightTheme = Partial<Record<string, HighlightFormatter>>;
3
4
  export interface HighlightOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,CAoDtF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAQ9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
1
+ {"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAqDA,wBAAgB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAezD;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,CAoDtF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAQ9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -1,5 +1,65 @@
1
- import hljs from "highlight.js";
1
+ /// <reference path="./highlight-js.d.ts" />
2
+ import hljs from "highlight.js/lib/core.js";
3
+ import bash from "highlight.js/lib/languages/bash.js";
4
+ import c from "highlight.js/lib/languages/c.js";
5
+ import cpp from "highlight.js/lib/languages/cpp.js";
6
+ import csharp from "highlight.js/lib/languages/csharp.js";
7
+ import dart from "highlight.js/lib/languages/dart.js";
8
+ import go from "highlight.js/lib/languages/go.js";
9
+ import groovy from "highlight.js/lib/languages/groovy.js";
10
+ import java from "highlight.js/lib/languages/java.js";
11
+ import javascript from "highlight.js/lib/languages/javascript.js";
12
+ import kotlin from "highlight.js/lib/languages/kotlin.js";
13
+ import lua from "highlight.js/lib/languages/lua.js";
14
+ import nix from "highlight.js/lib/languages/nix.js";
15
+ import perl from "highlight.js/lib/languages/perl.js";
16
+ import php from "highlight.js/lib/languages/php.js";
17
+ import python from "highlight.js/lib/languages/python.js";
18
+ import ruby from "highlight.js/lib/languages/ruby.js";
19
+ import rust from "highlight.js/lib/languages/rust.js";
20
+ import scala from "highlight.js/lib/languages/scala.js";
21
+ import swift from "highlight.js/lib/languages/swift.js";
22
+ import typescript from "highlight.js/lib/languages/typescript.js";
2
23
  import { decodeHtmlEntityAt } from "./html.js";
24
+ const eagerLanguages = {
25
+ python,
26
+ java,
27
+ go,
28
+ javascript,
29
+ cpp,
30
+ typescript,
31
+ php,
32
+ ruby,
33
+ c,
34
+ csharp,
35
+ nix,
36
+ bash,
37
+ rust,
38
+ scala,
39
+ kotlin,
40
+ swift,
41
+ dart,
42
+ groovy,
43
+ perl,
44
+ lua,
45
+ };
46
+ for (const [name, language] of Object.entries(eagerLanguages)) {
47
+ hljs.registerLanguage(name, language);
48
+ }
49
+ let allLanguagesPromise;
50
+ export function loadAllHighlightLanguages() {
51
+ if (!allLanguagesPromise) {
52
+ allLanguagesPromise = new Promise((resolve) => {
53
+ setImmediate(() => {
54
+ void import("highlight.js/lib/index.js").then(() => resolve(), () => {
55
+ // Eager languages and plaintext fallback remain available.
56
+ resolve();
57
+ });
58
+ });
59
+ });
60
+ }
61
+ return allLanguagesPromise;
62
+ }
3
63
  const SPAN_CLOSE = "</span>";
4
64
  const HIGHLIGHT_CLASS_PREFIX = "hljs-";
5
65
  function getScopeFromSpanTag(tag) {
@@ -1 +1 @@
1
- {"version":3,"file":"syntax-highlight.js","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"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","sourcesContent":["import hljs from \"highlight.js\";\nimport { decodeHtmlEntityAt } from \"./html.ts\";\n\nexport type HighlightFormatter = (text: string) => string;\nexport type HighlightTheme = Partial<Record<string, HighlightFormatter>>;\n\nexport interface HighlightOptions {\n\tlanguage?: string;\n\tignoreIllegals?: boolean;\n\tlanguageSubset?: string[];\n\ttheme?: HighlightTheme;\n}\n\nconst SPAN_CLOSE = \"</span>\";\nconst HIGHLIGHT_CLASS_PREFIX = \"hljs-\";\n\nfunction getScopeFromSpanTag(tag: string): string | undefined {\n\tconst match = /\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/.exec(tag);\n\tconst classValue = match?.[1] ?? match?.[2];\n\tif (!classValue) {\n\t\treturn undefined;\n\t}\n\n\tfor (const className of classValue.split(/\\s+/)) {\n\t\tif (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {\n\t\t\treturn className.slice(HIGHLIGHT_CLASS_PREFIX.length);\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getScopeFormatter(scope: string, theme: HighlightTheme): HighlightFormatter | undefined {\n\tconst exact = theme[scope];\n\tif (exact) {\n\t\treturn exact;\n\t}\n\n\tconst dotIndex = scope.indexOf(\".\");\n\tif (dotIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dotIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\tconst dashIndex = scope.indexOf(\"-\");\n\tif (dashIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dashIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getActiveFormatter(scopes: Array<string | undefined>, theme: HighlightTheme): HighlightFormatter | undefined {\n\tfor (let i = scopes.length - 1; i >= 0; i--) {\n\t\tconst scope = scopes[i];\n\t\tif (!scope) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst formatter = getScopeFormatter(scope, theme);\n\t\tif (formatter) {\n\t\t\treturn formatter;\n\t\t}\n\t}\n\treturn theme.default;\n}\n\nfunction isSpanOpenTagStart(html: string, index: number): boolean {\n\tif (!html.startsWith(\"<span\", index)) {\n\t\treturn false;\n\t}\n\tconst nextChar = html[index + \"<span\".length];\n\treturn nextChar === \">\" || nextChar === \" \" || nextChar === \"\\t\" || nextChar === \"\\n\" || nextChar === \"\\r\";\n}\n\nexport function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): string {\n\tlet output = \"\";\n\tlet textBuffer = \"\";\n\tconst scopes: Array<string | undefined> = [];\n\n\tconst flushText = () => {\n\t\tif (!textBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tconst formatter = getActiveFormatter(scopes, theme);\n\t\toutput += formatter ? formatter(textBuffer) : textBuffer;\n\t\ttextBuffer = \"\";\n\t};\n\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tif (isSpanOpenTagStart(html, index)) {\n\t\t\tconst tagEndIndex = html.indexOf(\">\", index + 5);\n\t\t\tif (tagEndIndex !== -1) {\n\t\t\t\tflushText();\n\t\t\t\tconst tag = html.slice(index, tagEndIndex + 1);\n\t\t\t\tconst scope = getScopeFromSpanTag(tag);\n\t\t\t\tscopes.push(scope);\n\t\t\t\tindex = tagEndIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (html.startsWith(SPAN_CLOSE, index)) {\n\t\t\tflushText();\n\t\t\tif (scopes.length > 0) {\n\t\t\t\tscopes.pop();\n\t\t\t}\n\t\t\tindex += SPAN_CLOSE.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (html[index] === \"&\") {\n\t\t\tconst decoded = decodeHtmlEntityAt(html, index);\n\t\t\tif (decoded) {\n\t\t\t\ttextBuffer += decoded.text;\n\t\t\t\tindex += decoded.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\ttextBuffer += html[index];\n\t\tindex++;\n\t}\n\n\tflushText();\n\treturn output;\n}\n\nexport function highlight(code: string, options: HighlightOptions = {}): string {\n\tconst html = options.language\n\t\t? hljs.highlight(code, {\n\t\t\t\tlanguage: options.language,\n\t\t\t\tignoreIllegals: options.ignoreIllegals,\n\t\t\t}).value\n\t\t: hljs.highlightAuto(code, options.languageSubset).value;\n\treturn renderHighlightedHtml(html, options.theme);\n}\n\nexport function supportsLanguage(name: string): boolean {\n\treturn hljs.getLanguage(name) !== undefined;\n}\n"]}
1
+ {"version":3,"file":"syntax-highlight.js","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference path=\"./highlight-js.d.ts\" />\nimport hljs from \"highlight.js/lib/core.js\";\nimport bash from \"highlight.js/lib/languages/bash.js\";\nimport c from \"highlight.js/lib/languages/c.js\";\nimport cpp from \"highlight.js/lib/languages/cpp.js\";\nimport csharp from \"highlight.js/lib/languages/csharp.js\";\nimport dart from \"highlight.js/lib/languages/dart.js\";\nimport go from \"highlight.js/lib/languages/go.js\";\nimport groovy from \"highlight.js/lib/languages/groovy.js\";\nimport java from \"highlight.js/lib/languages/java.js\";\nimport javascript from \"highlight.js/lib/languages/javascript.js\";\nimport kotlin from \"highlight.js/lib/languages/kotlin.js\";\nimport lua from \"highlight.js/lib/languages/lua.js\";\nimport nix from \"highlight.js/lib/languages/nix.js\";\nimport perl from \"highlight.js/lib/languages/perl.js\";\nimport php from \"highlight.js/lib/languages/php.js\";\nimport python from \"highlight.js/lib/languages/python.js\";\nimport ruby from \"highlight.js/lib/languages/ruby.js\";\nimport rust from \"highlight.js/lib/languages/rust.js\";\nimport scala from \"highlight.js/lib/languages/scala.js\";\nimport swift from \"highlight.js/lib/languages/swift.js\";\nimport typescript from \"highlight.js/lib/languages/typescript.js\";\nimport { decodeHtmlEntityAt } from \"./html.ts\";\n\nconst eagerLanguages = {\n\tpython,\n\tjava,\n\tgo,\n\tjavascript,\n\tcpp,\n\ttypescript,\n\tphp,\n\truby,\n\tc,\n\tcsharp,\n\tnix,\n\tbash,\n\trust,\n\tscala,\n\tkotlin,\n\tswift,\n\tdart,\n\tgroovy,\n\tperl,\n\tlua,\n};\n\nfor (const [name, language] of Object.entries(eagerLanguages)) {\n\thljs.registerLanguage(name, language);\n}\n\nlet allLanguagesPromise: Promise<void> | undefined;\n\nexport function loadAllHighlightLanguages(): Promise<void> {\n\tif (!allLanguagesPromise) {\n\t\tallLanguagesPromise = new Promise((resolve) => {\n\t\t\tsetImmediate(() => {\n\t\t\t\tvoid import(\"highlight.js/lib/index.js\").then(\n\t\t\t\t\t() => resolve(),\n\t\t\t\t\t() => {\n\t\t\t\t\t\t// Eager languages and plaintext fallback remain available.\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t}\n\treturn allLanguagesPromise;\n}\n\nexport type HighlightFormatter = (text: string) => string;\nexport type HighlightTheme = Partial<Record<string, HighlightFormatter>>;\n\nexport interface HighlightOptions {\n\tlanguage?: string;\n\tignoreIllegals?: boolean;\n\tlanguageSubset?: string[];\n\ttheme?: HighlightTheme;\n}\n\nconst SPAN_CLOSE = \"</span>\";\nconst HIGHLIGHT_CLASS_PREFIX = \"hljs-\";\n\nfunction getScopeFromSpanTag(tag: string): string | undefined {\n\tconst match = /\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/.exec(tag);\n\tconst classValue = match?.[1] ?? match?.[2];\n\tif (!classValue) {\n\t\treturn undefined;\n\t}\n\n\tfor (const className of classValue.split(/\\s+/)) {\n\t\tif (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {\n\t\t\treturn className.slice(HIGHLIGHT_CLASS_PREFIX.length);\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getScopeFormatter(scope: string, theme: HighlightTheme): HighlightFormatter | undefined {\n\tconst exact = theme[scope];\n\tif (exact) {\n\t\treturn exact;\n\t}\n\n\tconst dotIndex = scope.indexOf(\".\");\n\tif (dotIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dotIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\tconst dashIndex = scope.indexOf(\"-\");\n\tif (dashIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dashIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getActiveFormatter(scopes: Array<string | undefined>, theme: HighlightTheme): HighlightFormatter | undefined {\n\tfor (let i = scopes.length - 1; i >= 0; i--) {\n\t\tconst scope = scopes[i];\n\t\tif (!scope) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst formatter = getScopeFormatter(scope, theme);\n\t\tif (formatter) {\n\t\t\treturn formatter;\n\t\t}\n\t}\n\treturn theme.default;\n}\n\nfunction isSpanOpenTagStart(html: string, index: number): boolean {\n\tif (!html.startsWith(\"<span\", index)) {\n\t\treturn false;\n\t}\n\tconst nextChar = html[index + \"<span\".length];\n\treturn nextChar === \">\" || nextChar === \" \" || nextChar === \"\\t\" || nextChar === \"\\n\" || nextChar === \"\\r\";\n}\n\nexport function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): string {\n\tlet output = \"\";\n\tlet textBuffer = \"\";\n\tconst scopes: Array<string | undefined> = [];\n\n\tconst flushText = () => {\n\t\tif (!textBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tconst formatter = getActiveFormatter(scopes, theme);\n\t\toutput += formatter ? formatter(textBuffer) : textBuffer;\n\t\ttextBuffer = \"\";\n\t};\n\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tif (isSpanOpenTagStart(html, index)) {\n\t\t\tconst tagEndIndex = html.indexOf(\">\", index + 5);\n\t\t\tif (tagEndIndex !== -1) {\n\t\t\t\tflushText();\n\t\t\t\tconst tag = html.slice(index, tagEndIndex + 1);\n\t\t\t\tconst scope = getScopeFromSpanTag(tag);\n\t\t\t\tscopes.push(scope);\n\t\t\t\tindex = tagEndIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (html.startsWith(SPAN_CLOSE, index)) {\n\t\t\tflushText();\n\t\t\tif (scopes.length > 0) {\n\t\t\t\tscopes.pop();\n\t\t\t}\n\t\t\tindex += SPAN_CLOSE.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (html[index] === \"&\") {\n\t\t\tconst decoded = decodeHtmlEntityAt(html, index);\n\t\t\tif (decoded) {\n\t\t\t\ttextBuffer += decoded.text;\n\t\t\t\tindex += decoded.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\ttextBuffer += html[index];\n\t\tindex++;\n\t}\n\n\tflushText();\n\treturn output;\n}\n\nexport function highlight(code: string, options: HighlightOptions = {}): string {\n\tconst html = options.language\n\t\t? hljs.highlight(code, {\n\t\t\t\tlanguage: options.language,\n\t\t\t\tignoreIllegals: options.ignoreIllegals,\n\t\t\t}).value\n\t\t: hljs.highlightAuto(code, options.languageSubset).value;\n\treturn renderHighlightedHtml(html, options.theme);\n}\n\nexport function supportsLanguage(name: string): boolean {\n\treturn hljs.getLanguage(name) !== undefined;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /** Split a leading UTF-8 byte order mark from decoded text. */
2
+ export declare function splitBom(content: string): {
3
+ bom: string;
4
+ text: string;
5
+ };
6
+ /** Remove a leading UTF-8 byte order mark from decoded text. */
7
+ export declare function stripBom(content: string): string;
8
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAEvE;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,9 @@
1
+ /** Split a leading UTF-8 byte order mark from decoded text. */
2
+ export function splitBom(content) {
3
+ return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
4
+ }
5
+ /** Remove a leading UTF-8 byte order mark from decoded text. */
6
+ export function stripBom(content) {
7
+ return splitBom(content).text;
8
+ }
9
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,MAAM,UAAU,QAAQ,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC9G,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,OAAe;IACvC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;AAC/B,CAAC","sourcesContent":["/** Split a leading UTF-8 byte order mark from decoded text. */\nexport function splitBom(content: string): { bom: string; text: string } {\n\treturn content.startsWith(\"\\uFEFF\") ? { bom: \"\\uFEFF\", text: content.slice(1) } : { bom: \"\", text: content };\n}\n\n/** Remove a leading UTF-8 byte order mark from decoded text. */\nexport function stripBom(content: string): string {\n\treturn splitBom(content).text;\n}\n"]}
@@ -21,7 +21,7 @@ description: "What's new in Atomic"
21
21
  - **Constrained tool sampling is public end to end.** Extensions and SDK tools can request strict JSON Schema (`prefer` or `require`) or OpenAI Lark/regex grammars. Atomic preserves exact optional-property identity during public inspection, and typed RPC model listings expose all four enforcement flags through optional `ModelInfo.compat`. Unsupported models never claim required enforcement. See [Extensions](/extensions#constrained-sampling), [Models](/models#constrained-tool-sampling), and [RPC](/rpc#get_available_models).
22
22
  - **Authentication and flagship catalogs expanded.** `/login openrouter` uses provider-owned browser PKCE with optional key minting, `/login kimi-coding` uses device authorization with automatic refresh, and isolated engine-only extension OAuth now supports safe descriptor discovery, correlated UI callbacks/cancellation, transactional engine persistence/refresh/rollback/logout, and no credential or executable-function transport. Intentional Escape/Ctrl+C/native-abort cancellation is quiet; genuine provider, network, token, storage, and refresh failures remain visible. Claude Opus 5 is available through Anthropic and Bedrock, while bearer-only `ANTHROPIC_AUTH_TOKEN` remains selectable across normal and isolated/RPC catalogs without emitting an API key or `x-api-key`. See [Providers](/providers) and [Custom Providers](/custom-provider#oauth-support).
23
23
  - **Catalogs and model configuration stay resilient.** Remote catalogs use ETag revalidation and accept empty `304` responses, newer bundled data beats stale overlays, and a validated persisted llama.cpp catalog remains selectable when the first online refresh after restart fails while Atomic still reports the original error; a later success replaces it. Opening `/model` reloads layered legacy `.pi` then primary `.atomic` configuration. See [Models](/models#catalog-freshness-and-precedence) and [llama.cpp](/llama-cpp).
24
- - **Bash integrations are session-aware and stream precisely.** Commands receive Atomic and exact Pi session/model environment aliases; unsaved sessions omit the file path. Direct RPC bash streams request-correlated stdout/stderr deltas, targeted/legacy cancellation, one terminal response, and exactly-once starting-session persistence across new/switch/import/fork/clone replacement; RPC shutdown drains active owners. See [Environment Variables](/environment-variables#bash-session-environment) and [RPC](/rpc#bash).
24
+ - **Bash integrations are session-aware and stream precisely.** Bash commands receive Atomic and exact Pi session/model environment aliases; unsaved sessions omit the file path. Direct RPC bash streams request-correlated stdout/stderr deltas, targeted/legacy cancellation, one terminal response, and exactly-once starting-session persistence across new/switch/import/fork/clone replacement; RPC shutdown drains active owners. See [Environment Variables](/environment-variables#bash-and-powershell-session-environment) and [RPC](/rpc#bash).
25
25
  - **Coding-agent parity fixes landed.** Custom message renderers receive `outputPad` in normal and isolated rendering; summaries use fresh routing IDs without prompt-cache writes; complete literal IDs win before thinking-suffix/glob parsing; unavailable scoped entries remain removable; shared async Ctrl+G editing uses a private exact `prompt.md`, configured/environment/platform precedence, and recursive cleanup across main and extension editors; `/copy` falls back after `wl-copy` failure; explicit update checks bypass startup-skip settings; configured agent directories own TUI logs; and context discovery ignores directories named like context files.
26
26
 
27
27
  </Update>
@@ -109,7 +109,7 @@ Configure defaults in `~/.atomic/agent/settings.json` or `.atomic/settings.json`
109
109
  ## When compaction runs
110
110
 
111
111
  - **Manual:** `/compact`, `ctx.compact()`, `session.compact()`, or RPC `{ "type": "compact" }`.
112
- - **Threshold:** automatic compaction starts when estimated context usage exceeds the effective input budget minus `reserveTokens`. Atomic checks both completed responses and the prospective next-turn context after tool results have been appended. A post-tool crossing is compacted before the active Pi tool loop sends its follow-up provider request.
112
+ - **Threshold:** automatic compaction starts when estimated context usage exceeds the effective input budget minus `reserveTokens`. Atomic checks both completed responses and the prospective next-turn context after tool results have been appended. When a provider reports all-zero usage, Atomic estimates the visible message size instead of skipping the check. A post-tool crossing is compacted before the active Pi tool loop sends its follow-up provider request.
113
113
  - **Overflow:** an actual provider context overflow compacts and then retries the interrupted turn.
114
114
  - **Truncated response:** a `length` stop before the original requested output cap gets one compact-and-retry attempt, independent of reported context-window metadata. If no compactable region exists, Atomic makes at most one direct continuation under that same recovery budget; a later actual context overflow remains eligible for load-bearing recovery. A response that reached the cap keeps Atomic's bounded direct-continuation behavior.
115
115
 
@@ -293,6 +293,19 @@ pi.on("session_compact", async (event) => {
293
293
 
294
294
  Observer errors are isolated and cannot roll back the already-persisted boundary.
295
295
 
296
+ ### `session_compact_failed`
297
+
298
+ A failed or cancelled manual, threshold, or overflow compaction emits an observe-only failure event:
299
+
300
+ ```typescript
301
+ pi.on("session_compact_failed", async (event) => {
302
+ console.log(event.reason, event.errorMessage);
303
+ console.log(event.aborted, event.willRetry, event.fromExtension);
304
+ });
305
+ ```
306
+
307
+ `errorMessage` is absent for cancellation. `fromExtension` identifies failures after a `session_before_compact` handler supplied replacement text; no compaction boundary is persisted.
308
+
296
309
  ## Branch Summarization
297
310
 
298
311
  ### When It Triggers
@@ -306,7 +319,7 @@ Branch summarization is a separate mechanism from context compaction. It generat
306
319
  1. **Find common ancestor**: Deepest node shared by old and new positions
307
320
  2. **Collect entries**: Walk from old leaf back to common ancestor
308
321
  3. **Prepare with budget**: Include messages up to token budget (newest first)
309
- 4. **Generate summary**: Call LLM with structured format
322
+ 4. **Generate summary**: Call the LLM with tools disabled; reject tool calls and token-cap-truncated prose
310
323
  5. **Append entry**: Save `BranchSummaryEntry` at navigation point
311
324
 
312
325
  ```mermaid
@@ -467,7 +480,7 @@ See `SessionBeforeTreeEvent` and `TreePreparation` in the types file.
467
480
 
468
481
  ## Summary request isolation
469
482
 
470
- Verbatim planning and branch summarization are standalone provider requests. Each receives a fresh routing session ID instead of reusing the chat's provider-affinity ID, and sets cache retention to `none` so it cannot write summary/planner prompts into the main prompt cache. Neither sends a `max_tokens` of its own. Existing API-key, header-only `ANTHROPIC_AUTH_TOKEN`, custom-header, abort, and bounded retry behavior still applies. These controls affect provider request routing/cache writes only; successful results are persisted through the normal Atomic session lifecycle.
483
+ Verbatim planning and branch summarization are standalone provider requests. Each receives a fresh routing session ID instead of reusing the chat's provider-affinity ID, and sets cache retention to `none` so it cannot write summary/planner prompts into the main prompt cache. All three explicitly set `toolChoice: "none"` and reject returned tool calls. Prose branch/session summaries reject `length` stops rather than persisting partial text; the deletion planner alone may recover complete records from a truncated response, and every recovered range still passes the normal validation airlock. Neither sends a `max_tokens` of its own. Existing API-key, header-only `ANTHROPIC_AUTH_TOKEN`, custom-header, abort, and bounded retry behavior still applies. These controls affect provider request routing/cache writes only; successful results are persisted through the normal Atomic session lifecycle.
471
484
 
472
485
  **Isolation is per model, not per session.** When compaction borrows a fallback model, that request is built with the borrowed candidate's own API key, headers, and base URL; the session model's credentials are never sent to another provider. The corollary is a real data-flow change: **a model listed in `settings.fallbackModels` may receive the compaction transcript.** The list is user-authored, so the set of providers that can see it is yours to control — remove an entry if you do not want it to see transcript content.
473
486
 
@@ -60,6 +60,23 @@ WORKDIR /workspace
60
60
  ENTRYPOINT ["atomic"]
61
61
  ```
62
62
 
63
+ For an image without Node.js or npm, use the release-archive installer instead:
64
+
65
+ ```dockerfile
66
+ FROM debian:bookworm-slim
67
+
68
+ RUN apt-get update \
69
+ && apt-get install -y --no-install-recommends bash ca-certificates curl git ripgrep tar \
70
+ && rm -rf /var/lib/apt/lists/*
71
+ RUN curl -fsSL https://raw.githubusercontent.com/bastani-inc/atomic/main/install.sh | sh
72
+
73
+ ENV PATH="/root/.local/bin:${PATH}"
74
+ WORKDIR /workspace
75
+ ENTRYPOINT ["atomic"]
76
+ ```
77
+
78
+ The archive path installs the full prebuilt payload and needs no JavaScript runtime or package manager. The other packages in this example support Atomic's shell and common coding tasks.
79
+
63
80
  Build and run:
64
81
 
65
82
  ```bash
@@ -373,7 +373,7 @@ interface OAuthCredentials {
373
373
 
374
374
  ## Custom Streaming API
375
375
 
376
- For providers with non-standard APIs, implement `streamSimple`. Study the existing streaming implementations before writing your own:
376
+ For providers with non-standard APIs, implement `streamSimple`. Study the existing API implementations before writing your own:
377
377
 
378
378
  **Reference implementations:**
379
379
 
@@ -27,9 +27,9 @@ Atomic accepts environment variables for configuration, provider credentials, an
27
27
 
28
28
  Provider keys include `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `AZURE_OPENAI_API_KEY`, `GEMINI_API_KEY`, AWS/Bedrock credentials, and the variables listed in [Providers](/providers#environment-variables-or-auth-file). `ANTHROPIC_AUTH_TOKEN` is a distinct header-only bearer credential for Anthropic-compatible gateways: Atomic sends `Authorization: Bearer …` without requiring or inventing an API key, including normal turns, isolated execution, branch summaries, and Verbatim Compaction. Custom headers remain independent.
29
29
 
30
- ## Bash session environment
30
+ ## Bash and PowerShell session environment
31
31
 
32
- Every built-in, factory-created, direct, workflow-stage, and isolated bash execution receives one execution-time snapshot:
32
+ Every built-in, factory-created, direct, workflow-stage, and isolated bash or PowerShell execution receives one execution-time snapshot:
33
33
 
34
34
  | Atomic variable | Exact Pi alias | Value |
35
35
  |---|---|---|
@@ -39,6 +39,6 @@ Every built-in, factory-created, direct, workflow-stage, and isolated bash execu
39
39
  | `ATOMIC_MODEL` | `PI_MODEL` | Active model ID; omitted when no model is selected |
40
40
  | `ATOMIC_REASONING_LEVEL` | `PI_REASONING_LEVEL` | Active reasoning level |
41
41
 
42
- Atomic clears these ten reserved names before overlaying the current snapshot, preventing stale metadata from another session or workflow stage. Unrelated inherited/caller variables remain intact. The snapshot is taken when execution begins, so a resumed session or later model change is reflected. SDK `createBashTool()` exposes it by default; set `exposeSessionEnvironment: false` to opt out.
42
+ Atomic clears these ten reserved names before overlaying the current snapshot, preventing stale metadata from another session or workflow stage. Unrelated inherited/caller variables remain intact. The snapshot is taken when execution begins, so a resumed session or later model change is reflected. SDK `createBashTool()` and `createPowerShellTool()` expose it by default; set `exposeSessionEnvironment: false` to opt out.
43
43
 
44
44
  See [Using Atomic](/usage#environment-variables) and [RPC direct bash](/rpc#bash) for execution and streaming behavior.
@@ -394,7 +394,8 @@ user sends another prompt ◄─────────────────
394
394
  /compact or auto-compaction
395
395
  ├─► compaction_start / compaction_end (verbatim line-compaction status)
396
396
  ├─► session_before_compact (can cancel or provide compactedText)
397
- └─► session_compact (after the compaction boundary is persisted)
397
+ ├─► session_compact (after the compaction boundary is persisted)
398
+ └─► session_compact_failed (failure or cancellation)
398
399
 
399
400
  /tree navigation
400
401
  ├─► session_before_tree (can cancel or customize)
@@ -512,7 +513,7 @@ pi.on("session_before_fork", async (event, ctx) => {
512
513
  After a successful fork or clone, Atomic emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
513
514
  Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
514
515
 
515
- #### session_before_compact / session_compact
516
+ #### session_before_compact / session_compact / session_compact_failed
516
517
 
517
518
  Fired by `/compact` and auto-compaction, including a threshold crossing detected after tool results enter the prospective next-turn context. Atomic prepares the complete active transcript except for the exact newest `preserve_recent` context-visible messages. Extensions may cancel or provide a complete, non-empty `compactedText` replacement for that region; they cannot move `firstKeptEntryId`. The override is persisted verbatim and works without provider credentials. A successful post-tool compaction returns its rebuilt context directly to the already-active Pi loop; it does not start a separate continuation. Cancellation or failure prevents that loop's follow-up provider request.
518
519
 
@@ -545,6 +546,13 @@ pi.on("session_compact", async (event) => {
545
546
  // event.fromExtension - true when session_before_compact provided compactedText
546
547
  // Observe-only: errors are isolated after persistence.
547
548
  });
549
+
550
+ pi.on("session_compact_failed", async (event) => {
551
+ // event.reason - "manual" | "threshold" | "overflow"
552
+ // event.errorMessage - absent for cancellation
553
+ // event.aborted / event.willRetry - terminal state
554
+ // event.fromExtension - whether extension-provided text was active
555
+ });
548
556
  ```
549
557
 
550
558
  #### session_before_tree / session_tree
@@ -820,7 +828,7 @@ Behavior guarantees:
820
828
  import { isToolCallEventType } from "@bastani/atomic";
821
829
 
822
830
  pi.on("tool_call", async (event, ctx) => {
823
- // event.toolName - "bash", "read", "write", "edit", "find", "search", etc.
831
+ // event.toolName - "bash", "powershell", "read", "write", "edit", "find", "search", etc.
824
832
  // event.toolCallId
825
833
  // event.input - tool parameters (mutable)
826
834
 
@@ -834,6 +842,11 @@ pi.on("tool_call", async (event, ctx) => {
834
842
  }
835
843
  }
836
844
 
845
+ if (isToolCallEventType("powershell", event)) {
846
+ // event.input is typed as PowerShellToolInput
847
+ event.input.command = `$ErrorActionPreference = "Stop"\n${event.input.command}`;
848
+ }
849
+
837
850
  if (isToolCallEventType("read", event)) {
838
851
  // event.input is { path: string }
839
852
  console.log(`Reading: ${event.input.path}`);
@@ -884,7 +897,7 @@ After all handlers finish, Atomic normalizes image blocks returned by the tool o
884
897
  Use `ctx.signal` for nested async work inside the handler. This lets Escape cancel model calls, `fetch()`, and other abort-aware operations started by the extension.
885
898
 
886
899
  ```typescript
887
- import { isBashToolResult, isSearchToolResult } from "@bastani/atomic";
900
+ import { isBashToolResult, isPowerShellToolResult, isSearchToolResult } from "@bastani/atomic";
888
901
 
889
902
  pi.on("tool_result", async (event, ctx) => {
890
903
  // event.toolName, event.toolCallId, event.input
@@ -894,6 +907,10 @@ pi.on("tool_result", async (event, ctx) => {
894
907
  // event.details is typed as BashToolDetails
895
908
  }
896
909
 
910
+ if (isPowerShellToolResult(event)) {
911
+ // event.details is typed as PowerShellToolDetails | undefined
912
+ }
913
+
897
914
  if (isSearchToolResult(event)) {
898
915
  // event.details is typed as SearchToolDetails | undefined
899
916
  }
@@ -2222,7 +2239,7 @@ pi.registerTool({
2222
2239
 
2223
2240
  ### Overriding Built-in Tools
2224
2241
 
2225
- Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `find`, `search`, `ask_user_question`, `todo`) by registering a tool with the same name. Interactive mode displays a warning when this happens.
2242
+ Extensions can override built-in tools (`read`, `bash`, `powershell`, `edit`, `write`, `find`, `search`, `ask_user_question`, `todo`) by registering a tool with the same name. Interactive mode displays a warning when this happens.
2226
2243
 
2227
2244
  ```bash
2228
2245
  # Extension's read tool replaces built-in read