@draht/coding-agent 2026.3.14 → 2026.3.25-1

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 (345) hide show
  1. package/README.md +45 -30
  2. package/dist/bun/cli.d.ts +3 -0
  3. package/dist/bun/cli.d.ts.map +1 -0
  4. package/dist/bun/cli.js +7 -0
  5. package/dist/bun/cli.js.map +1 -0
  6. package/dist/bun/register-bedrock.d.ts +2 -0
  7. package/dist/bun/register-bedrock.d.ts.map +1 -0
  8. package/dist/bun/register-bedrock.js +4 -0
  9. package/dist/bun/register-bedrock.js.map +1 -0
  10. package/dist/cli/args.d.ts +1 -0
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +11 -6
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/file-processor.d.ts.map +1 -1
  15. package/dist/cli/file-processor.js +4 -0
  16. package/dist/cli/file-processor.js.map +1 -1
  17. package/dist/cli/initial-message.d.ts +18 -0
  18. package/dist/cli/initial-message.d.ts.map +1 -0
  19. package/dist/cli/initial-message.js +22 -0
  20. package/dist/cli/initial-message.js.map +1 -0
  21. package/dist/cli/session-picker.d.ts.map +1 -1
  22. package/dist/cli/session-picker.js +2 -1
  23. package/dist/cli/session-picker.js.map +1 -1
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +1 -3
  26. package/dist/cli.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +38 -5
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +201 -73
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts +6 -7
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +8 -107
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  36. package/dist/core/compaction/branch-summarization.js +1 -0
  37. package/dist/core/compaction/branch-summarization.js.map +1 -1
  38. package/dist/core/compaction/compaction.d.ts.map +1 -1
  39. package/dist/core/compaction/compaction.js +2 -0
  40. package/dist/core/compaction/compaction.js.map +1 -1
  41. package/dist/core/exec.d.ts.map +1 -1
  42. package/dist/core/exec.js +7 -3
  43. package/dist/core/exec.js.map +1 -1
  44. package/dist/core/export-html/index.d.ts +2 -2
  45. package/dist/core/export-html/index.d.ts.map +1 -1
  46. package/dist/core/export-html/index.js +7 -6
  47. package/dist/core/export-html/index.js.map +1 -1
  48. package/dist/core/export-html/template.css +43 -13
  49. package/dist/core/export-html/template.html +1 -0
  50. package/dist/core/export-html/template.js +107 -0
  51. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  52. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  53. package/dist/core/export-html/tool-renderer.js +41 -16
  54. package/dist/core/export-html/tool-renderer.js.map +1 -1
  55. package/dist/core/extensions/index.d.ts +4 -3
  56. package/dist/core/extensions/index.d.ts.map +1 -1
  57. package/dist/core/extensions/index.js +1 -1
  58. package/dist/core/extensions/index.js.map +1 -1
  59. package/dist/core/extensions/loader.d.ts.map +1 -1
  60. package/dist/core/extensions/loader.js +16 -6
  61. package/dist/core/extensions/loader.js.map +1 -1
  62. package/dist/core/extensions/runner.d.ts +9 -9
  63. package/dist/core/extensions/runner.d.ts.map +1 -1
  64. package/dist/core/extensions/runner.js +89 -71
  65. package/dist/core/extensions/runner.js.map +1 -1
  66. package/dist/core/extensions/types.d.ts +49 -13
  67. package/dist/core/extensions/types.d.ts.map +1 -1
  68. package/dist/core/extensions/types.js.map +1 -1
  69. package/dist/core/extensions/wrapper.d.ts +4 -11
  70. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  71. package/dist/core/extensions/wrapper.js +6 -86
  72. package/dist/core/extensions/wrapper.js.map +1 -1
  73. package/dist/core/footer-data-provider.d.ts +13 -1
  74. package/dist/core/footer-data-provider.d.ts.map +1 -1
  75. package/dist/core/footer-data-provider.js +155 -37
  76. package/dist/core/footer-data-provider.js.map +1 -1
  77. package/dist/core/index.d.ts +2 -1
  78. package/dist/core/index.d.ts.map +1 -1
  79. package/dist/core/index.js +2 -1
  80. package/dist/core/index.js.map +1 -1
  81. package/dist/core/keybindings.d.ts +270 -50
  82. package/dist/core/keybindings.d.ts.map +1 -1
  83. package/dist/core/keybindings.js +222 -134
  84. package/dist/core/keybindings.js.map +1 -1
  85. package/dist/core/model-registry.d.ts +1 -0
  86. package/dist/core/model-registry.d.ts.map +1 -1
  87. package/dist/core/model-registry.js +49 -23
  88. package/dist/core/model-registry.js.map +1 -1
  89. package/dist/core/model-resolver.d.ts +6 -0
  90. package/dist/core/model-resolver.d.ts.map +1 -1
  91. package/dist/core/model-resolver.js +41 -17
  92. package/dist/core/model-resolver.js.map +1 -1
  93. package/dist/core/output-guard.d.ts +6 -0
  94. package/dist/core/output-guard.d.ts.map +1 -0
  95. package/dist/core/output-guard.js +59 -0
  96. package/dist/core/output-guard.js.map +1 -0
  97. package/dist/core/package-manager.d.ts +22 -1
  98. package/dist/core/package-manager.d.ts.map +1 -1
  99. package/dist/core/package-manager.js +373 -53
  100. package/dist/core/package-manager.js.map +1 -1
  101. package/dist/core/prompt-templates.d.ts +2 -1
  102. package/dist/core/prompt-templates.d.ts.map +1 -1
  103. package/dist/core/prompt-templates.js +39 -39
  104. package/dist/core/prompt-templates.js.map +1 -1
  105. package/dist/core/resolve-config-value.d.ts.map +1 -1
  106. package/dist/core/resolve-config-value.js +43 -8
  107. package/dist/core/resolve-config-value.js.map +1 -1
  108. package/dist/core/resource-loader.d.ts +6 -7
  109. package/dist/core/resource-loader.d.ts.map +1 -1
  110. package/dist/core/resource-loader.js +141 -118
  111. package/dist/core/resource-loader.js.map +1 -1
  112. package/dist/core/sdk.d.ts +3 -3
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +4 -4
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager.d.ts +6 -0
  117. package/dist/core/session-manager.d.ts.map +1 -1
  118. package/dist/core/session-manager.js +9 -10
  119. package/dist/core/session-manager.js.map +1 -1
  120. package/dist/core/settings-manager.d.ts +3 -0
  121. package/dist/core/settings-manager.d.ts.map +1 -1
  122. package/dist/core/settings-manager.js +8 -0
  123. package/dist/core/settings-manager.js.map +1 -1
  124. package/dist/core/skills.d.ts +5 -3
  125. package/dist/core/skills.d.ts.map +1 -1
  126. package/dist/core/skills.js +54 -9
  127. package/dist/core/skills.js.map +1 -1
  128. package/dist/core/slash-commands.d.ts +2 -3
  129. package/dist/core/slash-commands.d.ts.map +1 -1
  130. package/dist/core/slash-commands.js +3 -2
  131. package/dist/core/slash-commands.js.map +1 -1
  132. package/dist/core/source-info.d.ts +18 -0
  133. package/dist/core/source-info.d.ts.map +1 -0
  134. package/dist/core/source-info.js +19 -0
  135. package/dist/core/source-info.js.map +1 -0
  136. package/dist/core/system-prompt.d.ts.map +1 -1
  137. package/dist/core/system-prompt.js +17 -60
  138. package/dist/core/system-prompt.js.map +1 -1
  139. package/dist/core/tools/bash.d.ts +24 -6
  140. package/dist/core/tools/bash.d.ts.map +1 -1
  141. package/dist/core/tools/bash.js +210 -110
  142. package/dist/core/tools/bash.js.map +1 -1
  143. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  144. package/dist/core/tools/edit-diff.js +1 -0
  145. package/dist/core/tools/edit-diff.js.map +1 -1
  146. package/dist/core/tools/edit.d.ts +14 -2
  147. package/dist/core/tools/edit.d.ts.map +1 -1
  148. package/dist/core/tools/edit.js +95 -23
  149. package/dist/core/tools/edit.js.map +1 -1
  150. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  151. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  152. package/dist/core/tools/file-mutation-queue.js +37 -0
  153. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  154. package/dist/core/tools/find.d.ts +11 -4
  155. package/dist/core/tools/find.d.ts.map +1 -1
  156. package/dist/core/tools/find.js +82 -30
  157. package/dist/core/tools/find.js.map +1 -1
  158. package/dist/core/tools/grep.d.ts +15 -4
  159. package/dist/core/tools/grep.d.ts.map +1 -1
  160. package/dist/core/tools/grep.js +83 -29
  161. package/dist/core/tools/grep.js.map +1 -1
  162. package/dist/core/tools/index.d.ts +58 -19
  163. package/dist/core/tools/index.d.ts.map +1 -1
  164. package/dist/core/tools/index.js +51 -26
  165. package/dist/core/tools/index.js.map +1 -1
  166. package/dist/core/tools/ls.d.ts +9 -3
  167. package/dist/core/tools/ls.d.ts.map +1 -1
  168. package/dist/core/tools/ls.js +67 -13
  169. package/dist/core/tools/ls.js.map +1 -1
  170. package/dist/core/tools/read.d.ts +10 -3
  171. package/dist/core/tools/read.d.ts.map +1 -1
  172. package/dist/core/tools/read.js +110 -51
  173. package/dist/core/tools/read.js.map +1 -1
  174. package/dist/core/tools/render-utils.d.ts +21 -0
  175. package/dist/core/tools/render-utils.d.ts.map +1 -0
  176. package/dist/core/tools/render-utils.js +49 -0
  177. package/dist/core/tools/render-utils.js.map +1 -0
  178. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  179. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  180. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  181. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  182. package/dist/core/tools/write.d.ts +9 -3
  183. package/dist/core/tools/write.d.ts.map +1 -1
  184. package/dist/core/tools/write.js +168 -30
  185. package/dist/core/tools/write.js.map +1 -1
  186. package/dist/index.d.ts +5 -4
  187. package/dist/index.d.ts.map +1 -1
  188. package/dist/index.js +4 -3
  189. package/dist/index.js.map +1 -1
  190. package/dist/main.d.ts.map +1 -1
  191. package/dist/main.js +105 -226
  192. package/dist/main.js.map +1 -1
  193. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  194. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/bash-execution.js +22 -9
  196. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  197. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  199. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  200. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/branch-summary-message.js +2 -2
  202. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  203. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
  205. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  206. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/config-selector.js +8 -8
  208. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  209. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  210. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  211. package/dist/modes/interactive/components/custom-editor.js +6 -6
  212. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  213. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  214. package/dist/modes/interactive/components/extension-editor.js +9 -9
  215. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  216. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/extension-input.js +5 -5
  218. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  219. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  220. package/dist/modes/interactive/components/extension-selector.js +8 -8
  221. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  222. package/dist/modes/interactive/components/index.d.ts +1 -1
  223. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/index.js +1 -1
  225. package/dist/modes/interactive/components/index.js.map +1 -1
  226. package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
  227. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/keybinding-hints.js +5 -44
  229. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  230. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/login-dialog.js +6 -6
  232. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  233. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/model-selector.js +13 -9
  235. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  236. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  238. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  239. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  240. package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
  241. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  242. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/session-selector.js +32 -35
  244. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  245. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  246. package/dist/modes/interactive/components/settings-selector.js +5 -1
  247. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  248. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/show-images-selector.js +5 -1
  250. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  253. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  254. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  255. package/dist/modes/interactive/components/theme-selector.js +5 -1
  256. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  257. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  258. package/dist/modes/interactive/components/thinking-selector.js +5 -1
  259. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  260. package/dist/modes/interactive/components/tool-execution.d.ts +16 -34
  261. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/tool-execution.js +128 -636
  263. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  264. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/tree-selector.js +27 -16
  266. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  267. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/user-message-selector.js +6 -6
  269. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  270. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  271. package/dist/modes/interactive/components/user-message.js +2 -1
  272. package/dist/modes/interactive/components/user-message.js.map +1 -1
  273. package/dist/modes/interactive/interactive-mode.d.ts +7 -11
  274. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  275. package/dist/modes/interactive/interactive-mode.js +353 -214
  276. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  277. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  278. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  279. package/dist/modes/interactive/theme/theme.js +63 -37
  280. package/dist/modes/interactive/theme/theme.js.map +1 -1
  281. package/dist/modes/print-mode.d.ts.map +1 -1
  282. package/dist/modes/print-mode.js +5 -11
  283. package/dist/modes/print-mode.js.map +1 -1
  284. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  285. package/dist/modes/rpc/rpc-mode.js +27 -17
  286. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  287. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  288. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  289. package/dist/modes/rpc/rpc-types.js.map +1 -1
  290. package/dist/utils/child-process.d.ts +11 -0
  291. package/dist/utils/child-process.d.ts.map +1 -0
  292. package/dist/utils/child-process.js +78 -0
  293. package/dist/utils/child-process.js.map +1 -0
  294. package/dist/utils/clipboard-image.d.ts.map +1 -1
  295. package/dist/utils/clipboard-image.js +94 -11
  296. package/dist/utils/clipboard-image.js.map +1 -1
  297. package/dist/utils/clipboard-native.d.ts +1 -0
  298. package/dist/utils/clipboard-native.d.ts.map +1 -1
  299. package/dist/utils/clipboard-native.js.map +1 -1
  300. package/dist/utils/clipboard.d.ts +1 -1
  301. package/dist/utils/clipboard.d.ts.map +1 -1
  302. package/dist/utils/clipboard.js +27 -16
  303. package/dist/utils/clipboard.js.map +1 -1
  304. package/dist/utils/exif-orientation.d.ts +5 -0
  305. package/dist/utils/exif-orientation.d.ts.map +1 -0
  306. package/dist/utils/exif-orientation.js +158 -0
  307. package/dist/utils/exif-orientation.js.map +1 -0
  308. package/dist/utils/image-convert.d.ts.map +1 -1
  309. package/dist/utils/image-convert.js +5 -1
  310. package/dist/utils/image-convert.js.map +1 -1
  311. package/dist/utils/image-resize.d.ts +5 -5
  312. package/dist/utils/image-resize.d.ts.map +1 -1
  313. package/dist/utils/image-resize.js +51 -95
  314. package/dist/utils/image-resize.js.map +1 -1
  315. package/dist/utils/tools-manager.d.ts.map +1 -1
  316. package/dist/utils/tools-manager.js +5 -4
  317. package/dist/utils/tools-manager.js.map +1 -1
  318. package/docs/custom-provider.md +6 -2
  319. package/docs/extensions.md +108 -21
  320. package/docs/keybindings.md +103 -112
  321. package/docs/models.md +39 -1
  322. package/docs/packages.md +9 -0
  323. package/docs/providers.md +7 -0
  324. package/docs/rpc.md +15 -6
  325. package/docs/sdk.md +2 -2
  326. package/docs/settings.md +9 -0
  327. package/docs/terminal-setup.md +11 -0
  328. package/docs/tui.md +2 -2
  329. package/examples/extensions/README.md +2 -2
  330. package/examples/extensions/antigravity-image-gen.ts +5 -3
  331. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  332. package/examples/extensions/commands.ts +3 -3
  333. package/examples/extensions/minimal-mode.ts +14 -14
  334. package/examples/extensions/question.ts +2 -2
  335. package/examples/extensions/questionnaire.ts +2 -2
  336. package/examples/extensions/subagent/index.ts +30 -8
  337. package/examples/extensions/titlebar-spinner.ts +2 -2
  338. package/examples/extensions/todo.ts +2 -2
  339. package/examples/extensions/tool-override.ts +9 -7
  340. package/examples/extensions/truncated-tool.ts +8 -5
  341. package/examples/sdk/04-skills.ts +8 -2
  342. package/examples/sdk/08-prompt-templates.ts +8 -2
  343. package/examples/sdk/12-full-control.ts +0 -1
  344. package/examples/sdk/README.md +1 -1
  345. package/package.json +4 -4
@@ -12,8 +12,8 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { readFileSync } from "node:fs";
16
- import { basename, dirname, join } from "node:path";
15
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
+ import { basename, dirname, join, resolve } from "node:path";
17
17
  import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@draht/ai";
18
18
  import { getDocsPath } from "../config.js";
19
19
  import { theme } from "../modes/interactive/theme/theme.js";
@@ -24,12 +24,13 @@ import { calculateContextTokens, collectEntriesForBranchSummary, compact, estima
24
24
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
25
25
  import { exportSessionToHtml } from "./export-html/index.js";
26
26
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
27
- import { ExtensionRunner, wrapRegisteredTools, wrapToolsWithExtensions, } from "./extensions/index.js";
27
+ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
28
28
  import { expandPromptTemplate } from "./prompt-templates.js";
29
- import { getLatestCompactionEntry } from "./session-manager.js";
30
- import { BUILTIN_SLASH_COMMANDS } from "./slash-commands.js";
29
+ import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
30
+ import { createSyntheticSourceInfo } from "./source-info.js";
31
31
  import { buildSystemPrompt } from "./system-prompt.js";
32
- import { createAllTools } from "./tools/index.js";
32
+ import { createAllToolDefinitions } from "./tools/index.js";
33
+ import { createToolDefinitionFromAgentTool, wrapToolDefinition } from "./tools/tool-definition-wrapper.js";
33
34
  /**
34
35
  * Parse a skill block from message text.
35
36
  * Returns null if the text doesn't contain a skill block.
@@ -89,7 +90,7 @@ export class AgentSession {
89
90
  _turnIndex = 0;
90
91
  _resourceLoader;
91
92
  _customTools;
92
- _baseToolRegistry = new Map();
93
+ _baseToolDefinitions = new Map();
93
94
  _cwd;
94
95
  _extensionRunnerRef;
95
96
  _initialActiveToolNames;
@@ -103,6 +104,7 @@ export class AgentSession {
103
104
  _modelRegistry;
104
105
  // Tool registry for extension getTools/setTools
105
106
  _toolRegistry = new Map();
107
+ _toolDefinitions = new Map();
106
108
  _toolPromptSnippets = new Map();
107
109
  _toolPromptGuidelines = new Map();
108
110
  // Base system prompt (without extension appends) - used to apply fresh appends each turn
@@ -122,6 +124,7 @@ export class AgentSession {
122
124
  // Always subscribe to agent events for internal handling
123
125
  // (session persistence, extensions, auto-compaction, retry logic)
124
126
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
127
+ this._installAgentToolHooks();
125
128
  this._buildRuntime({
126
129
  activeToolNames: this._initialActiveToolNames,
127
130
  includeAllExtensionTools: true,
@@ -131,6 +134,59 @@ export class AgentSession {
131
134
  get modelRegistry() {
132
135
  return this._modelRegistry;
133
136
  }
137
+ /**
138
+ * Install tool hooks once on the Agent instance.
139
+ *
140
+ * The callbacks read `this._extensionRunner` at execution time, so extension reload swaps in the
141
+ * new runner without reinstalling hooks. Extension-specific tool wrappers are still used to adapt
142
+ * registered tool execution to the extension context. Tool call and tool result interception now
143
+ * happens here instead of in wrappers.
144
+ */
145
+ _installAgentToolHooks() {
146
+ this.agent.setBeforeToolCall(async ({ toolCall, args }) => {
147
+ const runner = this._extensionRunner;
148
+ if (!runner?.hasHandlers("tool_call")) {
149
+ return undefined;
150
+ }
151
+ await this._agentEventQueue;
152
+ try {
153
+ return await runner.emitToolCall({
154
+ type: "tool_call",
155
+ toolName: toolCall.name,
156
+ toolCallId: toolCall.id,
157
+ input: args,
158
+ });
159
+ }
160
+ catch (err) {
161
+ if (err instanceof Error) {
162
+ throw err;
163
+ }
164
+ throw new Error(`Extension failed, blocking execution: ${String(err)}`);
165
+ }
166
+ });
167
+ this.agent.setAfterToolCall(async ({ toolCall, args, result, isError }) => {
168
+ const runner = this._extensionRunner;
169
+ if (!runner?.hasHandlers("tool_result")) {
170
+ return undefined;
171
+ }
172
+ const hookResult = await runner.emitToolResult({
173
+ type: "tool_result",
174
+ toolName: toolCall.name,
175
+ toolCallId: toolCall.id,
176
+ input: args,
177
+ content: result.content,
178
+ details: isError ? undefined : result.details,
179
+ isError,
180
+ });
181
+ if (!hookResult || isError) {
182
+ return undefined;
183
+ }
184
+ return {
185
+ content: hookResult.content,
186
+ details: hookResult.details,
187
+ };
188
+ });
189
+ }
134
190
  // =========================================================================
135
191
  // Event Subscription
136
192
  // =========================================================================
@@ -234,7 +290,6 @@ export class AgentSession {
234
290
  attempt: this._retryAttempt,
235
291
  });
236
292
  this._retryAttempt = 0;
237
- this._resolveRetry();
238
293
  }
239
294
  }
240
295
  }
@@ -248,6 +303,7 @@ export class AgentSession {
248
303
  if (didRetry)
249
304
  return; // Retry was initiated, don't proceed to compaction
250
305
  }
306
+ this._resolveRetry();
251
307
  await this._checkCompaction(msg);
252
308
  }
253
309
  }
@@ -439,15 +495,19 @@ export class AgentSession {
439
495
  return this.agent.state.tools.map((t) => t.name);
440
496
  }
441
497
  /**
442
- * Get all configured tools with name, description, and parameter schema.
498
+ * Get all configured tools with name, description, parameter schema, and source metadata.
443
499
  */
444
500
  getAllTools() {
445
- return Array.from(this._toolRegistry.values()).map((t) => ({
446
- name: t.name,
447
- description: t.description,
448
- parameters: t.parameters,
501
+ return Array.from(this._toolDefinitions.values()).map(({ definition, sourceInfo }) => ({
502
+ name: definition.name,
503
+ description: definition.description,
504
+ parameters: definition.parameters,
505
+ sourceInfo,
449
506
  }));
450
507
  }
508
+ getToolDefinition(name) {
509
+ return this._toolDefinitions.get(name)?.definition;
510
+ }
451
511
  /**
452
512
  * Set active tools by name.
453
513
  * Only tools in the registry can be enabled. Unknown tool names are ignored.
@@ -748,8 +808,9 @@ export class AgentSession {
748
808
  }
749
809
  }
750
810
  /**
751
- * Queue a steering message to interrupt the agent mid-run.
752
- * Delivered after current tool execution, skips remaining tools.
811
+ * Queue a steering message while the agent is running.
812
+ * Delivered after the current assistant turn finishes executing its tool calls,
813
+ * before the next LLM call.
753
814
  * Expands skill commands and prompt templates. Errors on extension commands.
754
815
  * @param images Optional image attachments to include with the message
755
816
  * @throws Error if text is an extension command
@@ -1371,19 +1432,9 @@ export class AgentSession {
1371
1432
  // This ensures sessions that hit persistent API errors (e.g. 529) can still compact.
1372
1433
  let contextTokens;
1373
1434
  if (assistantMessage.stopReason === "error") {
1374
- const messages = this.agent.state.messages;
1375
- const estimate = estimateContextTokens(messages);
1435
+ const estimate = estimateContextTokens(this.agent.state.messages);
1376
1436
  if (estimate.lastUsageIndex === null)
1377
1437
  return; // No usage data at all
1378
- // Verify the usage source is post-compaction. Kept pre-compaction messages
1379
- // have stale usage reflecting the old (larger) context and would falsely
1380
- // trigger compaction right after one just finished.
1381
- const usageMsg = messages[estimate.lastUsageIndex];
1382
- if (compactionEntry &&
1383
- usageMsg.role === "assistant" &&
1384
- usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
1385
- return;
1386
- }
1387
1438
  contextTokens = estimate.tokens;
1388
1439
  }
1389
1440
  else {
@@ -1589,37 +1640,36 @@ export class AgentSession {
1589
1640
  ? runner.onError(this._extensionErrorListener)
1590
1641
  : undefined;
1591
1642
  }
1643
+ _refreshCurrentModelFromRegistry() {
1644
+ const currentModel = this.model;
1645
+ if (!currentModel) {
1646
+ return;
1647
+ }
1648
+ const refreshedModel = this._modelRegistry.find(currentModel.provider, currentModel.id);
1649
+ if (!refreshedModel || refreshedModel === currentModel) {
1650
+ return;
1651
+ }
1652
+ this.agent.setModel(refreshedModel);
1653
+ }
1592
1654
  _bindExtensionCore(runner) {
1593
- const normalizeLocation = (source) => {
1594
- if (source === "user" || source === "project" || source === "path") {
1595
- return source;
1596
- }
1597
- return undefined;
1598
- };
1599
- const reservedBuiltins = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
1600
1655
  const getCommands = () => {
1601
- const extensionCommands = runner
1602
- .getRegisteredCommandsWithPaths()
1603
- .filter(({ command }) => !reservedBuiltins.has(command.name))
1604
- .map(({ command, extensionPath }) => ({
1605
- name: command.name,
1656
+ const extensionCommands = runner.getRegisteredCommands().map((command) => ({
1657
+ name: command.invocationName,
1606
1658
  description: command.description,
1607
1659
  source: "extension",
1608
- path: extensionPath,
1660
+ sourceInfo: command.sourceInfo,
1609
1661
  }));
1610
1662
  const templates = this.promptTemplates.map((template) => ({
1611
1663
  name: template.name,
1612
1664
  description: template.description,
1613
1665
  source: "prompt",
1614
- location: normalizeLocation(template.source),
1615
- path: template.filePath,
1666
+ sourceInfo: template.sourceInfo,
1616
1667
  }));
1617
1668
  const skills = this._resourceLoader.getSkills().skills.map((skill) => ({
1618
1669
  name: `skill:${skill.name}`,
1619
1670
  description: skill.description,
1620
1671
  source: "skill",
1621
- location: normalizeLocation(skill.source),
1622
- path: skill.filePath,
1672
+ sourceInfo: skill.sourceInfo,
1623
1673
  }));
1624
1674
  return [...extensionCommands, ...templates, ...skills];
1625
1675
  };
@@ -1690,6 +1740,15 @@ export class AgentSession {
1690
1740
  })();
1691
1741
  },
1692
1742
  getSystemPrompt: () => this.systemPrompt,
1743
+ }, {
1744
+ registerProvider: (name, config) => {
1745
+ this._modelRegistry.registerProvider(name, config);
1746
+ this._refreshCurrentModelFromRegistry();
1747
+ },
1748
+ unregisterProvider: (name) => {
1749
+ this._modelRegistry.unregisterProvider(name);
1750
+ this._refreshCurrentModelFromRegistry();
1751
+ },
1693
1752
  });
1694
1753
  }
1695
1754
  _refreshToolRegistry(options) {
@@ -1698,34 +1757,48 @@ export class AgentSession {
1698
1757
  const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
1699
1758
  const allCustomTools = [
1700
1759
  ...registeredTools,
1701
- ...this._customTools.map((def) => ({ definition: def, extensionPath: "<sdk>" })),
1760
+ ...this._customTools.map((definition) => ({
1761
+ definition,
1762
+ sourceInfo: createSyntheticSourceInfo(`<sdk:${definition.name}>`, { source: "sdk" }),
1763
+ })),
1702
1764
  ];
1703
- this._toolPromptSnippets = new Map(allCustomTools
1704
- .map((registeredTool) => {
1705
- const snippet = this._normalizePromptSnippet(registeredTool.definition.promptSnippet ?? registeredTool.definition.description);
1706
- return snippet ? [registeredTool.definition.name, snippet] : undefined;
1765
+ const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries()).map(([name, definition]) => [
1766
+ name,
1767
+ {
1768
+ definition,
1769
+ sourceInfo: createSyntheticSourceInfo(`<builtin:${name}>`, { source: "builtin" }),
1770
+ },
1771
+ ]));
1772
+ for (const tool of allCustomTools) {
1773
+ definitionRegistry.set(tool.definition.name, {
1774
+ definition: tool.definition,
1775
+ sourceInfo: tool.sourceInfo,
1776
+ });
1777
+ }
1778
+ this._toolDefinitions = definitionRegistry;
1779
+ this._toolPromptSnippets = new Map(Array.from(definitionRegistry.values())
1780
+ .map(({ definition }) => {
1781
+ const snippet = this._normalizePromptSnippet(definition.promptSnippet);
1782
+ return snippet ? [definition.name, snippet] : undefined;
1707
1783
  })
1708
1784
  .filter((entry) => entry !== undefined));
1709
- this._toolPromptGuidelines = new Map(allCustomTools
1710
- .map((registeredTool) => {
1711
- const guidelines = this._normalizePromptGuidelines(registeredTool.definition.promptGuidelines);
1712
- return guidelines.length > 0 ? [registeredTool.definition.name, guidelines] : undefined;
1785
+ this._toolPromptGuidelines = new Map(Array.from(definitionRegistry.values())
1786
+ .map(({ definition }) => {
1787
+ const guidelines = this._normalizePromptGuidelines(definition.promptGuidelines);
1788
+ return guidelines.length > 0 ? [definition.name, guidelines] : undefined;
1713
1789
  })
1714
1790
  .filter((entry) => entry !== undefined));
1715
1791
  const wrappedExtensionTools = this._extensionRunner
1716
1792
  ? wrapRegisteredTools(allCustomTools, this._extensionRunner)
1717
1793
  : [];
1718
- const toolRegistry = new Map(this._baseToolRegistry);
1794
+ const toolRegistry = new Map(Array.from(this._baseToolDefinitions.values()).map((definition) => [
1795
+ definition.name,
1796
+ wrapToolDefinition(definition),
1797
+ ]));
1719
1798
  for (const tool of wrappedExtensionTools) {
1720
1799
  toolRegistry.set(tool.name, tool);
1721
1800
  }
1722
- if (this._extensionRunner) {
1723
- const wrappedAllTools = wrapToolsWithExtensions(Array.from(toolRegistry.values()), this._extensionRunner);
1724
- this._toolRegistry = new Map(wrappedAllTools.map((tool) => [tool.name, tool]));
1725
- }
1726
- else {
1727
- this._toolRegistry = toolRegistry;
1728
- }
1801
+ this._toolRegistry = toolRegistry;
1729
1802
  const nextActiveToolNames = options?.activeToolNames
1730
1803
  ? [...options.activeToolNames]
1731
1804
  : [...previousActiveToolNames];
@@ -1746,13 +1819,16 @@ export class AgentSession {
1746
1819
  _buildRuntime(options) {
1747
1820
  const autoResizeImages = this.settingsManager.getImageAutoResize();
1748
1821
  const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
1749
- const baseTools = this._baseToolsOverride
1750
- ? this._baseToolsOverride
1751
- : createAllTools(this._cwd, {
1822
+ const baseToolDefinitions = this._baseToolsOverride
1823
+ ? Object.fromEntries(Object.entries(this._baseToolsOverride).map(([name, tool]) => [
1824
+ name,
1825
+ createToolDefinitionFromAgentTool(tool),
1826
+ ]))
1827
+ : createAllToolDefinitions(this._cwd, {
1752
1828
  read: { autoResizeImages },
1753
1829
  bash: { commandPrefix: shellCommandPrefix },
1754
1830
  });
1755
- this._baseToolRegistry = new Map(Object.entries(baseTools).map(([name, tool]) => [name, tool]));
1831
+ this._baseToolDefinitions = new Map(Object.entries(baseToolDefinitions).map(([name, tool]) => [name, tool]));
1756
1832
  const extensionsResult = this._resourceLoader.getExtensions();
1757
1833
  if (options.flagValues) {
1758
1834
  for (const [name, value] of options.flagValues) {
@@ -1816,8 +1892,8 @@ export class AgentSession {
1816
1892
  if (isContextOverflow(message, contextWindow))
1817
1893
  return false;
1818
1894
  const err = message.errorMessage;
1819
- // Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed, terminated, retry delay exceeded
1820
- return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay/i.test(err);
1895
+ // Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors, fetch failed, terminated, retry delay exceeded
1896
+ return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|timed? out|timeout|terminated|retry delay/i.test(err);
1821
1897
  }
1822
1898
  /**
1823
1899
  * Handle retryable errors with exponential backoff.
@@ -2380,6 +2456,7 @@ export class AgentSession {
2380
2456
  total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
2381
2457
  },
2382
2458
  cost: totalCost,
2459
+ contextUsage: this.getContextUsage(),
2383
2460
  };
2384
2461
  }
2385
2462
  getContextUsage() {
@@ -2431,19 +2508,70 @@ export class AgentSession {
2431
2508
  async exportToHtml(outputPath) {
2432
2509
  const themeName = this.settingsManager.getTheme();
2433
2510
  // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
2434
- let toolRenderer;
2435
- if (this._extensionRunner) {
2436
- toolRenderer = createToolHtmlRenderer({
2437
- getToolDefinition: (name) => this._extensionRunner.getToolDefinition(name),
2438
- theme,
2439
- });
2440
- }
2511
+ const toolRenderer = createToolHtmlRenderer({
2512
+ getToolDefinition: (name) => this.getToolDefinition(name),
2513
+ theme,
2514
+ });
2441
2515
  return await exportSessionToHtml(this.sessionManager, this.state, {
2442
2516
  outputPath,
2443
2517
  themeName,
2444
2518
  toolRenderer,
2445
2519
  });
2446
2520
  }
2521
+ /**
2522
+ * Export the current session branch to a JSONL file.
2523
+ * Writes the session header followed by all entries on the current branch path.
2524
+ * @param outputPath Target file path. If omitted, generates a timestamped file in cwd.
2525
+ * @returns The resolved output file path.
2526
+ */
2527
+ exportToJsonl(outputPath) {
2528
+ const filePath = resolve(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`);
2529
+ const dir = dirname(filePath);
2530
+ if (!existsSync(dir)) {
2531
+ mkdirSync(dir, { recursive: true });
2532
+ }
2533
+ const header = {
2534
+ type: "session",
2535
+ version: CURRENT_SESSION_VERSION,
2536
+ id: this.sessionManager.getSessionId(),
2537
+ timestamp: new Date().toISOString(),
2538
+ cwd: this.sessionManager.getCwd(),
2539
+ };
2540
+ const branchEntries = this.sessionManager.getBranch();
2541
+ const lines = [JSON.stringify(header)];
2542
+ // Re-chain parentIds to form a linear sequence
2543
+ let prevId = null;
2544
+ for (const entry of branchEntries) {
2545
+ const linear = { ...entry, parentId: prevId };
2546
+ lines.push(JSON.stringify(linear));
2547
+ prevId = entry.id;
2548
+ }
2549
+ writeFileSync(filePath, `${lines.join("\n")}\n`);
2550
+ return filePath;
2551
+ }
2552
+ /**
2553
+ * Import a JSONL session file.
2554
+ * Copies the file into the session directory and switches to it (like /resume).
2555
+ * @param inputPath Path to the JSONL file to import.
2556
+ * @returns true if the session was switched successfully.
2557
+ */
2558
+ async importFromJsonl(inputPath) {
2559
+ const resolved = resolve(inputPath);
2560
+ if (!existsSync(resolved)) {
2561
+ throw new Error(`File not found: ${resolved}`);
2562
+ }
2563
+ // Copy into the session directory so we don't modify the original
2564
+ const sessionDir = this.sessionManager.getSessionDir();
2565
+ if (!existsSync(sessionDir)) {
2566
+ mkdirSync(sessionDir, { recursive: true });
2567
+ }
2568
+ const destPath = join(sessionDir, basename(resolved));
2569
+ // Avoid overwriting if source and destination are the same file
2570
+ if (resolve(destPath) !== resolved) {
2571
+ copyFileSync(resolved, destPath);
2572
+ }
2573
+ return this.switchSession(destPath);
2574
+ }
2447
2575
  // =========================================================================
2448
2576
  // Utilities
2449
2577
  // =========================================================================