@namzu/cli 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/dist/bin.js +4 -32
  2. package/dist/bin.js.map +1 -1
  3. package/dist/cli.d.ts +14 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +124 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/cli.test.d.ts +2 -0
  8. package/dist/cli.test.d.ts.map +1 -0
  9. package/dist/cli.test.js +94 -0
  10. package/dist/cli.test.js.map +1 -0
  11. package/dist/commands/doctor.d.ts +6 -0
  12. package/dist/commands/doctor.d.ts.map +1 -1
  13. package/dist/commands/doctor.js +10 -0
  14. package/dist/commands/doctor.js.map +1 -1
  15. package/dist/commands/providers.d.ts +17 -0
  16. package/dist/commands/providers.d.ts.map +1 -0
  17. package/dist/commands/providers.js +274 -0
  18. package/dist/commands/providers.js.map +1 -0
  19. package/dist/commands/registry.d.ts +11 -0
  20. package/dist/commands/registry.d.ts.map +1 -0
  21. package/dist/commands/registry.js +28 -0
  22. package/dist/commands/registry.js.map +1 -0
  23. package/dist/commands/run.d.ts +14 -0
  24. package/dist/commands/run.d.ts.map +1 -0
  25. package/dist/commands/run.js +73 -0
  26. package/dist/commands/run.js.map +1 -0
  27. package/dist/commands/stubs.d.ts +12 -0
  28. package/dist/commands/stubs.d.ts.map +1 -0
  29. package/dist/commands/stubs.js +32 -0
  30. package/dist/commands/stubs.js.map +1 -0
  31. package/dist/commands/tools.d.ts +16 -0
  32. package/dist/commands/tools.d.ts.map +1 -0
  33. package/dist/commands/tools.js +161 -0
  34. package/dist/commands/tools.js.map +1 -0
  35. package/dist/commands/types.d.ts +25 -0
  36. package/dist/commands/types.d.ts.map +1 -0
  37. package/dist/commands/types.js +2 -0
  38. package/dist/commands/types.js.map +1 -0
  39. package/dist/config/load.d.ts +25 -0
  40. package/dist/config/load.d.ts.map +1 -0
  41. package/dist/config/load.js +92 -0
  42. package/dist/config/load.js.map +1 -0
  43. package/dist/config/load.test.d.ts +2 -0
  44. package/dist/config/load.test.d.ts.map +1 -0
  45. package/dist/config/load.test.js +57 -0
  46. package/dist/config/load.test.js.map +1 -0
  47. package/dist/config/schema.d.ts +29 -0
  48. package/dist/config/schema.d.ts.map +1 -0
  49. package/dist/config/schema.js +13 -0
  50. package/dist/config/schema.js.map +1 -0
  51. package/dist/doctor/registry.d.ts.map +1 -1
  52. package/dist/doctor/registry.js +3 -1
  53. package/dist/doctor/registry.js.map +1 -1
  54. package/dist/doctor/registry.test.js +4 -1
  55. package/dist/doctor/registry.test.js.map +1 -1
  56. package/dist/index.d.ts +6 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +7 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/integrations/clawtool/agents.d.ts +31 -0
  61. package/dist/integrations/clawtool/agents.d.ts.map +1 -0
  62. package/dist/integrations/clawtool/agents.js +30 -0
  63. package/dist/integrations/clawtool/agents.js.map +1 -0
  64. package/dist/integrations/clawtool/agents.test.d.ts +2 -0
  65. package/dist/integrations/clawtool/agents.test.d.ts.map +1 -0
  66. package/dist/integrations/clawtool/agents.test.js +79 -0
  67. package/dist/integrations/clawtool/agents.test.js.map +1 -0
  68. package/dist/integrations/clawtool/auth.d.ts +26 -0
  69. package/dist/integrations/clawtool/auth.d.ts.map +1 -0
  70. package/dist/integrations/clawtool/auth.js +56 -0
  71. package/dist/integrations/clawtool/auth.js.map +1 -0
  72. package/dist/integrations/clawtool/auth.test.d.ts +2 -0
  73. package/dist/integrations/clawtool/auth.test.d.ts.map +1 -0
  74. package/dist/integrations/clawtool/auth.test.js +40 -0
  75. package/dist/integrations/clawtool/auth.test.js.map +1 -0
  76. package/dist/integrations/clawtool/binary.d.ts +25 -0
  77. package/dist/integrations/clawtool/binary.d.ts.map +1 -0
  78. package/dist/integrations/clawtool/binary.js +50 -0
  79. package/dist/integrations/clawtool/binary.js.map +1 -0
  80. package/dist/integrations/clawtool/binary.test.d.ts +2 -0
  81. package/dist/integrations/clawtool/binary.test.d.ts.map +1 -0
  82. package/dist/integrations/clawtool/binary.test.js +42 -0
  83. package/dist/integrations/clawtool/binary.test.js.map +1 -0
  84. package/dist/integrations/clawtool/daemon.d.ts +37 -0
  85. package/dist/integrations/clawtool/daemon.d.ts.map +1 -0
  86. package/dist/integrations/clawtool/daemon.js +109 -0
  87. package/dist/integrations/clawtool/daemon.js.map +1 -0
  88. package/dist/integrations/clawtool/daemon.test.d.ts +11 -0
  89. package/dist/integrations/clawtool/daemon.test.d.ts.map +1 -0
  90. package/dist/integrations/clawtool/daemon.test.js +118 -0
  91. package/dist/integrations/clawtool/daemon.test.js.map +1 -0
  92. package/dist/integrations/clawtool/dispatch.d.ts +33 -0
  93. package/dist/integrations/clawtool/dispatch.d.ts.map +1 -0
  94. package/dist/integrations/clawtool/dispatch.js +155 -0
  95. package/dist/integrations/clawtool/dispatch.js.map +1 -0
  96. package/dist/integrations/clawtool/dispatch.test.d.ts +2 -0
  97. package/dist/integrations/clawtool/dispatch.test.d.ts.map +1 -0
  98. package/dist/integrations/clawtool/dispatch.test.js +138 -0
  99. package/dist/integrations/clawtool/dispatch.test.js.map +1 -0
  100. package/dist/integrations/clawtool/index.d.ts +11 -0
  101. package/dist/integrations/clawtool/index.d.ts.map +1 -0
  102. package/dist/integrations/clawtool/index.js +10 -0
  103. package/dist/integrations/clawtool/index.js.map +1 -0
  104. package/dist/integrations/clawtool/mcp.d.ts +51 -0
  105. package/dist/integrations/clawtool/mcp.d.ts.map +1 -0
  106. package/dist/integrations/clawtool/mcp.js +156 -0
  107. package/dist/integrations/clawtool/mcp.js.map +1 -0
  108. package/dist/integrations/clawtool/mcp.test.d.ts +2 -0
  109. package/dist/integrations/clawtool/mcp.test.d.ts.map +1 -0
  110. package/dist/integrations/clawtool/mcp.test.js +126 -0
  111. package/dist/integrations/clawtool/mcp.test.js.map +1 -0
  112. package/dist/integrations/clawtool/paths.d.ts +8 -0
  113. package/dist/integrations/clawtool/paths.d.ts.map +1 -0
  114. package/dist/integrations/clawtool/paths.js +19 -0
  115. package/dist/integrations/clawtool/paths.js.map +1 -0
  116. package/dist/integrations/clawtool/peers.d.ts +67 -0
  117. package/dist/integrations/clawtool/peers.d.ts.map +1 -0
  118. package/dist/integrations/clawtool/peers.js +150 -0
  119. package/dist/integrations/clawtool/peers.js.map +1 -0
  120. package/dist/integrations/clawtool/plugin.d.ts +42 -0
  121. package/dist/integrations/clawtool/plugin.d.ts.map +1 -0
  122. package/dist/integrations/clawtool/plugin.js +38 -0
  123. package/dist/integrations/clawtool/plugin.js.map +1 -0
  124. package/dist/integrations/clawtool/state.d.ts +11 -0
  125. package/dist/integrations/clawtool/state.d.ts.map +1 -0
  126. package/dist/integrations/clawtool/state.js +41 -0
  127. package/dist/integrations/clawtool/state.js.map +1 -0
  128. package/dist/integrations/clawtool/state.test.d.ts +2 -0
  129. package/dist/integrations/clawtool/state.test.d.ts.map +1 -0
  130. package/dist/integrations/clawtool/state.test.js +38 -0
  131. package/dist/integrations/clawtool/state.test.js.map +1 -0
  132. package/dist/integrations/clawtool/tooling.d.ts +50 -0
  133. package/dist/integrations/clawtool/tooling.d.ts.map +1 -0
  134. package/dist/integrations/clawtool/tooling.js +89 -0
  135. package/dist/integrations/clawtool/tooling.js.map +1 -0
  136. package/dist/integrations/clawtool/tooling.test.d.ts +2 -0
  137. package/dist/integrations/clawtool/tooling.test.d.ts.map +1 -0
  138. package/dist/integrations/clawtool/tooling.test.js +58 -0
  139. package/dist/integrations/clawtool/tooling.test.js.map +1 -0
  140. package/dist/integrations/clawtool/types.d.ts +41 -0
  141. package/dist/integrations/clawtool/types.d.ts.map +1 -0
  142. package/dist/integrations/clawtool/types.js +9 -0
  143. package/dist/integrations/clawtool/types.js.map +1 -0
  144. package/dist/integrations/clipboard/image.d.ts +18 -0
  145. package/dist/integrations/clipboard/image.d.ts.map +1 -0
  146. package/dist/integrations/clipboard/image.js +88 -0
  147. package/dist/integrations/clipboard/image.js.map +1 -0
  148. package/dist/integrations/providers/discover.d.ts +68 -0
  149. package/dist/integrations/providers/discover.d.ts.map +1 -0
  150. package/dist/integrations/providers/discover.js +108 -0
  151. package/dist/integrations/providers/discover.js.map +1 -0
  152. package/dist/integrations/providers/discover.test.d.ts +2 -0
  153. package/dist/integrations/providers/discover.test.d.ts.map +1 -0
  154. package/dist/integrations/providers/discover.test.js +120 -0
  155. package/dist/integrations/providers/discover.test.js.map +1 -0
  156. package/dist/integrations/providers/index.d.ts +10 -0
  157. package/dist/integrations/providers/index.d.ts.map +1 -0
  158. package/dist/integrations/providers/index.js +12 -0
  159. package/dist/integrations/providers/index.js.map +1 -0
  160. package/dist/integrations/providers/keychain.d.ts +44 -0
  161. package/dist/integrations/providers/keychain.d.ts.map +1 -0
  162. package/dist/integrations/providers/keychain.js +154 -0
  163. package/dist/integrations/providers/keychain.js.map +1 -0
  164. package/dist/integrations/providers/keychain.test.d.ts +2 -0
  165. package/dist/integrations/providers/keychain.test.d.ts.map +1 -0
  166. package/dist/integrations/providers/keychain.test.js +21 -0
  167. package/dist/integrations/providers/keychain.test.js.map +1 -0
  168. package/dist/integrations/providers/mask.d.ts +7 -0
  169. package/dist/integrations/providers/mask.d.ts.map +1 -0
  170. package/dist/integrations/providers/mask.js +14 -0
  171. package/dist/integrations/providers/mask.js.map +1 -0
  172. package/dist/integrations/providers/mask.test.d.ts +2 -0
  173. package/dist/integrations/providers/mask.test.d.ts.map +1 -0
  174. package/dist/integrations/providers/mask.test.js +20 -0
  175. package/dist/integrations/providers/mask.test.js.map +1 -0
  176. package/dist/integrations/providers/oauth.d.ts +28 -0
  177. package/dist/integrations/providers/oauth.d.ts.map +1 -0
  178. package/dist/integrations/providers/oauth.js +65 -0
  179. package/dist/integrations/providers/oauth.js.map +1 -0
  180. package/dist/integrations/providers/oauth.test.d.ts +2 -0
  181. package/dist/integrations/providers/oauth.test.d.ts.map +1 -0
  182. package/dist/integrations/providers/oauth.test.js +86 -0
  183. package/dist/integrations/providers/oauth.test.js.map +1 -0
  184. package/dist/integrations/providers/preferences.d.ts +43 -0
  185. package/dist/integrations/providers/preferences.d.ts.map +1 -0
  186. package/dist/integrations/providers/preferences.js +111 -0
  187. package/dist/integrations/providers/preferences.js.map +1 -0
  188. package/dist/integrations/providers/preferences.test.d.ts +2 -0
  189. package/dist/integrations/providers/preferences.test.d.ts.map +1 -0
  190. package/dist/integrations/providers/preferences.test.js +68 -0
  191. package/dist/integrations/providers/preferences.test.js.map +1 -0
  192. package/dist/integrations/providers/registry.d.ts +31 -0
  193. package/dist/integrations/providers/registry.d.ts.map +1 -0
  194. package/dist/integrations/providers/registry.js +79 -0
  195. package/dist/integrations/providers/registry.js.map +1 -0
  196. package/dist/integrations/providers/schema.d.ts +73 -0
  197. package/dist/integrations/providers/schema.d.ts.map +1 -0
  198. package/dist/integrations/providers/schema.js +70 -0
  199. package/dist/integrations/providers/schema.js.map +1 -0
  200. package/dist/integrations/providers/schema.test.d.ts +2 -0
  201. package/dist/integrations/providers/schema.test.d.ts.map +1 -0
  202. package/dist/integrations/providers/schema.test.js +43 -0
  203. package/dist/integrations/providers/schema.test.js.map +1 -0
  204. package/dist/integrations/providers/secrets.d.ts +37 -0
  205. package/dist/integrations/providers/secrets.d.ts.map +1 -0
  206. package/dist/integrations/providers/secrets.js +69 -0
  207. package/dist/integrations/providers/secrets.js.map +1 -0
  208. package/dist/integrations/providers/store.d.ts +32 -0
  209. package/dist/integrations/providers/store.d.ts.map +1 -0
  210. package/dist/integrations/providers/store.js +133 -0
  211. package/dist/integrations/providers/store.js.map +1 -0
  212. package/dist/integrations/providers/store.test.d.ts +2 -0
  213. package/dist/integrations/providers/store.test.d.ts.map +1 -0
  214. package/dist/integrations/providers/store.test.js +127 -0
  215. package/dist/integrations/providers/store.test.js.map +1 -0
  216. package/dist/integrations/sessions/store.d.ts +40 -0
  217. package/dist/integrations/sessions/store.d.ts.map +1 -0
  218. package/dist/integrations/sessions/store.js +90 -0
  219. package/dist/integrations/sessions/store.js.map +1 -0
  220. package/dist/integrations/subagents/runtime.d.ts +37 -0
  221. package/dist/integrations/subagents/runtime.d.ts.map +1 -0
  222. package/dist/integrations/subagents/runtime.js +183 -0
  223. package/dist/integrations/subagents/runtime.js.map +1 -0
  224. package/dist/integrations/trust/store.d.ts +16 -0
  225. package/dist/integrations/trust/store.d.ts.map +1 -0
  226. package/dist/integrations/trust/store.js +59 -0
  227. package/dist/integrations/trust/store.js.map +1 -0
  228. package/dist/integrations/trust/store.test.d.ts +2 -0
  229. package/dist/integrations/trust/store.test.d.ts.map +1 -0
  230. package/dist/integrations/trust/store.test.js +50 -0
  231. package/dist/integrations/trust/store.test.js.map +1 -0
  232. package/dist/integrations/updates.d.ts +27 -0
  233. package/dist/integrations/updates.d.ts.map +1 -0
  234. package/dist/integrations/updates.js +99 -0
  235. package/dist/integrations/updates.js.map +1 -0
  236. package/dist/memory/store.d.ts +29 -0
  237. package/dist/memory/store.d.ts.map +1 -0
  238. package/dist/memory/store.js +74 -0
  239. package/dist/memory/store.js.map +1 -0
  240. package/dist/memory/store.test.d.ts +2 -0
  241. package/dist/memory/store.test.d.ts.map +1 -0
  242. package/dist/memory/store.test.js +63 -0
  243. package/dist/memory/store.test.js.map +1 -0
  244. package/dist/output/formatter.d.ts +30 -0
  245. package/dist/output/formatter.d.ts.map +1 -0
  246. package/dist/output/formatter.js +16 -0
  247. package/dist/output/formatter.js.map +1 -0
  248. package/dist/output/formatter.test.d.ts +2 -0
  249. package/dist/output/formatter.test.d.ts.map +1 -0
  250. package/dist/output/formatter.test.js +88 -0
  251. package/dist/output/formatter.test.js.map +1 -0
  252. package/dist/output/index.d.ts +5 -0
  253. package/dist/output/index.d.ts.map +1 -0
  254. package/dist/output/index.js +22 -0
  255. package/dist/output/index.js.map +1 -0
  256. package/dist/output/json.d.ts +13 -0
  257. package/dist/output/json.d.ts.map +1 -0
  258. package/dist/output/json.js +51 -0
  259. package/dist/output/json.js.map +1 -0
  260. package/dist/output/text.d.ts +13 -0
  261. package/dist/output/text.d.ts.map +1 -0
  262. package/dist/output/text.js +36 -0
  263. package/dist/output/text.js.map +1 -0
  264. package/dist/output/yaml.d.ts +13 -0
  265. package/dist/output/yaml.d.ts.map +1 -0
  266. package/dist/output/yaml.js +26 -0
  267. package/dist/output/yaml.js.map +1 -0
  268. package/dist/skills/store.d.ts +49 -0
  269. package/dist/skills/store.d.ts.map +1 -0
  270. package/dist/skills/store.js +109 -0
  271. package/dist/skills/store.js.map +1 -0
  272. package/dist/skills/store.test.d.ts +2 -0
  273. package/dist/skills/store.test.d.ts.map +1 -0
  274. package/dist/skills/store.test.js +80 -0
  275. package/dist/skills/store.test.js.map +1 -0
  276. package/dist/tui/App.d.ts +18 -0
  277. package/dist/tui/App.d.ts.map +1 -0
  278. package/dist/tui/App.js +742 -0
  279. package/dist/tui/App.js.map +1 -0
  280. package/dist/tui/Composer.d.ts +17 -0
  281. package/dist/tui/Composer.d.ts.map +1 -0
  282. package/dist/tui/Composer.js +123 -0
  283. package/dist/tui/Composer.js.map +1 -0
  284. package/dist/tui/LiveActivity.d.ts +22 -0
  285. package/dist/tui/LiveActivity.d.ts.map +1 -0
  286. package/dist/tui/LiveActivity.js +46 -0
  287. package/dist/tui/LiveActivity.js.map +1 -0
  288. package/dist/tui/Markdown.d.ts +18 -0
  289. package/dist/tui/Markdown.d.ts.map +1 -0
  290. package/dist/tui/Markdown.js +68 -0
  291. package/dist/tui/Markdown.js.map +1 -0
  292. package/dist/tui/PermissionOverlay.d.ts +14 -0
  293. package/dist/tui/PermissionOverlay.d.ts.map +1 -0
  294. package/dist/tui/PermissionOverlay.js +22 -0
  295. package/dist/tui/PermissionOverlay.js.map +1 -0
  296. package/dist/tui/Picker.d.ts +20 -0
  297. package/dist/tui/Picker.d.ts.map +1 -0
  298. package/dist/tui/Picker.js +72 -0
  299. package/dist/tui/Picker.js.map +1 -0
  300. package/dist/tui/ResumePicker.d.ts +12 -0
  301. package/dist/tui/ResumePicker.d.ts.map +1 -0
  302. package/dist/tui/ResumePicker.js +26 -0
  303. package/dist/tui/ResumePicker.js.map +1 -0
  304. package/dist/tui/StatusBar.d.ts +22 -0
  305. package/dist/tui/StatusBar.d.ts.map +1 -0
  306. package/dist/tui/StatusBar.js +73 -0
  307. package/dist/tui/StatusBar.js.map +1 -0
  308. package/dist/tui/Transcript.d.ts +29 -0
  309. package/dist/tui/Transcript.d.ts.map +1 -0
  310. package/dist/tui/Transcript.js +105 -0
  311. package/dist/tui/Transcript.js.map +1 -0
  312. package/dist/tui/TrustPrompt.d.ts +11 -0
  313. package/dist/tui/TrustPrompt.d.ts.map +1 -0
  314. package/dist/tui/TrustPrompt.js +13 -0
  315. package/dist/tui/TrustPrompt.js.map +1 -0
  316. package/dist/tui/agent.d.ts +163 -0
  317. package/dist/tui/agent.d.ts.map +1 -0
  318. package/dist/tui/agent.js +684 -0
  319. package/dist/tui/agent.js.map +1 -0
  320. package/dist/tui/agent.test.d.ts +2 -0
  321. package/dist/tui/agent.test.d.ts.map +1 -0
  322. package/dist/tui/agent.test.js +225 -0
  323. package/dist/tui/agent.test.js.map +1 -0
  324. package/dist/tui/index.d.ts +7 -0
  325. package/dist/tui/index.d.ts.map +1 -0
  326. package/dist/tui/index.js +29 -0
  327. package/dist/tui/index.js.map +1 -0
  328. package/dist/tui/logo.d.ts +23 -0
  329. package/dist/tui/logo.d.ts.map +1 -0
  330. package/dist/tui/logo.js +35 -0
  331. package/dist/tui/logo.js.map +1 -0
  332. package/dist/tui/markdownParser.d.ts +45 -0
  333. package/dist/tui/markdownParser.d.ts.map +1 -0
  334. package/dist/tui/markdownParser.js +127 -0
  335. package/dist/tui/markdownParser.js.map +1 -0
  336. package/dist/tui/markdownParser.test.d.ts +2 -0
  337. package/dist/tui/markdownParser.test.d.ts.map +1 -0
  338. package/dist/tui/markdownParser.test.js +90 -0
  339. package/dist/tui/markdownParser.test.js.map +1 -0
  340. package/dist/tui/mentions.d.ts +22 -0
  341. package/dist/tui/mentions.d.ts.map +1 -0
  342. package/dist/tui/mentions.js +59 -0
  343. package/dist/tui/mentions.js.map +1 -0
  344. package/dist/tui/mentions.test.d.ts +2 -0
  345. package/dist/tui/mentions.test.d.ts.map +1 -0
  346. package/dist/tui/mentions.test.js +35 -0
  347. package/dist/tui/mentions.test.js.map +1 -0
  348. package/dist/tui/slashCommands.d.ts +64 -0
  349. package/dist/tui/slashCommands.d.ts.map +1 -0
  350. package/dist/tui/slashCommands.js +153 -0
  351. package/dist/tui/slashCommands.js.map +1 -0
  352. package/dist/tui/slashCommands.test.d.ts +2 -0
  353. package/dist/tui/slashCommands.test.d.ts.map +1 -0
  354. package/dist/tui/slashCommands.test.js +114 -0
  355. package/dist/tui/slashCommands.test.js.map +1 -0
  356. package/dist/tui/theme.d.ts +34 -0
  357. package/dist/tui/theme.d.ts.map +1 -0
  358. package/dist/tui/theme.js +32 -0
  359. package/dist/tui/theme.js.map +1 -0
  360. package/dist/tui/types.d.ts +27 -0
  361. package/dist/tui/types.d.ts.map +1 -0
  362. package/dist/tui/types.js +7 -0
  363. package/dist/tui/types.js.map +1 -0
  364. package/package.json +13 -3
@@ -0,0 +1,684 @@
1
+ /**
2
+ * TUI agent session — provider-direct, tool-enabled.
3
+ *
4
+ * Reads the picker selection (preferences.json), looks up the chosen
5
+ * provider in the declarative registry, lazy-loads the matching
6
+ * `@namzu/<type>` package, constructs the SDK provider, builds a
7
+ * `ToolRegistry` of the SDK builtin tools (bash / read / write / edit /
8
+ * glob / grep / …), and exposes `send(messages, signal?) →
9
+ * AsyncIterable<AgentEvent>` over the SDK agent loop `query()`.
10
+ *
11
+ * Unlike the earlier `chatStream()`-only adapter, this drives the full
12
+ * tool-execution loop: the model can call tools, their results are fed
13
+ * back, and the loop iterates until the turn settles. We translate the
14
+ * SDK's `RunEvent` stream into the TUI's smaller `AgentEvent` vocabulary
15
+ * (text deltas + tool start/end + done/error).
16
+ *
17
+ * The TUI owns conversation history and passes the full `Message[]` on
18
+ * every turn (stateless session). Empty / partial states (no
19
+ * credentials, no preferences, no matching detected provider) return an
20
+ * `emptySession()` whose `send()` yields a single error event so the UI
21
+ * renders an actionable hint rather than crashing.
22
+ */
23
+ import { DiskMemoryStore, DiskTaskStore, ProviderRegistry, SearchToolsTool, ToolRegistry, buildMemoryTools, getBuiltinTools, query, } from '@namzu/sdk';
24
+ import { join } from 'node:path';
25
+ import { loadClawtoolToolDefinitions } from '../integrations/clawtool/tooling.js';
26
+ import { PROVIDER_REGISTRY, discoverProviders, ensureFreshAnthropicToken, findDetected, isAnthropicOAuthToken, readClaudeCodeKeychainCredential, readPreferences, } from '../integrations/providers/index.js';
27
+ import { createSubagentRuntime } from '../integrations/subagents/runtime.js';
28
+ import { composeMemoryPrompt, readMemory } from '../memory/store.js';
29
+ /**
30
+ * Read preferences + run discovery once. Returned context drives the
31
+ * App's lifecycle decision: ready / picker / unhealthy.
32
+ */
33
+ export async function probeAgentSession() {
34
+ const read = readPreferences();
35
+ const detected = await discoverProviders();
36
+ switch (read.status) {
37
+ case 'ok':
38
+ return { preferences: read.prefs, needsRepickReason: null, detected };
39
+ case 'missing':
40
+ return { preferences: null, needsRepickReason: null, detected };
41
+ case 'needs-repick':
42
+ return { preferences: null, needsRepickReason: read.reason, detected };
43
+ }
44
+ }
45
+ const registered = new Set();
46
+ async function ensureRegistered(id) {
47
+ if (registered.has(id))
48
+ return;
49
+ switch (id) {
50
+ case 'anthropic': {
51
+ const mod = await import('@namzu/anthropic');
52
+ mod.registerAnthropic();
53
+ break;
54
+ }
55
+ case 'openai': {
56
+ const mod = await import('@namzu/openai');
57
+ mod.registerOpenAI();
58
+ break;
59
+ }
60
+ case 'openrouter': {
61
+ const mod = await import('@namzu/openrouter');
62
+ mod.registerOpenRouter();
63
+ break;
64
+ }
65
+ case 'ollama': {
66
+ const mod = await import('@namzu/ollama');
67
+ mod.registerOllama();
68
+ break;
69
+ }
70
+ default:
71
+ throw new Error(`provider "${id}" is not wired yet; pick another or wait for support`);
72
+ }
73
+ registered.add(id);
74
+ }
75
+ // Builtins we don't expose: `verify_outputs` — not part of the recognizable
76
+ // Claude-Code tool surface, just noise in `/tools`. (`append` was removed
77
+ // from the SDK entirely; `edit` with insertLine:"end" covers it.)
78
+ const EXCLUDED_BUILTINS = new Set(['verify_outputs']);
79
+ // namzu's own identity. Injected as system context so the agent presents as
80
+ // namzu — not Claude/Claude Code — even on the Anthropic OAuth path, which
81
+ // requires a "You are Claude Code" prefix block for the token to authorize.
82
+ const NAMZU_IDENTITY = [
83
+ "You are namzu, an AI coding agent that runs in the user's terminal via the namzu CLI.",
84
+ 'You are built on the @namzu/sdk and act through tools (bash, read, write, edit, glob, grep).',
85
+ 'Your name is namzu. When asked who or what you are, identify yourself as namzu —',
86
+ 'not Claude or Claude Code — even though you may be powered by an underlying model',
87
+ 'from Anthropic or another provider.',
88
+ '',
89
+ 'CRITICAL — never fabricate. Only claim to have done something if you actually did it through a tool call in THIS turn:',
90
+ '- Never say you ran a command, wrote/edited a file, delegated to a sub-agent, or researched something unless the corresponding tool call actually ran and returned.',
91
+ '- Never invent file paths, command output, URLs, research findings, or results. If you announce an action ("running…", "delegating…"), you MUST immediately make the tool call — do not narrate an action and then skip it.',
92
+ '- If a capability or tool is unavailable (e.g. no web access, a tool is missing, a sub-agent failed), say so plainly and stop — do not improvise a fake result.',
93
+ '- When you delegate with the `Agent` tool, report only what the sub-agent actually returned in its tool result; if it wrote files, verify with a tool before claiming paths.',
94
+ '- A reply from a tool that delegates to ANOTHER agent (e.g. clawtool `agent.run`, an A2A `tasks/send`, a remote peer) is that agent\'s unverified CLAIM, not fact — another model can hallucinate. If it says it ran a command, wrote a file, or "here is the output", treat that as narrative and confirm it yourself with a deterministic tool (a real shell like `bash.run`, a file read) before reporting it as done. Distinguish such conversational agent calls from deterministic tools, and never present another agent\'s prose as your own verified result.',
95
+ ].join('\n');
96
+ function buildToolRegistry() {
97
+ const registry = new ToolRegistry();
98
+ registry.register(getBuiltinTools().filter((t) => !EXCLUDED_BUILTINS.has(t.name)));
99
+ // SDK memory: the agent gets search_memory / read_memory / save_memory over
100
+ // a structured store at ~/.namzu/memory (separate from the user-curated
101
+ // MEMORY.md that's injected into the prompt).
102
+ const memoryStore = new DiskMemoryStore({ baseDir: join(process.cwd(), '.namzu') });
103
+ registry.register(buildMemoryTools(memoryStore, memoryStore.getIndex()));
104
+ // `search_tools` lets the model load deferred (clawtool) tools on demand.
105
+ registry.register([SearchToolsTool]);
106
+ return registry;
107
+ }
108
+ export async function createAgentSession(prefs, detected, scope = mintScope()) {
109
+ const entry = PROVIDER_REGISTRY[prefs.provider];
110
+ if (!entry) {
111
+ return emptySession(`Unknown provider "${prefs.provider}" — pick another.`);
112
+ }
113
+ const det = findDetected(detected, prefs.provider);
114
+ if (entry.requiresApiKey && (!det || !det.apiKey)) {
115
+ return emptySession(`No credential found for ${entry.label}. Set one of: ${entry.envVars.join(', ')} — or pick another provider.`);
116
+ }
117
+ try {
118
+ await ensureRegistered(prefs.provider);
119
+ }
120
+ catch (err) {
121
+ return emptySession(err instanceof Error ? err.message : String(err));
122
+ }
123
+ const model = prefs.model ?? entry.defaultModel;
124
+ let provider;
125
+ try {
126
+ provider = constructProvider(prefs.provider, det, model);
127
+ }
128
+ catch (err) {
129
+ return emptySession(`Failed to construct ${entry.label}: ${err instanceof Error ? err.message : String(err)}`);
130
+ }
131
+ // Claude Code OAuth access tokens are short-lived (~8h). They rarely lapse
132
+ // *during* a turn, but they do between turns — an idle session that sends
133
+ // again hours later would otherwise 401. So before each turn (see `send`)
134
+ // we re-read the Keychain (Claude Code may have rotated it) and refresh a
135
+ // stale token, rebuilding the client only when the token actually changed.
136
+ // Gated on `det.oauth` so env / secrets credentials are never touched.
137
+ const keychainRefresh = prefs.provider === 'anthropic' && Boolean(det?.oauth);
138
+ let currentToken = det?.apiKey;
139
+ const refreshTokenIfNeeded = async () => {
140
+ if (!keychainRefresh)
141
+ return;
142
+ const cred = readClaudeCodeKeychainCredential();
143
+ if (!cred)
144
+ return;
145
+ const fresh = await ensureFreshAnthropicToken(cred.accessToken, {
146
+ refreshToken: cred.refreshToken,
147
+ expiresAt: cred.expiresAt,
148
+ });
149
+ if (fresh === currentToken)
150
+ return;
151
+ currentToken = fresh;
152
+ try {
153
+ provider = constructProvider('anthropic', { ...det, apiKey: fresh }, model);
154
+ }
155
+ catch {
156
+ // Keep the previous client; the turn may still 401 but won't crash.
157
+ }
158
+ };
159
+ const registry = buildToolRegistry();
160
+ // clawtool's catalog (~70 tools) is registered DEFERRED: each costs only a
161
+ // name line in the prompt (no JSON schema), so it never balloons a turn —
162
+ // the model loads what it needs via `search_tools`. Best-effort: absent /
163
+ // down / slow clawtool just yields zero deferred tools, non-fatal.
164
+ const clawtoolTools = await loadClawtoolToolDefinitions({ skipNames: registry.listNames() });
165
+ if (clawtoolTools.length > 0)
166
+ registry.register(clawtoolTools, 'deferred');
167
+ // Native sub-agents: register the canonical `Agent` tool so the model can
168
+ // delegate a self-contained task to a fresh sub-agent (own context window).
169
+ // Best-effort — if the runtime can't stand up, the chat still works.
170
+ let subagentGateway;
171
+ // Buffer of the in-flight sub-agent's tool steps. The gateway streams the
172
+ // child's events here while the parent's `Agent` tool call blocks; runTurn
173
+ // drains it onto the `Agent` result as a `├─/└─` tree. Scoped per `Agent`
174
+ // call (cleared when one starts).
175
+ const childSteps = [];
176
+ try {
177
+ const sub = await createSubagentRuntime({
178
+ cwd: process.cwd(),
179
+ model,
180
+ buildProvider: () => constructProvider(prefs.provider, det ? { ...det, apiKey: currentToken ?? det.apiKey } : det, model),
181
+ buildTools: () => {
182
+ // Sub-agents get the same working set as the parent — builtins +
183
+ // memory + search_tools, plus clawtool's catalog (deferred) so they
184
+ // can load research / peer-dispatch tools on demand. Without this a
185
+ // sub-agent has no way to do real work beyond local files.
186
+ const r = buildToolRegistry();
187
+ if (clawtoolTools.length > 0)
188
+ r.register(clawtoolTools, 'deferred');
189
+ return r;
190
+ },
191
+ verificationGate: VERIFICATION_GATE,
192
+ onEvent: (e) => {
193
+ if (e.type === 'tool_executing') {
194
+ childSteps.push(`${e.toolName}(${summarizeToolInput(e.input)})`);
195
+ }
196
+ },
197
+ });
198
+ registry.register([sub.agentTool]);
199
+ subagentGateway = sub.gateway;
200
+ }
201
+ catch {
202
+ // Sub-agents unavailable this session — non-fatal.
203
+ }
204
+ const activeToolNames = registry.getCallableTools().map((t) => t.name);
205
+ const deferredToolCount = clawtoolTools.length;
206
+ // Task store → query auto-registers create_task / update_task / list_tasks
207
+ // and emits task_created/task_updated, so the agent can track a plan for the
208
+ // current request (Claude-Code todo style). Tasks are run-scoped.
209
+ const taskStore = new DiskTaskStore({
210
+ baseDir: join(process.cwd(), '.namzu'),
211
+ defaultRunId: 'run_namzu-cli',
212
+ tenantId: scope.tenantId,
213
+ });
214
+ // Persists across turns: once the user picks "approve all", later tool
215
+ // batches in this session run without prompting.
216
+ const approval = { all: false };
217
+ return {
218
+ hasProvider: true,
219
+ providerSummary: entry.label,
220
+ modelSummary: model,
221
+ toolNames: activeToolNames,
222
+ deferredToolCount,
223
+ errorHint: null,
224
+ send: async function* (messages, opts) {
225
+ // Renew a lapsed OAuth token before the turn runs (no-op for valid
226
+ // tokens and non-keychain credentials).
227
+ await refreshTokenIfNeeded();
228
+ // namzu identity first (so it establishes who the agent is even when
229
+ // the Anthropic OAuth path prepends the required Claude Code prefix),
230
+ // then memory read fresh each turn, then per-turn extra (active skills).
231
+ const memoryPrompt = composeMemoryPrompt(readMemory());
232
+ const systemPrompt = [NAMZU_IDENTITY, memoryPrompt, opts?.extraSystem]
233
+ .filter((s) => Boolean(s))
234
+ .join('\n\n') || undefined;
235
+ yield* runTurn(provider, model, registry, scope, approval, taskStore, systemPrompt, messages, opts, subagentGateway, childSteps);
236
+ },
237
+ };
238
+ }
239
+ function constructProvider(id, det, model) {
240
+ switch (id) {
241
+ case 'anthropic': {
242
+ const token = det?.apiKey ?? '';
243
+ const isOAuth = token.length > 0 && isAnthropicOAuthToken(token);
244
+ const { provider } = ProviderRegistry.create({
245
+ type: 'anthropic',
246
+ ...(isOAuth ? { authToken: token } : { apiKey: token }),
247
+ baseURL: det?.baseUrl,
248
+ model,
249
+ });
250
+ return provider;
251
+ }
252
+ case 'openai': {
253
+ const { provider } = ProviderRegistry.create({
254
+ type: 'openai',
255
+ apiKey: det?.apiKey ?? '',
256
+ baseURL: det?.baseUrl,
257
+ model,
258
+ });
259
+ return provider;
260
+ }
261
+ case 'openrouter': {
262
+ const { provider } = ProviderRegistry.create({
263
+ type: 'openrouter',
264
+ apiKey: det?.apiKey ?? '',
265
+ baseUrl: det?.baseUrl,
266
+ });
267
+ return provider;
268
+ }
269
+ case 'ollama': {
270
+ const { provider } = ProviderRegistry.create({
271
+ type: 'ollama',
272
+ host: det?.baseUrl,
273
+ model,
274
+ });
275
+ return provider;
276
+ }
277
+ default:
278
+ throw new Error(`provider "${id}" is not yet wired to ProviderRegistry`);
279
+ }
280
+ }
281
+ /** One scope per launched TUI session; runId is minted fresh per turn by the SDK. */
282
+ function mintScope() {
283
+ const suffix = `tui-${Date.now().toString(36)}`;
284
+ return {
285
+ sessionId: `ses_${suffix}`,
286
+ threadId: `thd_${suffix}`,
287
+ projectId: `prj_${suffix}`,
288
+ tenantId: `tnt_${suffix}`,
289
+ };
290
+ }
291
+ // Pre-execution safety gate: hard-deny catastrophic shell patterns
292
+ // (`rm -rf /`, mkfs, `curl … | sh`, sudo, fork bombs — the SDK's narrow
293
+ // DANGEROUS_PATTERNS list, which does NOT match e.g. `rm -rf node_modules`),
294
+ // auto-allow read-only tools, and send everything else to the permission
295
+ // prompt (`review` → our resumeHandler). The deny rule applies even in
296
+ // --yolo mode, so bypass never lets the model brick the machine.
297
+ const VERIFICATION_GATE = {
298
+ enabled: true,
299
+ allowReadOnlyTools: true,
300
+ denyDangerousPatterns: true,
301
+ logDecisions: false,
302
+ rules: [],
303
+ };
304
+ // Automatic context compression for long, tool-heavy turns: the structured
305
+ // strategy summarizes old tool results / notes once the message buffer
306
+ // crosses the trigger threshold of the token budget, keeping the most
307
+ // recent messages verbatim. A no-op for short turns; a safety net against
308
+ // unbounded context growth on long ones.
309
+ const COMPACTION_CONFIG = {
310
+ strategy: 'structured',
311
+ triggerThreshold: 0.7,
312
+ resetThreshold: 0.4,
313
+ keepRecentMessages: 6,
314
+ maxToolResults: 30,
315
+ maxListSize: 25,
316
+ llmVerification: false,
317
+ llmVerificationMaxTokens: 2048,
318
+ richStateThreshold: 15,
319
+ convoTextBudget: 12_000,
320
+ maxSentencesPerTurn: 5,
321
+ maxCharsPerNote: 500,
322
+ maxCharsPerRequirement: 300,
323
+ maxCharsPerTask: 400,
324
+ };
325
+ async function* runTurn(provider, model, tools, scope, approval, taskStore, systemPrompt, messages, opts, taskGateway, childSteps) {
326
+ const signal = opts?.signal;
327
+ try {
328
+ const events = query({
329
+ provider,
330
+ tools,
331
+ taskStore,
332
+ ...(taskGateway ? { taskGateway } : {}),
333
+ verificationGate: VERIFICATION_GATE,
334
+ compactionConfig: COMPACTION_CONFIG,
335
+ runConfig: {
336
+ model,
337
+ timeoutMs: 600_000,
338
+ tokenBudget: 1_000_000,
339
+ maxIterations: 50,
340
+ maxResponseTokens: 8192,
341
+ permissionMode: 'auto',
342
+ },
343
+ agentId: 'namzu',
344
+ agentName: 'namzu',
345
+ ...(systemPrompt ? { systemPrompt } : {}),
346
+ messages: [...messages],
347
+ workingDirectory: process.cwd(),
348
+ resumeHandler: makeResumeHandler(approval, opts?.onPermission),
349
+ signal,
350
+ ...scope,
351
+ });
352
+ for await (const event of events) {
353
+ if (signal?.aborted) {
354
+ yield { kind: 'error', message: 'aborted' };
355
+ return;
356
+ }
357
+ const mapped = toAgentEvent(event);
358
+ if (!mapped)
359
+ continue;
360
+ // On an `Agent` delegation finishing, attach the sub-agent's tool
361
+ // steps (collected via the gateway while the call blocked) as a
362
+ // `├─/└─` tree under its result, then reset for the next delegation.
363
+ // (Don't clear on tool_executing: the parent's events can be buffered
364
+ // and pulled only after the child already ran, which would wipe it.)
365
+ if (event.type === 'tool_completed' &&
366
+ event.toolName === 'Agent' &&
367
+ childSteps.length > 0 &&
368
+ mapped.kind === 'tool-end') {
369
+ yield { ...mapped, detail: [...(mapped.detail ?? []), ...asTree(childSteps)] };
370
+ childSteps.length = 0;
371
+ continue;
372
+ }
373
+ yield mapped;
374
+ }
375
+ }
376
+ catch (err) {
377
+ yield {
378
+ kind: 'error',
379
+ message: err instanceof Error ? err.message : String(err),
380
+ };
381
+ }
382
+ }
383
+ /**
384
+ * Bridge the SDK's HITL `tool_review` request to the TUI's permission
385
+ * callback. Read-only batches (nothing destructive) run silently; batches
386
+ * with a destructive call prompt the user unless they've already chosen
387
+ * "approve all" for the session. Plans and iteration checkpoints are
388
+ * auto-continued (the TUI doesn't use plan mode).
389
+ */
390
+ export function makeResumeHandler(approval, onPermission) {
391
+ return async (request) => {
392
+ if (request.type !== 'tool_review') {
393
+ return request.type === 'plan_approval' ? { action: 'approve_plan' } : { action: 'continue' };
394
+ }
395
+ if (!onPermission || approval.all || !batchNeedsPrompt(request.toolCalls)) {
396
+ return { action: 'approve_tools' };
397
+ }
398
+ const decision = await onPermission({
399
+ toolCalls: request.toolCalls.map((tc) => ({
400
+ id: tc.id,
401
+ name: tc.name,
402
+ summary: summarizeToolInput(tc.input),
403
+ isDestructive: tc.isDestructive,
404
+ preview: previewToolInput(tc.name, tc.input),
405
+ })),
406
+ });
407
+ switch (decision.kind) {
408
+ case 'approve':
409
+ return { action: 'approve_tools' };
410
+ case 'approve-all':
411
+ approval.all = true;
412
+ return { action: 'approve_tools' };
413
+ case 'reject':
414
+ return {
415
+ action: 'reject_tools',
416
+ feedback: decision.feedback ?? 'User declined to run the proposed tool(s).',
417
+ };
418
+ }
419
+ };
420
+ }
421
+ /**
422
+ * Tools known to only observe, never mutate. Anything NOT in this set
423
+ * prompts for approval (safe-by-default: unknown and future tools — e.g.
424
+ * bridged clawtool tools — are treated as needing consent). Matched
425
+ * case-insensitively so `Read`/`read` both count.
426
+ */
427
+ const READ_ONLY_TOOLS = new Set([
428
+ 'read',
429
+ 'glob',
430
+ 'grep',
431
+ 'ls',
432
+ 'verify_outputs',
433
+ // Memory + task tools touch only the agent's own ~/.namzu state — safe, no prompt.
434
+ 'search_memory',
435
+ 'read_memory',
436
+ 'save_memory',
437
+ 'task_create',
438
+ 'task_update',
439
+ 'task_list',
440
+ ]);
441
+ /**
442
+ * A batch needs explicit approval when any call mutates state: flagged
443
+ * destructive by the SDK, or simply not on the read-only allowlist.
444
+ */
445
+ export function batchNeedsPrompt(toolCalls) {
446
+ return toolCalls.some((tc) => tc.isDestructive || !READ_ONLY_TOOLS.has(tc.name.toLowerCase()));
447
+ }
448
+ /**
449
+ * Translate one SDK `RunEvent` into the TUI's `AgentEvent` vocabulary, or
450
+ * `null` for events the chat surface doesn't render (iteration markers,
451
+ * token usage, checkpoints, plan/task lifecycle, …). Pure — unit-tested.
452
+ */
453
+ export function toAgentEvent(event) {
454
+ switch (event.type) {
455
+ case 'text_delta':
456
+ return { kind: 'delta', text: event.text };
457
+ case 'tool_executing':
458
+ return {
459
+ kind: 'tool-start',
460
+ toolUseId: event.toolUseId,
461
+ toolName: event.toolName,
462
+ summary: summarizeToolInput(event.input),
463
+ detail: toolStartDetail(event.toolName, event.input),
464
+ };
465
+ case 'tool_completed':
466
+ return {
467
+ kind: 'tool-end',
468
+ toolUseId: event.toolUseId,
469
+ toolName: event.toolName,
470
+ isError: event.isError,
471
+ summary: firstLine(event.result),
472
+ detail: toolEndDetail(event.toolName, event.result),
473
+ };
474
+ case 'token_usage_updated':
475
+ return {
476
+ kind: 'usage',
477
+ totalTokens: event.usage.totalTokens,
478
+ costUsd: event.cost.totalCost,
479
+ };
480
+ case 'task_created':
481
+ return { kind: 'task', subject: event.subject, status: event.status };
482
+ case 'task_updated':
483
+ // Only surface completions — skip pending/in-progress churn so the
484
+ // transcript shows "todo added" then "todo done", not every flip.
485
+ return event.status === 'completed'
486
+ ? { kind: 'task', subject: event.subject, status: event.status }
487
+ : null;
488
+ case 'run_completed':
489
+ return { kind: 'done' };
490
+ case 'run_failed':
491
+ return { kind: 'error', message: event.error };
492
+ default:
493
+ return null;
494
+ }
495
+ }
496
+ /** Render a sub-agent's tool steps as a `├─/└─` tree for the Agent result. */
497
+ function asTree(steps) {
498
+ return steps.map((s, i) => `${i === steps.length - 1 ? '└─' : '├─'} ${s}`);
499
+ }
500
+ /** Short, human-readable one-liner for a tool call (e.g. `ls -la`, path). */
501
+ function summarizeToolInput(input) {
502
+ if (input && typeof input === 'object') {
503
+ const obj = input;
504
+ const pick = (k) => (typeof obj[k] === 'string' ? obj[k] : undefined);
505
+ const primary = pick('command') ?? pick('path') ?? pick('file_path') ?? pick('pattern') ?? pick('query');
506
+ if (primary)
507
+ return truncate(primary, 120);
508
+ }
509
+ if (typeof input === 'string')
510
+ return truncate(input, 120);
511
+ return truncate(JSON.stringify(input ?? {}), 120);
512
+ }
513
+ function truncate(value, max) {
514
+ const oneLine = value.replace(/\s+/g, ' ');
515
+ return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine;
516
+ }
517
+ /**
518
+ * Multi-line preview of a mutating tool's effect, shown in the permission
519
+ * overlay so the user approves with sight of what changes. `write` shows
520
+ * the leading content lines; `edit` shows a minimal -old / +new diff;
521
+ * everything else has no preview (the one-line summary suffices). Pure —
522
+ * unit-tested.
523
+ */
524
+ export function previewToolInput(toolName, input) {
525
+ if (!input || typeof input !== 'object')
526
+ return undefined;
527
+ const obj = input;
528
+ const str = (k) => (typeof obj[k] === 'string' ? obj[k] : undefined);
529
+ const name = toolName.toLowerCase();
530
+ if (name === 'write') {
531
+ const content = str('content');
532
+ if (content !== undefined)
533
+ return previewLines(content, 8);
534
+ }
535
+ if (name === 'edit') {
536
+ const oldStr = str('old_string') ?? str('oldStr');
537
+ const newStr = str('new_string') ?? str('newStr');
538
+ const lines = [];
539
+ if (oldStr)
540
+ for (const l of previewLines(oldStr, 4))
541
+ lines.push(`- ${l}`);
542
+ if (newStr)
543
+ for (const l of previewLines(newStr, 4))
544
+ lines.push(`+ ${l}`);
545
+ if (lines.length > 0)
546
+ return lines;
547
+ }
548
+ return undefined;
549
+ }
550
+ function previewLines(value, max) {
551
+ const lines = value.split('\n');
552
+ const head = lines.slice(0, max).map((l) => truncate(l, 100));
553
+ if (lines.length > max)
554
+ head.push(`… (+${lines.length - max} more lines)`);
555
+ return head;
556
+ }
557
+ const MAX_DETAIL_LINES = 200;
558
+ function clampLines(value) {
559
+ const lines = value.replace(/\s+$/, '').split('\n');
560
+ return lines.length > MAX_DETAIL_LINES ? lines.slice(0, MAX_DETAIL_LINES) : lines;
561
+ }
562
+ /**
563
+ * Diff / content shown under a tool CALL (`⏺`): an `edit` renders a
564
+ * `- old` / `+ new` diff, a `write` renders the content. Other tools show
565
+ * nothing at call time (their output appears under the result instead).
566
+ */
567
+ export function toolStartDetail(toolName, input) {
568
+ if (!input || typeof input !== 'object')
569
+ return undefined;
570
+ const obj = input;
571
+ const str = (k) => (typeof obj[k] === 'string' ? obj[k] : undefined);
572
+ const name = toolName.toLowerCase().replace(/^clawtool_/, '');
573
+ if (name === 'write') {
574
+ const content = str('content');
575
+ return content !== undefined ? clampLines(content) : undefined;
576
+ }
577
+ if (name === 'edit') {
578
+ const oldStr = str('old_string') ?? str('oldStr');
579
+ const newStr = str('new_string') ?? str('newStr');
580
+ const lines = [];
581
+ if (oldStr)
582
+ for (const l of clampLines(oldStr))
583
+ lines.push(`- ${l}`);
584
+ if (newStr)
585
+ for (const l of clampLines(newStr))
586
+ lines.push(`+ ${l}`);
587
+ return lines.length > 0 ? lines : undefined;
588
+ }
589
+ return undefined;
590
+ }
591
+ /** Parse a string as a JSON object, or null. clawtool/MCP tools return JSON. */
592
+ function parseJsonObject(s) {
593
+ const t = s.trim();
594
+ if (!(t.startsWith('{') || t.startsWith('[')))
595
+ return null;
596
+ try {
597
+ const v = JSON.parse(t);
598
+ return typeof v === 'object' && v !== null ? v : null;
599
+ }
600
+ catch {
601
+ return null;
602
+ }
603
+ }
604
+ /**
605
+ * Strip the bash tool's `STDOUT:` / `STDERR:` section labels so command output
606
+ * reads as plain text (the ✗ glyph already signals a non-zero exit). Other
607
+ * text passes through unchanged.
608
+ */
609
+ function cleanToolText(s) {
610
+ if (!/^STDOUT:|(?:^|\n)STDERR:/.test(s))
611
+ return s;
612
+ return s
613
+ .replace(/^STDOUT:\n?/, '')
614
+ .replace(/\n{0,2}STDERR:\n?/, '\n')
615
+ .trim();
616
+ }
617
+ /** Unwrap a tool's payload string from its JSON envelope, if any. */
618
+ function payloadString(result) {
619
+ const obj = parseJsonObject(result);
620
+ if (!obj)
621
+ return null;
622
+ const inner = obj.output ?? obj.result ?? obj.content ?? obj.text;
623
+ if (typeof inner === 'string' && inner.trim().length > 0)
624
+ return cleanToolText(inner.trim());
625
+ return null;
626
+ }
627
+ /** Pretty-print JSON tool output; otherwise return the raw text as lines. */
628
+ function resultToLines(result) {
629
+ const payload = payloadString(result);
630
+ if (payload !== null)
631
+ return clampLines(payload);
632
+ const obj = parseJsonObject(result);
633
+ if (obj)
634
+ return clampLines(JSON.stringify(obj, null, 2));
635
+ return clampLines(cleanToolText(result.trim()));
636
+ }
637
+ /**
638
+ * Output shown under a tool RESULT (`⎿`). For `edit`/`write` the diff was
639
+ * already shown at call time, so the result stays a one-line confirmation;
640
+ * every other tool (read/bash/grep/…) shows its captured output here — JSON
641
+ * results are pretty-printed / unwrapped so they don't read as a raw blob.
642
+ */
643
+ export function toolEndDetail(toolName, result) {
644
+ const name = toolName.toLowerCase().replace(/^clawtool_/, '');
645
+ if (name === 'edit' || name === 'write')
646
+ return undefined;
647
+ if (result.trim().length === 0)
648
+ return undefined;
649
+ const lines = resultToLines(result);
650
+ // A single short line is already the summary — no need to repeat it.
651
+ return lines.length <= 1 ? undefined : lines;
652
+ }
653
+ /** Concise one-line summary of a tool result for the `⎿` line. */
654
+ function firstLine(result) {
655
+ const payload = payloadString(result);
656
+ if (payload !== null) {
657
+ return truncate(payload.split('\n').find((l) => l.trim().length > 0) ?? '', 120);
658
+ }
659
+ const obj = parseJsonObject(result);
660
+ if (obj) {
661
+ if (obj.success === false && typeof obj.error === 'string')
662
+ return truncate(obj.error, 120);
663
+ const keys = Object.keys(obj);
664
+ return keys.length > 0
665
+ ? `{ ${keys.slice(0, 6).join(', ')}${keys.length > 6 ? ', …' : ''} }`
666
+ : '{}';
667
+ }
668
+ const cleaned = cleanToolText(result.trim());
669
+ return truncate(cleaned.split('\n').find((l) => l.trim().length > 0) ?? '', 120);
670
+ }
671
+ function emptySession(errorHint) {
672
+ return {
673
+ hasProvider: false,
674
+ providerSummary: null,
675
+ modelSummary: null,
676
+ toolNames: [],
677
+ deferredToolCount: 0,
678
+ errorHint,
679
+ send: async function* () {
680
+ yield { kind: 'error', message: errorHint };
681
+ },
682
+ };
683
+ }
684
+ //# sourceMappingURL=agent.js.map