@calliopelabs/cli 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (856) hide show
  1. package/README.md +54 -7
  2. package/dist/acp.d.ts +1 -4
  3. package/dist/acp.d.ts.map +1 -1
  4. package/dist/acp.js +118 -217
  5. package/dist/acp.js.map +1 -1
  6. package/dist/approvals/commands.d.ts +35 -0
  7. package/dist/approvals/commands.d.ts.map +1 -0
  8. package/dist/approvals/commands.js +51 -0
  9. package/dist/approvals/commands.js.map +1 -0
  10. package/dist/approvals/index.d.ts +6 -0
  11. package/dist/approvals/index.d.ts.map +1 -0
  12. package/dist/approvals/index.js +6 -0
  13. package/dist/approvals/index.js.map +1 -0
  14. package/dist/approvals/queue.d.ts +18 -0
  15. package/dist/approvals/queue.d.ts.map +1 -0
  16. package/dist/approvals/queue.js +46 -0
  17. package/dist/approvals/queue.js.map +1 -0
  18. package/dist/approvals/request.d.ts +13 -0
  19. package/dist/approvals/request.d.ts.map +1 -0
  20. package/dist/approvals/request.js +102 -0
  21. package/dist/approvals/request.js.map +1 -0
  22. package/dist/approvals/store.d.ts +24 -0
  23. package/dist/approvals/store.d.ts.map +1 -0
  24. package/dist/approvals/store.js +229 -0
  25. package/dist/approvals/store.js.map +1 -0
  26. package/dist/approvals/types.d.ts +27 -0
  27. package/dist/approvals/types.d.ts.map +1 -0
  28. package/dist/approvals/types.js +4 -0
  29. package/dist/approvals/types.js.map +1 -0
  30. package/dist/auto-compressor.d.ts +3 -2
  31. package/dist/auto-compressor.d.ts.map +1 -1
  32. package/dist/auto-compressor.js +9 -9
  33. package/dist/auto-compressor.js.map +1 -1
  34. package/dist/bin.d.ts.map +1 -1
  35. package/dist/bin.js +125 -22
  36. package/dist/bin.js.map +1 -1
  37. package/dist/brain/access.d.ts +65 -0
  38. package/dist/brain/access.d.ts.map +1 -0
  39. package/dist/brain/access.js +216 -0
  40. package/dist/brain/access.js.map +1 -0
  41. package/dist/brain/actions.d.ts +120 -0
  42. package/dist/brain/actions.d.ts.map +1 -0
  43. package/dist/brain/actions.js +270 -0
  44. package/dist/brain/actions.js.map +1 -0
  45. package/dist/brain/cli.d.ts +17 -0
  46. package/dist/brain/cli.d.ts.map +1 -0
  47. package/dist/brain/cli.js +206 -0
  48. package/dist/brain/cli.js.map +1 -0
  49. package/dist/brain/index.d.ts +16 -0
  50. package/dist/brain/index.d.ts.map +1 -0
  51. package/dist/brain/index.js +16 -0
  52. package/dist/brain/index.js.map +1 -0
  53. package/dist/brain/journal.d.ts +17 -0
  54. package/dist/brain/journal.d.ts.map +1 -0
  55. package/dist/brain/journal.js +189 -0
  56. package/dist/brain/journal.js.map +1 -0
  57. package/dist/brain/kg.d.ts +23 -0
  58. package/dist/brain/kg.d.ts.map +1 -0
  59. package/dist/brain/kg.js +36 -0
  60. package/dist/brain/kg.js.map +1 -0
  61. package/dist/brain/presentation.d.ts +3 -0
  62. package/dist/brain/presentation.d.ts.map +1 -0
  63. package/dist/brain/presentation.js +48 -0
  64. package/dist/brain/presentation.js.map +1 -0
  65. package/dist/brain/queries.d.ts +179 -0
  66. package/dist/brain/queries.d.ts.map +1 -0
  67. package/dist/brain/queries.js +210 -0
  68. package/dist/brain/queries.js.map +1 -0
  69. package/dist/brain/run-ingest.d.ts +29 -0
  70. package/dist/brain/run-ingest.d.ts.map +1 -0
  71. package/dist/brain/run-ingest.js +200 -0
  72. package/dist/brain/run-ingest.js.map +1 -0
  73. package/dist/brain/sqlite.d.ts +19 -0
  74. package/dist/brain/sqlite.d.ts.map +1 -0
  75. package/dist/brain/sqlite.js +252 -0
  76. package/dist/brain/sqlite.js.map +1 -0
  77. package/dist/brain/store.d.ts +31 -0
  78. package/dist/brain/store.d.ts.map +1 -0
  79. package/dist/brain/store.js +324 -0
  80. package/dist/brain/store.js.map +1 -0
  81. package/dist/brain/tool-query.d.ts +6 -0
  82. package/dist/brain/tool-query.d.ts.map +1 -0
  83. package/dist/brain/tool-query.js +42 -0
  84. package/dist/brain/tool-query.js.map +1 -0
  85. package/dist/brain/tools.d.ts +13 -0
  86. package/dist/brain/tools.d.ts.map +1 -0
  87. package/dist/brain/tools.js +20 -0
  88. package/dist/brain/tools.js.map +1 -0
  89. package/dist/brain/transfer.d.ts +57 -0
  90. package/dist/brain/transfer.d.ts.map +1 -0
  91. package/dist/brain/transfer.js +266 -0
  92. package/dist/brain/transfer.js.map +1 -0
  93. package/dist/brain/types.d.ts +145 -0
  94. package/dist/brain/types.d.ts.map +1 -0
  95. package/dist/brain/types.js +38 -0
  96. package/dist/brain/types.js.map +1 -0
  97. package/dist/brain/validation.d.ts +15 -0
  98. package/dist/brain/validation.d.ts.map +1 -0
  99. package/dist/brain/validation.js +163 -0
  100. package/dist/brain/validation.js.map +1 -0
  101. package/dist/budget.d.ts +5 -4
  102. package/dist/budget.d.ts.map +1 -1
  103. package/dist/budget.js +16 -38
  104. package/dist/budget.js.map +1 -1
  105. package/dist/cancellation.d.ts +8 -0
  106. package/dist/cancellation.d.ts.map +1 -0
  107. package/dist/cancellation.js +42 -0
  108. package/dist/cancellation.js.map +1 -0
  109. package/dist/checkpoint.d.ts +2 -2
  110. package/dist/checkpoint.d.ts.map +1 -1
  111. package/dist/checkpoint.js +5 -5
  112. package/dist/checkpoint.js.map +1 -1
  113. package/dist/config.d.ts +13 -0
  114. package/dist/config.d.ts.map +1 -1
  115. package/dist/config.js +32 -8
  116. package/dist/config.js.map +1 -1
  117. package/dist/doctor.d.ts +30 -0
  118. package/dist/doctor.d.ts.map +1 -0
  119. package/dist/doctor.js +124 -0
  120. package/dist/doctor.js.map +1 -0
  121. package/dist/errors.d.ts +15 -0
  122. package/dist/errors.d.ts.map +1 -1
  123. package/dist/errors.js +33 -9
  124. package/dist/errors.js.map +1 -1
  125. package/dist/execution/attribution.d.ts +17 -0
  126. package/dist/execution/attribution.d.ts.map +1 -0
  127. package/dist/execution/attribution.js +21 -0
  128. package/dist/execution/attribution.js.map +1 -0
  129. package/dist/execution/authority.d.ts +22 -0
  130. package/dist/execution/authority.d.ts.map +1 -0
  131. package/dist/execution/authority.js +134 -0
  132. package/dist/execution/authority.js.map +1 -0
  133. package/dist/execution/billing.d.ts +100 -0
  134. package/dist/execution/billing.d.ts.map +1 -0
  135. package/dist/execution/billing.js +166 -0
  136. package/dist/execution/billing.js.map +1 -0
  137. package/dist/execution/child-grants.d.ts +9 -0
  138. package/dist/execution/child-grants.d.ts.map +1 -0
  139. package/dist/execution/child-grants.js +55 -0
  140. package/dist/execution/child-grants.js.map +1 -0
  141. package/dist/execution/files.d.ts +6 -0
  142. package/dist/execution/files.d.ts.map +1 -0
  143. package/dist/execution/files.js +159 -0
  144. package/dist/execution/files.js.map +1 -0
  145. package/dist/execution/guard.d.ts +50 -0
  146. package/dist/execution/guard.d.ts.map +1 -0
  147. package/dist/execution/guard.js +158 -0
  148. package/dist/execution/guard.js.map +1 -0
  149. package/dist/execution/index.d.ts +12 -0
  150. package/dist/execution/index.d.ts.map +1 -0
  151. package/dist/execution/index.js +12 -0
  152. package/dist/execution/index.js.map +1 -0
  153. package/dist/execution/ledger.d.ts +23 -0
  154. package/dist/execution/ledger.d.ts.map +1 -0
  155. package/dist/execution/ledger.js +379 -0
  156. package/dist/execution/ledger.js.map +1 -0
  157. package/dist/execution/project-spend.d.ts +45 -0
  158. package/dist/execution/project-spend.d.ts.map +1 -0
  159. package/dist/execution/project-spend.js +274 -0
  160. package/dist/execution/project-spend.js.map +1 -0
  161. package/dist/execution/quote.d.ts +19 -0
  162. package/dist/execution/quote.d.ts.map +1 -0
  163. package/dist/execution/quote.js +116 -0
  164. package/dist/execution/quote.js.map +1 -0
  165. package/dist/execution/types.d.ts +113 -0
  166. package/dist/execution/types.d.ts.map +1 -0
  167. package/dist/execution/types.js +9 -0
  168. package/dist/execution/types.js.map +1 -0
  169. package/dist/execution/writer-recovery.d.ts +3 -0
  170. package/dist/execution/writer-recovery.d.ts.map +1 -0
  171. package/dist/execution/writer-recovery.js +85 -0
  172. package/dist/execution/writer-recovery.js.map +1 -0
  173. package/dist/goals/accounting.d.ts +69 -0
  174. package/dist/goals/accounting.d.ts.map +1 -0
  175. package/dist/goals/accounting.js +100 -0
  176. package/dist/goals/accounting.js.map +1 -0
  177. package/dist/goals/actions.d.ts +33 -0
  178. package/dist/goals/actions.d.ts.map +1 -0
  179. package/dist/goals/actions.js +261 -0
  180. package/dist/goals/actions.js.map +1 -0
  181. package/dist/goals/authority.d.ts +9 -0
  182. package/dist/goals/authority.d.ts.map +1 -0
  183. package/dist/goals/authority.js +47 -0
  184. package/dist/goals/authority.js.map +1 -0
  185. package/dist/goals/cli.d.ts +10 -0
  186. package/dist/goals/cli.d.ts.map +1 -0
  187. package/dist/goals/cli.js +169 -0
  188. package/dist/goals/cli.js.map +1 -0
  189. package/dist/goals/contracts.d.ts +18 -0
  190. package/dist/goals/contracts.d.ts.map +1 -0
  191. package/dist/goals/contracts.js +127 -0
  192. package/dist/goals/contracts.js.map +1 -0
  193. package/dist/goals/index.d.ts +17 -0
  194. package/dist/goals/index.d.ts.map +1 -0
  195. package/dist/goals/index.js +16 -0
  196. package/dist/goals/index.js.map +1 -0
  197. package/dist/goals/journal.d.ts +5 -0
  198. package/dist/goals/journal.d.ts.map +1 -0
  199. package/dist/goals/journal.js +142 -0
  200. package/dist/goals/journal.js.map +1 -0
  201. package/dist/goals/metrics.d.ts +53 -0
  202. package/dist/goals/metrics.d.ts.map +1 -0
  203. package/dist/goals/metrics.js +141 -0
  204. package/dist/goals/metrics.js.map +1 -0
  205. package/dist/goals/repair.d.ts +10 -0
  206. package/dist/goals/repair.d.ts.map +1 -0
  207. package/dist/goals/repair.js +50 -0
  208. package/dist/goals/repair.js.map +1 -0
  209. package/dist/goals/routing.d.ts +18 -0
  210. package/dist/goals/routing.d.ts.map +1 -0
  211. package/dist/goals/routing.js +60 -0
  212. package/dist/goals/routing.js.map +1 -0
  213. package/dist/goals/store.d.ts +30 -0
  214. package/dist/goals/store.d.ts.map +1 -0
  215. package/dist/goals/store.js +314 -0
  216. package/dist/goals/store.js.map +1 -0
  217. package/dist/goals/supervised-cli.d.ts +5 -0
  218. package/dist/goals/supervised-cli.d.ts.map +1 -0
  219. package/dist/goals/supervised-cli.js +44 -0
  220. package/dist/goals/supervised-cli.js.map +1 -0
  221. package/dist/goals/supervised.d.ts +8 -0
  222. package/dist/goals/supervised.d.ts.map +1 -0
  223. package/dist/goals/supervised.js +65 -0
  224. package/dist/goals/supervised.js.map +1 -0
  225. package/dist/goals/team.d.ts +8 -0
  226. package/dist/goals/team.d.ts.map +1 -0
  227. package/dist/goals/team.js +64 -0
  228. package/dist/goals/team.js.map +1 -0
  229. package/dist/goals/types.d.ts +174 -0
  230. package/dist/goals/types.d.ts.map +1 -0
  231. package/dist/goals/types.js +2 -0
  232. package/dist/goals/types.js.map +1 -0
  233. package/dist/goals/validation.d.ts +17 -0
  234. package/dist/goals/validation.d.ts.map +1 -0
  235. package/dist/goals/validation.js +172 -0
  236. package/dist/goals/validation.js.map +1 -0
  237. package/dist/headless.d.ts +1 -0
  238. package/dist/headless.d.ts.map +1 -1
  239. package/dist/headless.js +98 -275
  240. package/dist/headless.js.map +1 -1
  241. package/dist/health/index.d.ts +5 -0
  242. package/dist/health/index.d.ts.map +1 -0
  243. package/dist/health/index.js +5 -0
  244. package/dist/health/index.js.map +1 -0
  245. package/dist/health/store.d.ts +24 -0
  246. package/dist/health/store.d.ts.map +1 -0
  247. package/dist/health/store.js +190 -0
  248. package/dist/health/store.js.map +1 -0
  249. package/dist/health/summary.d.ts +10 -0
  250. package/dist/health/summary.d.ts.map +1 -0
  251. package/dist/health/summary.js +95 -0
  252. package/dist/health/summary.js.map +1 -0
  253. package/dist/health/target.d.ts +3 -0
  254. package/dist/health/target.d.ts.map +1 -0
  255. package/dist/health/target.js +53 -0
  256. package/dist/health/target.js.map +1 -0
  257. package/dist/health/types.d.ts +72 -0
  258. package/dist/health/types.d.ts.map +1 -0
  259. package/dist/health/types.js +2 -0
  260. package/dist/health/types.js.map +1 -0
  261. package/dist/hooks.d.ts +7 -2
  262. package/dist/hooks.d.ts.map +1 -1
  263. package/dist/hooks.js +35 -17
  264. package/dist/hooks.js.map +1 -1
  265. package/dist/improvement/actions.d.ts +27 -0
  266. package/dist/improvement/actions.d.ts.map +1 -0
  267. package/dist/improvement/actions.js +116 -0
  268. package/dist/improvement/actions.js.map +1 -0
  269. package/dist/improvement/cli.d.ts +9 -0
  270. package/dist/improvement/cli.d.ts.map +1 -0
  271. package/dist/improvement/cli.js +58 -0
  272. package/dist/improvement/cli.js.map +1 -0
  273. package/dist/improvement/feedback.d.ts +30 -0
  274. package/dist/improvement/feedback.d.ts.map +1 -0
  275. package/dist/improvement/feedback.js +7 -0
  276. package/dist/improvement/feedback.js.map +1 -0
  277. package/dist/improvement/index.d.ts +7 -0
  278. package/dist/improvement/index.d.ts.map +1 -0
  279. package/dist/improvement/index.js +7 -0
  280. package/dist/improvement/index.js.map +1 -0
  281. package/dist/improvement/metrics.d.ts +4 -0
  282. package/dist/improvement/metrics.d.ts.map +1 -0
  283. package/dist/improvement/metrics.js +38 -0
  284. package/dist/improvement/metrics.js.map +1 -0
  285. package/dist/improvement/projection.d.ts +7 -0
  286. package/dist/improvement/projection.d.ts.map +1 -0
  287. package/dist/improvement/projection.js +135 -0
  288. package/dist/improvement/projection.js.map +1 -0
  289. package/dist/improvement/types.d.ts +115 -0
  290. package/dist/improvement/types.d.ts.map +1 -0
  291. package/dist/improvement/types.js +2 -0
  292. package/dist/improvement/types.js.map +1 -0
  293. package/dist/index.d.ts +3 -0
  294. package/dist/index.d.ts.map +1 -1
  295. package/dist/index.js +3 -0
  296. package/dist/index.js.map +1 -1
  297. package/dist/instructions.d.ts +10 -0
  298. package/dist/instructions.d.ts.map +1 -0
  299. package/dist/instructions.js +93 -0
  300. package/dist/instructions.js.map +1 -0
  301. package/dist/isolation/contracts.d.ts +42 -0
  302. package/dist/isolation/contracts.d.ts.map +1 -0
  303. package/dist/isolation/contracts.js +33 -0
  304. package/dist/isolation/contracts.js.map +1 -0
  305. package/dist/isolation/coordinator.d.ts +10 -0
  306. package/dist/isolation/coordinator.d.ts.map +1 -0
  307. package/dist/isolation/coordinator.js +79 -0
  308. package/dist/isolation/coordinator.js.map +1 -0
  309. package/dist/isolation/index.d.ts +5 -0
  310. package/dist/isolation/index.d.ts.map +1 -0
  311. package/dist/isolation/index.js +5 -0
  312. package/dist/isolation/index.js.map +1 -0
  313. package/dist/isolation/process.d.ts +11 -0
  314. package/dist/isolation/process.d.ts.map +1 -0
  315. package/dist/isolation/process.js +131 -0
  316. package/dist/isolation/process.js.map +1 -0
  317. package/dist/isolation/worktree.d.ts +25 -0
  318. package/dist/isolation/worktree.d.ts.map +1 -0
  319. package/dist/isolation/worktree.js +282 -0
  320. package/dist/isolation/worktree.js.map +1 -0
  321. package/dist/memory.d.ts.map +1 -1
  322. package/dist/memory.js +5 -1
  323. package/dist/memory.js.map +1 -1
  324. package/dist/model-detection.d.ts +14 -18
  325. package/dist/model-detection.d.ts.map +1 -1
  326. package/dist/model-detection.js +287 -149
  327. package/dist/model-detection.js.map +1 -1
  328. package/dist/models/index.d.ts +3 -0
  329. package/dist/models/index.d.ts.map +1 -0
  330. package/dist/models/index.js +3 -0
  331. package/dist/models/index.js.map +1 -0
  332. package/dist/models/metadata.d.ts +43 -0
  333. package/dist/models/metadata.d.ts.map +1 -0
  334. package/dist/models/metadata.js +72 -0
  335. package/dist/models/metadata.js.map +1 -0
  336. package/dist/models/openrouter.d.ts +6 -0
  337. package/dist/models/openrouter.d.ts.map +1 -0
  338. package/dist/models/openrouter.js +38 -0
  339. package/dist/models/openrouter.js.map +1 -0
  340. package/dist/orchestration/accounting.d.ts +35 -0
  341. package/dist/orchestration/accounting.d.ts.map +1 -0
  342. package/dist/orchestration/accounting.js +27 -0
  343. package/dist/orchestration/accounting.js.map +1 -0
  344. package/dist/orchestration/actions.d.ts +23 -0
  345. package/dist/orchestration/actions.d.ts.map +1 -0
  346. package/dist/orchestration/actions.js +65 -0
  347. package/dist/orchestration/actions.js.map +1 -0
  348. package/dist/orchestration/cli.d.ts +28 -0
  349. package/dist/orchestration/cli.d.ts.map +1 -0
  350. package/dist/orchestration/cli.js +178 -0
  351. package/dist/orchestration/cli.js.map +1 -0
  352. package/dist/orchestration/coordinator-actions.d.ts +15 -0
  353. package/dist/orchestration/coordinator-actions.d.ts.map +1 -0
  354. package/dist/orchestration/coordinator-actions.js +79 -0
  355. package/dist/orchestration/coordinator-actions.js.map +1 -0
  356. package/dist/orchestration/coordinator-types.d.ts +193 -0
  357. package/dist/orchestration/coordinator-types.d.ts.map +1 -0
  358. package/dist/orchestration/coordinator-types.js +2 -0
  359. package/dist/orchestration/coordinator-types.js.map +1 -0
  360. package/dist/orchestration/coordinator.d.ts +32 -0
  361. package/dist/orchestration/coordinator.d.ts.map +1 -0
  362. package/dist/orchestration/coordinator.js +405 -0
  363. package/dist/orchestration/coordinator.js.map +1 -0
  364. package/dist/orchestration/execution-cli.d.ts +11 -0
  365. package/dist/orchestration/execution-cli.d.ts.map +1 -0
  366. package/dist/orchestration/execution-cli.js +183 -0
  367. package/dist/orchestration/execution-cli.js.map +1 -0
  368. package/dist/orchestration/execution-journal.d.ts +15 -0
  369. package/dist/orchestration/execution-journal.d.ts.map +1 -0
  370. package/dist/orchestration/execution-journal.js +477 -0
  371. package/dist/orchestration/execution-journal.js.map +1 -0
  372. package/dist/orchestration/execution-store.d.ts +39 -0
  373. package/dist/orchestration/execution-store.d.ts.map +1 -0
  374. package/dist/orchestration/execution-store.js +325 -0
  375. package/dist/orchestration/execution-store.js.map +1 -0
  376. package/dist/orchestration/execution.d.ts +8 -0
  377. package/dist/orchestration/execution.d.ts.map +1 -0
  378. package/dist/orchestration/execution.js +47 -0
  379. package/dist/orchestration/execution.js.map +1 -0
  380. package/dist/orchestration/index.d.ts +20 -0
  381. package/dist/orchestration/index.d.ts.map +1 -0
  382. package/dist/orchestration/index.js +17 -0
  383. package/dist/orchestration/index.js.map +1 -0
  384. package/dist/orchestration/progress.d.ts +19 -0
  385. package/dist/orchestration/progress.d.ts.map +1 -0
  386. package/dist/orchestration/progress.js +20 -0
  387. package/dist/orchestration/progress.js.map +1 -0
  388. package/dist/orchestration/recovery.d.ts +4 -0
  389. package/dist/orchestration/recovery.d.ts.map +1 -0
  390. package/dist/orchestration/recovery.js +121 -0
  391. package/dist/orchestration/recovery.js.map +1 -0
  392. package/dist/orchestration/request-accounting.d.ts +35 -0
  393. package/dist/orchestration/request-accounting.d.ts.map +1 -0
  394. package/dist/orchestration/request-accounting.js +71 -0
  395. package/dist/orchestration/request-accounting.js.map +1 -0
  396. package/dist/orchestration/routing.d.ts +20 -0
  397. package/dist/orchestration/routing.d.ts.map +1 -0
  398. package/dist/orchestration/routing.js +38 -0
  399. package/dist/orchestration/routing.js.map +1 -0
  400. package/dist/orchestration/store.d.ts +26 -0
  401. package/dist/orchestration/store.d.ts.map +1 -0
  402. package/dist/orchestration/store.js +381 -0
  403. package/dist/orchestration/store.js.map +1 -0
  404. package/dist/orchestration/types.d.ts +201 -0
  405. package/dist/orchestration/types.d.ts.map +1 -0
  406. package/dist/orchestration/types.js +11 -0
  407. package/dist/orchestration/types.js.map +1 -0
  408. package/dist/orchestration/validation.d.ts +29 -0
  409. package/dist/orchestration/validation.d.ts.map +1 -0
  410. package/dist/orchestration/validation.js +440 -0
  411. package/dist/orchestration/validation.js.map +1 -0
  412. package/dist/orchestration/verification.d.ts +30 -0
  413. package/dist/orchestration/verification.d.ts.map +1 -0
  414. package/dist/orchestration/verification.js +204 -0
  415. package/dist/orchestration/verification.js.map +1 -0
  416. package/dist/orchestration/worker-context.d.ts +33 -0
  417. package/dist/orchestration/worker-context.d.ts.map +1 -0
  418. package/dist/orchestration/worker-context.js +31 -0
  419. package/dist/orchestration/worker-context.js.map +1 -0
  420. package/dist/policy.d.ts +1 -0
  421. package/dist/policy.d.ts.map +1 -1
  422. package/dist/policy.js +25 -5
  423. package/dist/policy.js.map +1 -1
  424. package/dist/preferences/controls.d.ts +21 -0
  425. package/dist/preferences/controls.d.ts.map +1 -0
  426. package/dist/preferences/controls.js +36 -0
  427. package/dist/preferences/controls.js.map +1 -0
  428. package/dist/preferences/flags.d.ts +8 -0
  429. package/dist/preferences/flags.d.ts.map +1 -0
  430. package/dist/preferences/flags.js +27 -0
  431. package/dist/preferences/flags.js.map +1 -0
  432. package/dist/preferences/index.d.ts +9 -0
  433. package/dist/preferences/index.d.ts.map +1 -0
  434. package/dist/preferences/index.js +9 -0
  435. package/dist/preferences/index.js.map +1 -0
  436. package/dist/preferences/once.d.ts +8 -0
  437. package/dist/preferences/once.d.ts.map +1 -0
  438. package/dist/preferences/once.js +28 -0
  439. package/dist/preferences/once.js.map +1 -0
  440. package/dist/preferences/resolve.d.ts +14 -0
  441. package/dist/preferences/resolve.d.ts.map +1 -0
  442. package/dist/preferences/resolve.js +58 -0
  443. package/dist/preferences/resolve.js.map +1 -0
  444. package/dist/preferences/store.d.ts +19 -0
  445. package/dist/preferences/store.d.ts.map +1 -0
  446. package/dist/preferences/store.js +147 -0
  447. package/dist/preferences/store.js.map +1 -0
  448. package/dist/preferences/submission.d.ts +16 -0
  449. package/dist/preferences/submission.d.ts.map +1 -0
  450. package/dist/preferences/submission.js +29 -0
  451. package/dist/preferences/submission.js.map +1 -0
  452. package/dist/preferences/types.d.ts +23 -0
  453. package/dist/preferences/types.d.ts.map +1 -0
  454. package/dist/preferences/types.js +2 -0
  455. package/dist/preferences/types.js.map +1 -0
  456. package/dist/preferences/validate.d.ts +3 -0
  457. package/dist/preferences/validate.d.ts.map +1 -0
  458. package/dist/preferences/validate.js +15 -0
  459. package/dist/preferences/validate.js.map +1 -0
  460. package/dist/process-cancellation.d.ts +6 -0
  461. package/dist/process-cancellation.d.ts.map +1 -0
  462. package/dist/process-cancellation.js +39 -0
  463. package/dist/process-cancellation.js.map +1 -0
  464. package/dist/providers/anthropic.d.ts +7 -2
  465. package/dist/providers/anthropic.d.ts.map +1 -1
  466. package/dist/providers/anthropic.js +72 -41
  467. package/dist/providers/anthropic.js.map +1 -1
  468. package/dist/providers/bedrock.d.ts +1 -1
  469. package/dist/providers/bedrock.d.ts.map +1 -1
  470. package/dist/providers/bedrock.js +39 -23
  471. package/dist/providers/bedrock.js.map +1 -1
  472. package/dist/providers/compat.d.ts +4 -3
  473. package/dist/providers/compat.d.ts.map +1 -1
  474. package/dist/providers/compat.js +49 -31
  475. package/dist/providers/compat.js.map +1 -1
  476. package/dist/providers/deepseek-reasoning.d.ts +64 -0
  477. package/dist/providers/deepseek-reasoning.d.ts.map +1 -0
  478. package/dist/providers/deepseek-reasoning.js +28 -0
  479. package/dist/providers/deepseek-reasoning.js.map +1 -0
  480. package/dist/providers/google.d.ts +4 -3
  481. package/dist/providers/google.d.ts.map +1 -1
  482. package/dist/providers/google.js +128 -19
  483. package/dist/providers/google.js.map +1 -1
  484. package/dist/providers/index.d.ts +2 -0
  485. package/dist/providers/index.d.ts.map +1 -1
  486. package/dist/providers/index.js +216 -36
  487. package/dist/providers/index.js.map +1 -1
  488. package/dist/providers/ollama.d.ts.map +1 -1
  489. package/dist/providers/ollama.js +79 -43
  490. package/dist/providers/ollama.js.map +1 -1
  491. package/dist/providers/openai.d.ts +2 -2
  492. package/dist/providers/openai.d.ts.map +1 -1
  493. package/dist/providers/openai.js +50 -46
  494. package/dist/providers/openai.js.map +1 -1
  495. package/dist/providers/openrouter-bounds.d.ts +22 -0
  496. package/dist/providers/openrouter-bounds.d.ts.map +1 -0
  497. package/dist/providers/openrouter-bounds.js +16 -0
  498. package/dist/providers/openrouter-bounds.js.map +1 -0
  499. package/dist/providers/stream-attempt.d.ts +29 -0
  500. package/dist/providers/stream-attempt.d.ts.map +1 -0
  501. package/dist/providers/stream-attempt.js +63 -0
  502. package/dist/providers/stream-attempt.js.map +1 -0
  503. package/dist/providers/types.d.ts +40 -4
  504. package/dist/providers/types.d.ts.map +1 -1
  505. package/dist/providers/types.js +19 -0
  506. package/dist/providers/types.js.map +1 -1
  507. package/dist/replay.d.ts.map +1 -1
  508. package/dist/replay.js +16 -0
  509. package/dist/replay.js.map +1 -1
  510. package/dist/risk.d.ts.map +1 -1
  511. package/dist/risk.js +5 -0
  512. package/dist/risk.js.map +1 -1
  513. package/dist/router.d.ts +7 -110
  514. package/dist/router.d.ts.map +1 -1
  515. package/dist/router.js +0 -451
  516. package/dist/router.js.map +1 -1
  517. package/dist/routing/index.d.ts +6 -0
  518. package/dist/routing/index.d.ts.map +1 -0
  519. package/dist/routing/index.js +5 -0
  520. package/dist/routing/index.js.map +1 -0
  521. package/dist/routing/prompt.d.ts +4 -0
  522. package/dist/routing/prompt.d.ts.map +1 -0
  523. package/dist/routing/prompt.js +14 -0
  524. package/dist/routing/prompt.js.map +1 -0
  525. package/dist/routing/select.d.ts +9 -0
  526. package/dist/routing/select.d.ts.map +1 -0
  527. package/dist/routing/select.js +279 -0
  528. package/dist/routing/select.js.map +1 -0
  529. package/dist/routing/smart.d.ts +31 -0
  530. package/dist/routing/smart.d.ts.map +1 -0
  531. package/dist/routing/smart.js +46 -0
  532. package/dist/routing/smart.js.map +1 -0
  533. package/dist/routing/types.d.ts +79 -0
  534. package/dist/routing/types.d.ts.map +1 -0
  535. package/dist/routing/types.js +2 -0
  536. package/dist/routing/types.js.map +1 -0
  537. package/dist/runlog.d.ts +32 -4
  538. package/dist/runlog.d.ts.map +1 -1
  539. package/dist/runlog.js +22 -6
  540. package/dist/runlog.js.map +1 -1
  541. package/dist/runtime/index.d.ts +6 -0
  542. package/dist/runtime/index.d.ts.map +1 -0
  543. package/dist/runtime/index.js +6 -0
  544. package/dist/runtime/index.js.map +1 -0
  545. package/dist/runtime/permissions.d.ts +21 -0
  546. package/dist/runtime/permissions.d.ts.map +1 -0
  547. package/dist/runtime/permissions.js +110 -0
  548. package/dist/runtime/permissions.js.map +1 -0
  549. package/dist/runtime/repair.d.ts +20 -0
  550. package/dist/runtime/repair.d.ts.map +1 -0
  551. package/dist/runtime/repair.js +50 -0
  552. package/dist/runtime/repair.js.map +1 -0
  553. package/dist/runtime/tool-retry.d.ts +3 -0
  554. package/dist/runtime/tool-retry.d.ts.map +1 -0
  555. package/dist/runtime/tool-retry.js +12 -0
  556. package/dist/runtime/tool-retry.js.map +1 -0
  557. package/dist/runtime/turn.d.ts +97 -0
  558. package/dist/runtime/turn.d.ts.map +1 -0
  559. package/dist/runtime/turn.js +437 -0
  560. package/dist/runtime/turn.js.map +1 -0
  561. package/dist/runtime/types.d.ts +11 -0
  562. package/dist/runtime/types.d.ts.map +1 -0
  563. package/dist/runtime/types.js +5 -0
  564. package/dist/runtime/types.js.map +1 -0
  565. package/dist/sandbox/docker.d.ts +4 -4
  566. package/dist/sandbox/docker.d.ts.map +1 -1
  567. package/dist/sandbox/docker.js +55 -16
  568. package/dist/sandbox/docker.js.map +1 -1
  569. package/dist/sandbox/native.d.ts +1 -0
  570. package/dist/sandbox/native.d.ts.map +1 -1
  571. package/dist/sandbox/native.js +11 -2
  572. package/dist/sandbox/native.js.map +1 -1
  573. package/dist/scope.d.ts +5 -9
  574. package/dist/scope.d.ts.map +1 -1
  575. package/dist/scope.js +43 -20
  576. package/dist/scope.js.map +1 -1
  577. package/dist/session-management/actions.d.ts +28 -0
  578. package/dist/session-management/actions.d.ts.map +1 -0
  579. package/dist/session-management/actions.js +78 -0
  580. package/dist/session-management/actions.js.map +1 -0
  581. package/dist/session-management/cli.d.ts +24 -0
  582. package/dist/session-management/cli.d.ts.map +1 -0
  583. package/dist/session-management/cli.js +92 -0
  584. package/dist/session-management/cli.js.map +1 -0
  585. package/dist/session-management/files.d.ts +5 -0
  586. package/dist/session-management/files.d.ts.map +1 -0
  587. package/dist/session-management/files.js +92 -0
  588. package/dist/session-management/files.js.map +1 -0
  589. package/dist/session-management/index.d.ts +7 -0
  590. package/dist/session-management/index.d.ts.map +1 -0
  591. package/dist/session-management/index.js +7 -0
  592. package/dist/session-management/index.js.map +1 -0
  593. package/dist/session-management/permissions.d.ts +12 -0
  594. package/dist/session-management/permissions.d.ts.map +1 -0
  595. package/dist/session-management/permissions.js +33 -0
  596. package/dist/session-management/permissions.js.map +1 -0
  597. package/dist/session-management/presentation.d.ts +5 -0
  598. package/dist/session-management/presentation.d.ts.map +1 -0
  599. package/dist/session-management/presentation.js +10 -0
  600. package/dist/session-management/presentation.js.map +1 -0
  601. package/dist/session-management/transfer.d.ts +9 -0
  602. package/dist/session-management/transfer.d.ts.map +1 -0
  603. package/dist/session-management/transfer.js +39 -0
  604. package/dist/session-management/transfer.js.map +1 -0
  605. package/dist/sessions/context.d.ts +4 -0
  606. package/dist/sessions/context.d.ts.map +1 -0
  607. package/dist/sessions/context.js +6 -0
  608. package/dist/sessions/context.js.map +1 -0
  609. package/dist/sessions/format.d.ts +38 -0
  610. package/dist/sessions/format.d.ts.map +1 -0
  611. package/dist/sessions/format.js +116 -0
  612. package/dist/sessions/format.js.map +1 -0
  613. package/dist/sessions/history.d.ts +37 -0
  614. package/dist/sessions/history.d.ts.map +1 -0
  615. package/dist/sessions/history.js +197 -0
  616. package/dist/sessions/history.js.map +1 -0
  617. package/dist/sessions/index.d.ts +8 -0
  618. package/dist/sessions/index.d.ts.map +1 -0
  619. package/dist/sessions/index.js +8 -0
  620. package/dist/sessions/index.js.map +1 -0
  621. package/dist/sessions/output.d.ts +31 -0
  622. package/dist/sessions/output.d.ts.map +1 -0
  623. package/dist/sessions/output.js +115 -0
  624. package/dist/sessions/output.js.map +1 -0
  625. package/dist/sessions/portable.d.ts +26 -0
  626. package/dist/sessions/portable.d.ts.map +1 -0
  627. package/dist/sessions/portable.js +105 -0
  628. package/dist/sessions/portable.js.map +1 -0
  629. package/dist/sessions/snapshot.d.ts +11 -0
  630. package/dist/sessions/snapshot.d.ts.map +1 -0
  631. package/dist/sessions/snapshot.js +118 -0
  632. package/dist/sessions/snapshot.js.map +1 -0
  633. package/dist/sessions/tool-state.d.ts +11 -0
  634. package/dist/sessions/tool-state.d.ts.map +1 -0
  635. package/dist/sessions/tool-state.js +152 -0
  636. package/dist/sessions/tool-state.js.map +1 -0
  637. package/dist/setup.d.ts.map +1 -1
  638. package/dist/setup.js +13 -5
  639. package/dist/setup.js.map +1 -1
  640. package/dist/spawning/actions.d.ts +23 -0
  641. package/dist/spawning/actions.d.ts.map +1 -0
  642. package/dist/spawning/actions.js +120 -0
  643. package/dist/spawning/actions.js.map +1 -0
  644. package/dist/spawning/authority.d.ts +15 -0
  645. package/dist/spawning/authority.d.ts.map +1 -0
  646. package/dist/spawning/authority.js +16 -0
  647. package/dist/spawning/authority.js.map +1 -0
  648. package/dist/spawning/cli.d.ts +4 -0
  649. package/dist/spawning/cli.d.ts.map +1 -0
  650. package/dist/spawning/cli.js +46 -0
  651. package/dist/spawning/cli.js.map +1 -0
  652. package/dist/spawning/execution.d.ts +13 -0
  653. package/dist/spawning/execution.d.ts.map +1 -0
  654. package/dist/spawning/execution.js +59 -0
  655. package/dist/spawning/execution.js.map +1 -0
  656. package/dist/spawning/index.d.ts +8 -0
  657. package/dist/spawning/index.d.ts.map +1 -0
  658. package/dist/spawning/index.js +8 -0
  659. package/dist/spawning/index.js.map +1 -0
  660. package/dist/spawning/store.d.ts +12 -0
  661. package/dist/spawning/store.d.ts.map +1 -0
  662. package/dist/spawning/store.js +93 -0
  663. package/dist/spawning/store.js.map +1 -0
  664. package/dist/spawning/types.d.ts +34 -0
  665. package/dist/spawning/types.d.ts.map +1 -0
  666. package/dist/spawning/types.js +2 -0
  667. package/dist/spawning/types.js.map +1 -0
  668. package/dist/spawning/validation.d.ts +14 -0
  669. package/dist/spawning/validation.d.ts.map +1 -0
  670. package/dist/spawning/validation.js +95 -0
  671. package/dist/spawning/validation.js.map +1 -0
  672. package/dist/storage.d.ts +29 -1
  673. package/dist/storage.d.ts.map +1 -1
  674. package/dist/storage.js +112 -40
  675. package/dist/storage.js.map +1 -1
  676. package/dist/supervision/approval.d.ts +4 -0
  677. package/dist/supervision/approval.d.ts.map +1 -0
  678. package/dist/supervision/approval.js +7 -0
  679. package/dist/supervision/approval.js.map +1 -0
  680. package/dist/supervision/availability.d.ts +34 -0
  681. package/dist/supervision/availability.d.ts.map +1 -0
  682. package/dist/supervision/availability.js +44 -0
  683. package/dist/supervision/availability.js.map +1 -0
  684. package/dist/supervision/context.d.ts +36 -0
  685. package/dist/supervision/context.d.ts.map +1 -0
  686. package/dist/supervision/context.js +52 -0
  687. package/dist/supervision/context.js.map +1 -0
  688. package/dist/supervision/contracts.d.ts +7 -0
  689. package/dist/supervision/contracts.d.ts.map +1 -0
  690. package/dist/supervision/contracts.js +86 -0
  691. package/dist/supervision/contracts.js.map +1 -0
  692. package/dist/supervision/effects.d.ts +15 -0
  693. package/dist/supervision/effects.d.ts.map +1 -0
  694. package/dist/supervision/effects.js +19 -0
  695. package/dist/supervision/effects.js.map +1 -0
  696. package/dist/supervision/evidence.d.ts +32 -0
  697. package/dist/supervision/evidence.d.ts.map +1 -0
  698. package/dist/supervision/evidence.js +68 -0
  699. package/dist/supervision/evidence.js.map +1 -0
  700. package/dist/supervision/health.d.ts +16 -0
  701. package/dist/supervision/health.d.ts.map +1 -0
  702. package/dist/supervision/health.js +86 -0
  703. package/dist/supervision/health.js.map +1 -0
  704. package/dist/supervision/index.d.ts +13 -0
  705. package/dist/supervision/index.d.ts.map +1 -0
  706. package/dist/supervision/index.js +13 -0
  707. package/dist/supervision/index.js.map +1 -0
  708. package/dist/supervision/journal.d.ts +16 -0
  709. package/dist/supervision/journal.d.ts.map +1 -0
  710. package/dist/supervision/journal.js +311 -0
  711. package/dist/supervision/journal.js.map +1 -0
  712. package/dist/supervision/receipts.d.ts +4 -0
  713. package/dist/supervision/receipts.d.ts.map +1 -0
  714. package/dist/supervision/receipts.js +21 -0
  715. package/dist/supervision/receipts.js.map +1 -0
  716. package/dist/supervision/reply.d.ts +12 -0
  717. package/dist/supervision/reply.d.ts.map +1 -0
  718. package/dist/supervision/reply.js +50 -0
  719. package/dist/supervision/reply.js.map +1 -0
  720. package/dist/supervision/runtime.d.ts +8 -0
  721. package/dist/supervision/runtime.d.ts.map +1 -0
  722. package/dist/supervision/runtime.js +188 -0
  723. package/dist/supervision/runtime.js.map +1 -0
  724. package/dist/supervision/types.d.ts +174 -0
  725. package/dist/supervision/types.d.ts.map +1 -0
  726. package/dist/supervision/types.js +2 -0
  727. package/dist/supervision/types.js.map +1 -0
  728. package/dist/tools.d.ts +12 -0
  729. package/dist/tools.d.ts.map +1 -1
  730. package/dist/tools.js +131 -23
  731. package/dist/tools.js.map +1 -1
  732. package/dist/turn-controller.d.ts +12 -0
  733. package/dist/turn-controller.d.ts.map +1 -0
  734. package/dist/turn-controller.js +51 -0
  735. package/dist/turn-controller.js.map +1 -0
  736. package/dist/types.d.ts +7 -1
  737. package/dist/types.d.ts.map +1 -1
  738. package/dist/types.js +3 -0
  739. package/dist/types.js.map +1 -1
  740. package/dist/ui/agent.d.ts +11 -10
  741. package/dist/ui/agent.d.ts.map +1 -1
  742. package/dist/ui/agent.js +293 -897
  743. package/dist/ui/agent.js.map +1 -1
  744. package/dist/ui/commands.d.ts +20 -2
  745. package/dist/ui/commands.d.ts.map +1 -1
  746. package/dist/ui/commands.js +208 -155
  747. package/dist/ui/commands.js.map +1 -1
  748. package/dist/ui/completions.d.ts.map +1 -1
  749. package/dist/ui/completions.js +16 -5
  750. package/dist/ui/completions.js.map +1 -1
  751. package/dist/ui/index.d.ts +6 -2
  752. package/dist/ui/index.d.ts.map +1 -1
  753. package/dist/ui/index.js +13 -11
  754. package/dist/ui/index.js.map +1 -1
  755. package/dist/ui/messages.d.ts.map +1 -1
  756. package/dist/ui/messages.js +5 -0
  757. package/dist/ui/messages.js.map +1 -1
  758. package/dist/ui/modals/index.d.ts +1 -0
  759. package/dist/ui/modals/index.d.ts.map +1 -1
  760. package/dist/ui/modals/index.js +1 -0
  761. package/dist/ui/modals/index.js.map +1 -1
  762. package/dist/ui/modals/prompts.d.ts +4 -7
  763. package/dist/ui/modals/prompts.d.ts.map +1 -1
  764. package/dist/ui/modals/prompts.js +14 -10
  765. package/dist/ui/modals/prompts.js.map +1 -1
  766. package/dist/ui/modals/selectors.d.ts +1 -0
  767. package/dist/ui/modals/selectors.d.ts.map +1 -1
  768. package/dist/ui/modals/selectors.js +3 -2
  769. package/dist/ui/modals/selectors.js.map +1 -1
  770. package/dist/ui/modals/tool-output.d.ts +8 -0
  771. package/dist/ui/modals/tool-output.d.ts.map +1 -0
  772. package/dist/ui/modals/tool-output.js +26 -0
  773. package/dist/ui/modals/tool-output.js.map +1 -0
  774. package/dist/ui/model-commands.d.ts +4 -0
  775. package/dist/ui/model-commands.d.ts.map +1 -0
  776. package/dist/ui/model-commands.js +69 -0
  777. package/dist/ui/model-commands.js.map +1 -0
  778. package/dist/ui/regions/modal-host.d.ts +3 -0
  779. package/dist/ui/regions/modal-host.d.ts.map +1 -1
  780. package/dist/ui/regions/modal-host.js +7 -1
  781. package/dist/ui/regions/modal-host.js.map +1 -1
  782. package/dist/ui/regions/status-region.d.ts +2 -1
  783. package/dist/ui/regions/status-region.d.ts.map +1 -1
  784. package/dist/ui/regions/status-region.js +2 -2
  785. package/dist/ui/regions/status-region.js.map +1 -1
  786. package/dist/ui/regions/tool-activity.d.ts +6 -0
  787. package/dist/ui/regions/tool-activity.d.ts.map +1 -0
  788. package/dist/ui/regions/tool-activity.js +9 -0
  789. package/dist/ui/regions/tool-activity.js.map +1 -0
  790. package/dist/ui/regions/transcript-region.d.ts.map +1 -1
  791. package/dist/ui/regions/transcript-region.js +2 -1
  792. package/dist/ui/regions/transcript-region.js.map +1 -1
  793. package/dist/ui/regions/workflow-region.d.ts +9 -0
  794. package/dist/ui/regions/workflow-region.d.ts.map +1 -0
  795. package/dist/ui/regions/workflow-region.js +11 -0
  796. package/dist/ui/regions/workflow-region.js.map +1 -0
  797. package/dist/ui/session-commands.d.ts +3 -0
  798. package/dist/ui/session-commands.d.ts.map +1 -0
  799. package/dist/ui/session-commands.js +141 -0
  800. package/dist/ui/session-commands.js.map +1 -0
  801. package/dist/ui/state/use-approval-state.d.ts +9 -0
  802. package/dist/ui/state/use-approval-state.d.ts.map +1 -0
  803. package/dist/ui/state/use-approval-state.js +18 -0
  804. package/dist/ui/state/use-approval-state.js.map +1 -0
  805. package/dist/ui/state/use-chat-controller.d.ts +4 -1
  806. package/dist/ui/state/use-chat-controller.d.ts.map +1 -1
  807. package/dist/ui/state/use-chat-controller.js +256 -180
  808. package/dist/ui/state/use-chat-controller.js.map +1 -1
  809. package/dist/ui/state/use-modal-state.d.ts +6 -8
  810. package/dist/ui/state/use-modal-state.d.ts.map +1 -1
  811. package/dist/ui/state/use-modal-state.js +3 -4
  812. package/dist/ui/state/use-modal-state.js.map +1 -1
  813. package/dist/ui/state/use-model-state.d.ts +7 -3
  814. package/dist/ui/state/use-model-state.d.ts.map +1 -1
  815. package/dist/ui/state/use-model-state.js +24 -17
  816. package/dist/ui/state/use-model-state.js.map +1 -1
  817. package/dist/ui/state/use-queue-state.d.ts +4 -3
  818. package/dist/ui/state/use-queue-state.d.ts.map +1 -1
  819. package/dist/ui/state/use-queue-state.js +11 -8
  820. package/dist/ui/state/use-queue-state.js.map +1 -1
  821. package/dist/ui/state/use-session-init.d.ts +4 -0
  822. package/dist/ui/state/use-session-init.d.ts.map +1 -1
  823. package/dist/ui/state/use-session-init.js +19 -3
  824. package/dist/ui/state/use-session-init.js.map +1 -1
  825. package/dist/ui/state/use-transcript-state.d.ts +6 -3
  826. package/dist/ui/state/use-transcript-state.d.ts.map +1 -1
  827. package/dist/ui/state/use-transcript-state.js +27 -15
  828. package/dist/ui/state/use-transcript-state.js.map +1 -1
  829. package/dist/ui/status-bar.d.ts +2 -1
  830. package/dist/ui/status-bar.d.ts.map +1 -1
  831. package/dist/ui/status-bar.js +2 -2
  832. package/dist/ui/status-bar.js.map +1 -1
  833. package/dist/ui/tool-output-cache.d.ts +12 -0
  834. package/dist/ui/tool-output-cache.d.ts.map +1 -0
  835. package/dist/ui/tool-output-cache.js +27 -0
  836. package/dist/ui/tool-output-cache.js.map +1 -0
  837. package/dist/ui/tool-output-commands.d.ts +3 -0
  838. package/dist/ui/tool-output-commands.d.ts.map +1 -0
  839. package/dist/ui/tool-output-commands.js +37 -0
  840. package/dist/ui/tool-output-commands.js.map +1 -0
  841. package/dist/ui/tool-output-wrap.d.ts +3 -0
  842. package/dist/ui/tool-output-wrap.d.ts.map +1 -0
  843. package/dist/ui/tool-output-wrap.js +24 -0
  844. package/dist/ui/tool-output-wrap.js.map +1 -0
  845. package/dist/ui/tool-progress.d.ts +23 -0
  846. package/dist/ui/tool-progress.d.ts.map +1 -0
  847. package/dist/ui/tool-progress.js +42 -0
  848. package/dist/ui/tool-progress.js.map +1 -0
  849. package/dist/ui/types.d.ts +8 -0
  850. package/dist/ui/types.d.ts.map +1 -1
  851. package/dist/ui/workflow-progress.d.ts +21 -0
  852. package/dist/ui/workflow-progress.d.ts.map +1 -0
  853. package/dist/ui/workflow-progress.js +107 -0
  854. package/dist/ui/workflow-progress.js.map +1 -0
  855. package/package.json +13 -4
  856. package/scripts/verify-runlog.mjs +208 -0
package/dist/ui/agent.js CHANGED
@@ -1,55 +1,25 @@
1
- /**
2
- * UI Module - Agent Runner
3
- *
4
- * Core agent execution loop, tool handling, and message validation.
5
- * Extracted from TerminalChat using an AgentContext state bag.
6
- */
1
+ import { approvalDisplayText } from '../approvals/index.js';
2
+ import { runTurn } from '../runtime/index.js';
3
+ import { cancellableDelay, isCancellation, throwIfCancelled } from '../cancellation.js';
7
4
  import * as config from '../config.js';
8
- import { chat } from '../providers/index.js';
9
- import { estimateContextUsage, needsSummarization } from '../providers/types.js';
10
- import { executeTool, getTools } from '../tools.js';
11
- import { DEFAULT_MODELS, RISK_CONFIG, calculateCost } from '../types.js';
12
- import { getModelContextLimit } from '../model-detection.js';
13
- import { assessToolRisk, requiresConfirmation } from '../risk.js';
5
+ import { estimateContextUsage } from '../providers/types.js';
6
+ import { getTools } from '../tools.js';
7
+ import { RISK_CONFIG } from '../types.js';
8
+ import { assessToolRisk } from '../risk.js';
14
9
  import { formatError, classifyError } from '../errors.js';
15
10
  import { getAvailableProviders } from '../providers/index.js';
16
11
  import * as storage from '../storage.js';
17
- import * as hooks from '../hooks.js';
18
- import * as router from '../router.js';
12
+ import { formatRoutingDecision } from '../routing/index.js';
19
13
  import { fleetActive, fleetMirrorAssistant } from '../fleet.js';
20
14
  import * as summarization from '../summarization.js';
21
- import { autoCompress } from '../auto-compressor.js';
22
15
  import { createStreamFlusher } from '../streaming.js';
23
- import { executeParallel, getParallelizationStats } from '../parallel-tools.js';
16
+ import { ToolProgress } from './tool-progress.js';
24
17
  import { checkAndWarnContextLimit } from './context.js';
25
- import { smartRoute } from '../router.js';
26
- import { isLocalBackend, detectMalformedToolCall, buildRepairMessage, buildToolCallEnvelopeSchema, extractRepairedToolCall, getLocalModelProfile, } from '../local-model.js';
18
+ import { SessionRecoveryError } from '../sessions/index.js';
27
19
  import { shouldCheckpoint, createCheckpoint } from '../checkpoint.js';
28
20
  import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
29
- import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit, } from '../iteration-limit.js';
30
- import { RunLog } from '../runlog.js';
31
- import { getBudgetCaps, evaluateBudget, hasBudgetCaps, recordProjectSpend, loadProjectSpend, formatBudgetHalt, } from '../budget.js';
32
- import { evaluatePolicy, isPolicyEnabled } from '../policy.js';
33
- // ============================================================================
34
- // Tool Result Truncation
35
- // ============================================================================
36
- /**
37
- * Truncate tool result content to fit within available context.
38
- * For small models (< 16K), aggressively cap tool output to prevent context overflow.
39
- */
40
- function truncateToolResult(content, modelLimit) {
41
- // Scale max tool result size based on model context
42
- // Small models: 25% of context, large models: up to 50K chars
43
- const maxChars = modelLimit < 8000 ? Math.floor(modelLimit * 0.6) // ~2.4K chars for 4K model
44
- : modelLimit < 16000 ? Math.floor(modelLimit * 0.8) // ~12K chars for 16K model
45
- : modelLimit < 32000 ? 20000
46
- : 50000;
47
- if (content.length <= maxChars)
48
- return content;
49
- const half = Math.floor(maxChars / 2);
50
- const trimmed = content.slice(0, half) + `\n\n... [truncated ${content.length - maxChars} chars] ...\n\n` + content.slice(-half);
51
- return trimmed;
52
- }
21
+ import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit } from '../iteration-limit.js';
22
+ import { formatBudgetHalt } from '../budget.js';
53
23
  function summarizeMessageContent(content) {
54
24
  if (typeof content === 'string') {
55
25
  return content;
@@ -103,148 +73,21 @@ export function validateAndRepairMessagesImpl(ctx) {
103
73
  }
104
74
  return repaired;
105
75
  }
106
- // ============================================================================
107
- // Local-model tool-call repair (features 2 & 3)
108
- // ============================================================================
109
- /**
110
- * When a LOCAL backend returns a malformed tool call, spend ONE corrective
111
- * round-trip trying to fix it before the error surfaces naturally through
112
- * executeTool. At most one call is repaired per response (cost control on
113
- * self-hosted hardware); each call id gets at most one repair (`repairedCallIds`).
114
- *
115
- * On Ollama we grammar-constrain the repair reply with the tool-call envelope
116
- * schema (the `format` param) — but only here, never on normal prose turns.
117
- *
118
- * Cloud backends and well-formed responses pass straight through unchanged.
119
- */
120
- async function maybeRepairLocalToolCalls(ctx, baseMessages, response, effectiveModel, repairedCallIds) {
121
- if (!response.toolCalls?.length)
122
- return response;
123
- const provider = ctx.actualProvider;
124
- if (!isLocalBackend(provider))
125
- return response;
126
- const tools = getTools();
127
- let target;
128
- for (const call of response.toolCalls) {
129
- if (repairedCallIds.has(call.id))
130
- continue;
131
- const fault = detectMalformedToolCall(call, tools);
132
- if (fault) {
133
- target = { call, reason: fault.reason };
134
- break;
135
- }
136
- }
137
- if (!target)
138
- return response;
139
- const { call, reason } = target;
140
- repairedCallIds.add(call.id); // one repair per tool call, whatever the outcome
141
- ctx.debugLog('repair', 'malformed local tool call', call.name, reason);
142
- ctx.addMessage('system', `🔧 Malformed tool call from local model (${reason}); attempting one repair…`);
143
- // Grammar-constrain the reply when the backend supports JSON-schema format.
144
- let format;
145
- try {
146
- const profile = await getLocalModelProfile(provider, effectiveModel || ctx.actualModel);
147
- if (profile.supportsJsonSchemaFormat) {
148
- format = buildToolCallEnvelopeSchema(tools.map(t => t.name));
149
- }
150
- }
151
- catch {
152
- // No format constraint — the corrective message alone still helps.
153
- }
154
- const repairMessages = [
155
- ...baseMessages,
156
- { role: 'assistant', content: typeof response.content === 'string' ? response.content : '', toolCalls: response.toolCalls },
157
- { role: 'user', content: buildRepairMessage(call, { reason }) },
158
- ];
159
- let repaired;
160
- try {
161
- repaired = await chat(ctx.provider, repairMessages, tools, effectiveModel, undefined, undefined, format ? { format } : undefined);
162
- }
163
- catch (err) {
164
- ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
165
- ctx.debugLog('repair', 'repair round-trip threw', err instanceof Error ? err.message : String(err));
166
- return response; // let the original error surface through executeTool
167
- }
168
- const corrected = extractRepairedToolCall(typeof repaired.content === 'string' ? repaired.content : '', repaired.toolCalls, call.id);
169
- if (!corrected) {
170
- ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
171
- ctx.addMessage('system', '🔧 Repair produced no usable tool call; surfacing the original error.');
172
- return response;
173
- }
174
- const stillBad = detectMalformedToolCall(corrected, tools);
175
- ctx.ledger?.recordAction('repair', { tool: call.name, to: corrected.name }, stillBad ? 'error' : 'ok', stillBad?.reason);
176
- ctx.addMessage('system', stillBad
177
- ? '🔧 Repair did not resolve the malformed call; surfacing the error.'
178
- : `🔧 Repaired tool call → ${corrected.name}`);
179
- return { ...response, toolCalls: response.toolCalls.map(tc => (tc.id === call.id ? corrected : tc)) };
180
- }
181
- // ============================================================================
182
- // Provider warnings
183
- // ============================================================================
184
- // Provider warnings already surfaced this process, keyed by sessionId::warning,
185
- // so a repeated model substitution across turns/iterations isn't shown twice
186
- // (#217). Non-fatal notices must be surfaced, never swallowed.
187
76
  const surfacedWarnings = new Set();
188
- function surfaceResponseWarnings(ctx, response) {
189
- if (!response.warnings?.length)
190
- return;
191
- const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
192
- for (const warning of response.warnings) {
193
- const key = `${sessionId}::${warning}`;
194
- if (surfacedWarnings.has(key))
195
- continue;
196
- surfacedWarnings.add(key);
197
- ctx.addMessage('system', warning);
198
- }
199
- }
200
- // ============================================================================
201
- // Run Agent
202
- // ============================================================================
203
- /**
204
- * Run agent with user prompt. Core execution loop that handles LLM calls,
205
- * tool execution (parallel + sequential), auto-compaction, and queued messages.
206
- */
207
- // #231: remember the previous turn's mode so the first work-mode turn after
208
- // a plan-mode turn can carry an explicit execution directive. Module scope is
209
- // intentional — mode itself outlives session resets.
210
77
  let previousTurnMode = null;
211
78
  /** Test seam: clear the plan-to-work transition tracking. */
212
79
  export function _resetModeTracking() {
213
80
  previousTurnMode = null;
214
81
  }
215
82
  export async function runAgentImpl(ctx, content) {
83
+ throwIfCancelled(ctx.signal);
216
84
  ctx.debugLog('runAgent', 'ENTER', typeof content === 'string' ? content.substring(0, 50) : '[complex]');
217
85
  // Validate message history before adding new content
218
86
  ctx.validateAndRepairMessages();
219
87
  ctx.llmMessages.current.push({ role: 'user', content });
220
88
  ctx.setStats(s => ({ ...s, messageCount: s.messageCount + 1 }));
221
89
  ctx.setStreamingResponse('');
222
- // Smart routing (cross-provider) takes precedence over autoRoute (single-provider)
223
- let effectiveModel = ctx.model;
224
- let effectiveProvider = ctx.provider;
225
- if (ctx.smartRouteActive && ctx.smartRoutingConfig?.enabled && typeof content === 'string') {
226
- const decision = smartRoute(content, ctx.smartRoutingConfig, {
227
- messageCount: ctx.stats.messageCount,
228
- hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
229
- });
230
- effectiveModel = decision.selected.model;
231
- effectiveProvider = decision.selected.provider;
232
- if (effectiveModel !== ctx.model || effectiveProvider !== ctx.provider) {
233
- ctx.addMessage('system', `[Smart route: ${decision.selected.provider}/${decision.selected.tier} - ${decision.taskType}/${decision.complexity}]`);
234
- }
235
- }
236
- else if (ctx.autoRoute && typeof content === 'string') {
237
- const routeDecision = router.routeRequest(content, ctx.provider, {
238
- messageCount: ctx.stats.messageCount,
239
- hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
240
- });
241
- effectiveModel = routeDecision.model.model;
242
- if (effectiveModel !== ctx.model) {
243
- ctx.addMessage('system', `[Auto-route: ${routeDecision.tier} tier - ${routeDecision.reason}]`);
244
- }
245
- }
246
90
  const maxIterations = resolveIterationLimit(config.get('maxIterations'));
247
- let completedNaturally = false;
248
91
  const hasParentRun = Boolean(ctx.ledger?.getActiveRun('loop') ||
249
92
  ctx.ledger?.getActiveRun('workflow') ||
250
93
  ctx.ledger?.getActiveRun('swarm') ||
@@ -256,758 +99,302 @@ export async function runAgentImpl(ctx, content) {
256
99
  : undefined;
257
100
  let runStatus;
258
101
  let runErrorSummary;
259
- // Local-backend behaviours (features 2 & 4): whether to append anchor hashes
260
- // to read_file output, and which tool-call ids have already spent their one
261
- // repair round-trip.
262
- const localBackend = isLocalBackend(ctx.actualProvider);
263
- const executeOptions = { appendAnchorHash: localBackend };
264
- const repairedCallIds = new Set();
265
- // Governance (#189): audit run log, budget caps, policy hook. The run log is a
266
- // per-session append-only JSONL trace; each agent turn is one run within it.
267
102
  const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
268
103
  const projectDir = ctx.sessionRef.current?.projectPath ?? process.cwd();
269
- const runlog = RunLog.open(sessionId);
270
- const budgetCaps = getBudgetCaps();
271
- // Only maintain the cross-run project ledger when a project cap is active.
272
- const trackProjectBudget = typeof budgetCaps.maxCostPerProject === 'number';
273
- const runStartedAt = Date.now();
274
- let runInputTokens = 0;
275
- let runOutputTokens = 0;
276
- let runCostUsd = 0;
277
- let runToolCalls = 0;
278
- let runEnded = false;
279
- let runExitReason;
280
- let budgetVerdict;
281
- runlog.runStart({
282
- session: sessionId,
283
- cwd: projectDir,
284
- provider: effectiveProvider,
285
- model: effectiveModel || ctx.actualModel,
286
- config: config.getConfig(),
287
- });
288
- runlog.userPrompt(summarizeMessageContent(content));
289
- // #231: a terse reply right after leaving plan mode is an approval, not a
290
- // conversation — bind it to execution explicitly or weak models answer
291
- // "done" without doing anything.
104
+ let provider = ctx.actualProvider;
105
+ let model = ctx.actualModel;
292
106
  const justLeftPlanMode = previousTurnMode === 'plan' && ctx.mode !== 'plan';
293
107
  previousTurnMode = ctx.mode;
294
- const finalizeRun = (exitReason) => {
295
- if (runEnded)
296
- return;
297
- runEnded = true;
298
- runlog.runEnd({
299
- totals: {
300
- inputTokens: runInputTokens,
301
- outputTokens: runOutputTokens,
302
- cost: runCostUsd,
303
- toolCalls: runToolCalls,
304
- durationMs: Date.now() - runStartedAt,
305
- },
306
- exitReason,
307
- });
308
- void runlog.flush();
108
+ let flusher;
109
+ let iteration = 0;
110
+ let streamStarted = false;
111
+ let errorShown = false;
112
+ let finalResponse = false;
113
+ let lastResponseCost;
114
+ const blocked = new Set();
115
+ const toolProgress = new ToolProgress();
116
+ const showToolProgress = () => ctx.setActivityState(toolProgress.snapshot());
117
+ const failed = ctx.ledger?.getFailedApproachesMessage();
118
+ if (failed)
119
+ ctx.llmMessages.current.push({ role: 'user', content: failed });
120
+ const clearDisplay = () => {
121
+ toolProgress.clear();
122
+ flusher?.destroy();
123
+ ctx.setThinkingState(null);
124
+ ctx.setActivityState(null);
125
+ ctx.setStreamingResponse('');
309
126
  };
310
- // Check context limit and warn if approaching capacity
311
- // Uses model's actual context length from API when available
312
- let currentContextTokens = ctx.estimateContextTokens();
313
- const modelLimit = getModelContextLimit(ctx.actualProvider, effectiveModel || ctx.actualModel);
314
- let contextPercentage = (currentContextTokens / modelLimit) * 100;
315
- // Adaptive preserveRecent: small models keep fewer messages to leave room for output
316
- const preserveRecent = modelLimit < 8000 ? 2 : modelLimit < 16000 ? 4 : modelLimit < 32000 ? 6 : modelLimit < 64000 ? 10 : 15;
317
- // Auto-compact using the new auto-compressor
318
- const autoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
319
- if (autoCompressResult.compressed) {
320
- ctx.llmMessages.current = autoCompressResult.messages;
321
- currentContextTokens = ctx.estimateContextTokens();
322
- contextPercentage = (currentContextTokens / modelLimit) * 100;
323
- ctx.setContextTokens(currentContextTokens);
324
- ctx.addMessage('system', `🔄 Auto-compressed ${autoCompressResult.summarizedCount} messages using ${autoCompressResult.method} (${Math.round(autoCompressResult.originalTokens / 1000)}K → ${Math.round(autoCompressResult.compressedTokens / 1000)}K tokens)`);
325
- }
326
- else if (contextPercentage > 65) {
327
- ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
328
- Consider: /compact | /clear | shorter messages`);
329
- }
330
- // Inject failed approaches into context so the agent avoids repeating mistakes
331
- if (ctx.ledger) {
332
- const failedMsg = ctx.ledger.getFailedApproachesMessage();
333
- if (failedMsg) {
334
- ctx.llmMessages.current.push({ role: 'user', content: failedMsg });
335
- }
336
- }
337
- for (let i = 0; i < maxIterations; i++) {
338
- // Start ledger tracking for this iteration
339
- if (ctx.ledger) {
340
- ctx.ledger.startIteration(ctx.ledger.getNextIterationNumber());
341
- }
342
- // Safety check at start of each iteration - context may have grown from tool results
343
- if (i > 0) {
344
- const iterContextTokens = ctx.estimateContextTokens();
345
- const iterAutoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
346
- if (iterAutoCompressResult.compressed) {
347
- ctx.llmMessages.current = iterAutoCompressResult.messages;
348
- ctx.setContextTokens(ctx.estimateContextTokens());
349
- ctx.addMessage('system', `🔄 Auto-compressed ${iterAutoCompressResult.summarizedCount} messages during iteration ${i + 1} (${iterAutoCompressResult.method})`);
350
- }
351
- }
352
- // Frame-bounded streaming: coalesce provider tokens so setStreamingResponse
353
- // fires at most ~30fps instead of once per token (which re-rendered the
354
- // transcript region on every token). The flusher is per-iteration; its
355
- // pending timer is drained by flush() on success and cleared by destroy()
356
- // on the error/cancel path below.
357
- const streamFlusher = createStreamFlusher((delta) => {
358
- ctx.setStreamingResponse(prev => prev + delta);
359
- });
360
- try {
361
- // Update thinking state for LLM call
362
- ctx.setThinkingState({
363
- status: i === 0 ? 'Analyzing request...' : 'Processing response...',
364
- detail: `Iteration ${formatIterationProgress(i + 1, maxIterations)}`,
365
- iteration: i + 1,
366
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
367
- });
368
- ctx.setActivityState({
369
- action: i === 0 ? 'Analyzing request' : 'Processing',
370
- target: `iteration ${i + 1}`,
371
- startTime: Date.now(),
372
- });
373
- // Streaming callback for final response. Clear the thinking indicator on
374
- // the first token, then hand the token to the frame-bounded flusher.
375
- let streamStarted = false;
376
- const onToken = (token) => {
377
- if (!streamStarted) {
378
- ctx.setThinkingState(null); // Clear thinking when streaming starts
379
- streamStarted = true;
127
+ ctx.signal?.addEventListener('abort', clearDisplay, { once: true });
128
+ try {
129
+ const result = await runTurn({
130
+ captureToolOutput: true,
131
+ onCheckpoint: ctx.onCheckpoint,
132
+ onSafetyBranch: ctx.onCheckpoint ? async () => {
133
+ const { branchSession } = await import('../session-management/index.js');
134
+ const branch = await branchSession(sessionId, { kind: 'safety', signal: ctx.signal, mode: ctx.mode });
135
+ ctx.addMessage('system', `Recovery conversation branch: ${branch.session.id}. Use /checkout to recover its conversation and tool state.`);
136
+ } : undefined,
137
+ client: 'terminal', sessionId, cwd: projectDir, provider: ctx.provider, model: ctx.model,
138
+ preferenceSources: ctx.preferenceSources,
139
+ routing: { ...ctx.smartRoutingConfig, ...config.get('routing') },
140
+ onRoute: decision => {
141
+ if (decision.selected) {
142
+ provider = decision.selected.provider;
143
+ model = decision.selected.model;
380
144
  }
381
- streamFlusher.push(token);
382
- };
383
- // Retry callback for error recovery
384
- const onRetry = (attempt, error, delayMs) => {
385
- ctx.setThinkingState({
386
- status: `Retrying... (attempt ${attempt + 1})`,
387
- detail: `${error.message.substring(0, 40)}... Waiting ${Math.round(delayMs / 1000)}s`,
388
- iteration: i + 1,
389
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
390
- });
391
- };
392
- ctx.debugLog('chat', 'WAITING for LLM response', `iteration=${i + 1}`);
393
- // Validate message history to prevent orphaned tool_result errors
394
- let validatedMessages = summarization.validateMessageHistory(ctx.llmMessages.current);
395
- if (validatedMessages.length !== ctx.llmMessages.current.length) {
396
- ctx.debugLog('chat', 'CLEANED orphaned tool results', `removed=${ctx.llmMessages.current.length - validatedMessages.length}`);
397
- ctx.llmMessages.current = validatedMessages;
398
- }
399
- // Plan mode: tell the model it is planning and that plans need receipts.
400
- // Injected per-request (not persisted) so mode switches take effect
401
- // immediately and history stays clean.
402
- if (ctx.mode === 'plan') {
403
- validatedMessages = [
404
- ...validatedMessages,
405
- {
406
- role: 'system',
407
- content: 'You are in PLAN mode: no mutating tools will execute. '
145
+ ctx.onRoute?.(decision);
146
+ ctx.addMessage(decision.selected ? 'system' : 'error', formatRoutingDecision(decision));
147
+ },
148
+ prompt: summarizeMessageContent(content), messages: ctx.llmMessages,
149
+ signal: ctx.signal, mode: ctx.mode, maxIterations,
150
+ inheritScope: true, parallel: true, continueOnLength: true, tools: getTools,
151
+ confirmation: ctx.confirmMode ? 'interactive' : 'none',
152
+ approvals: ctx.approvals,
153
+ approve: ctx.approve ? (_call, decision) => ctx.approve(decision, ctx.signal) : undefined,
154
+ prepare: async (request, index) => {
155
+ iteration = index;
156
+ finalResponse = false;
157
+ ctx.ledger?.startIteration(ctx.ledger.getNextIterationNumber());
158
+ flusher?.destroy();
159
+ flusher = createStreamFlusher(delta => { if (!ctx.signal?.aborted)
160
+ ctx.setStreamingResponse(prev => prev + delta); });
161
+ streamStarted = false;
162
+ ctx.setStreamingResponse('');
163
+ ctx.setThinkingState({ status: index === 1 ? 'Analyzing request...' : 'Processing response...',
164
+ detail: `Iteration ${formatIterationProgress(index, maxIterations)}`, iteration: index,
165
+ maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined });
166
+ ctx.setActivityState({ action: index === 1 ? 'Analyzing request' : 'Processing', target: `iteration ${index}`, startTime: Date.now() });
167
+ ctx.llmMessages.current = summarization.validateMessageHistory(ctx.llmMessages.current);
168
+ const context = estimateContextUsage(request.provider, request.model, ctx.llmMessages.current, request.tools);
169
+ if (context.needsSummarization) {
170
+ const compact = summarization.summarizeConversation(ctx.llmMessages.current, { maxTokens: Math.floor(context.limit * 0.6) });
171
+ if (compact.summarizedCount > 0)
172
+ ctx.llmMessages.current = compact.messages;
173
+ }
174
+ // Mode directives are transient; compaction never writes them into history.
175
+ let messages = [...ctx.llmMessages.current];
176
+ if (ctx.mode === 'plan')
177
+ messages.push({ role: 'system', content: 'You are in PLAN mode: no mutating tools will execute. '
408
178
  + 'Read-only tools (read_file, list_files, think, create_plan, ask_question) ARE available — use them. '
409
179
  + 'Before proposing any plan, read the files you intend to change and cite file:line for every claim. '
410
180
  + 'State explicitly what you verified versus what you assume. '
411
- + 'A plan produced without reading anything will be marked unverified.',
412
- },
413
- ];
414
- }
415
- if (justLeftPlanMode && i === 0) {
416
- validatedMessages = [
417
- ...validatedMessages,
418
- {
419
- role: 'system',
420
- content: 'The user has just switched from plan mode to work mode and replied. '
181
+ + 'A plan produced without reading anything will be marked unverified.' });
182
+ if (justLeftPlanMode && index === 1)
183
+ messages.push({ role: 'system', content: 'The user has just switched from plan mode to work mode and replied. '
421
184
  + 'Treat their message as approval to execute the plan discussed above. '
422
185
  + 'Carry it out now using tools, step by step. '
423
- + 'Never state that work is done unless you performed it with tool calls in this turn.',
424
- },
425
- ];
426
- }
427
- // Pre-request summarization check - summarize BEFORE sending if context is too large
428
- const tools = getTools();
429
- const contextCheck = estimateContextUsage(ctx.provider, effectiveModel || DEFAULT_MODELS[ctx.provider], validatedMessages, tools);
430
- ctx.debugLog('chat', 'CONTEXT CHECK', `estimated=${contextCheck.estimated}, limit=${contextCheck.limit}, percent=${contextCheck.percent}%`);
431
- if (contextCheck.needsSummarization) {
432
- ctx.debugLog('chat', 'PRE-REQUEST SUMMARIZING', `estimated=${contextCheck.estimated} >= 80% of ${contextCheck.limit}`);
433
- const result = summarization.summarizeConversation(validatedMessages, { maxTokens: Math.floor(contextCheck.limit * 0.6) });
434
- if (result.summarizedCount > 0) {
435
- ctx.llmMessages.current = result.messages;
436
- validatedMessages = result.messages;
437
- ctx.debugLog('chat', 'PRE-SUMMARIZED', `removed=${result.summarizedCount} messages, reduced from ${result.originalTokens} to ${result.reducedTokens}`);
186
+ + 'Never state that work is done unless you performed it with tool calls in this turn.' });
187
+ return { ...request, messages };
188
+ },
189
+ onCompression: result => {
190
+ ctx.setContextTokens(ctx.estimateContextTokens());
191
+ ctx.addMessage('system', `🔄 Auto-compressed ${result.summarizedCount} messages using ${result.method} (${Math.round(result.originalTokens / 1000)}K → ${Math.round(result.compressedTokens / 1000)}K tokens)`);
192
+ },
193
+ onStreamReset: () => {
194
+ flusher?.destroy();
195
+ flusher = createStreamFlusher(delta => { if (!ctx.signal?.aborted)
196
+ ctx.setStreamingResponse(prev => prev + delta); });
197
+ streamStarted = false;
198
+ ctx.setStreamingResponse('');
199
+ },
200
+ onToken: token => {
201
+ if (!streamStarted) {
202
+ ctx.setThinkingState(null);
203
+ streamStarted = true;
438
204
  }
439
- }
440
- let response = await chat(ctx.provider, validatedMessages, tools, effectiveModel, onToken, onRetry);
441
- streamFlusher.flush(); // drain any batched tail before we swap to history
442
- ctx.debugLog('chat', 'GOT response', `toolCalls=${response.toolCalls?.length ?? 0}`);
443
- // Local backends: give a malformed tool call ONE corrective round-trip
444
- // before it surfaces as an execution error (features 2 & 3). No-op for
445
- // cloud providers and well-formed responses.
446
- response = await maybeRepairLocalToolCalls(ctx, validatedMessages, response, effectiveModel, repairedCallIds);
447
- // Surface provider warnings (e.g. Ollama model substitution) once per
448
- // unique message per session — never swallow them (#217).
449
- surfaceResponseWarnings(ctx, response);
450
- // Update token stats and cost
451
- if (response.usage) {
452
- const usageCost = calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens);
453
- ctx.setStats(s => ({
454
- ...s,
455
- inputTokens: s.inputTokens + response.usage.inputTokens,
456
- outputTokens: s.outputTokens + response.usage.outputTokens,
457
- cost: s.cost + usageCost,
458
- }));
459
- // Record in iteration ledger
460
- ctx.ledger?.recordTokens(response.usage.inputTokens, response.usage.outputTokens, usageCost);
461
- // Persist cost to storage
462
- storage.recordCost(usageCost, ctx.actualProvider, ctx.sessionRef.current?.id);
463
- // Auto-summarize if context is getting too full (85% threshold)
464
- if (needsSummarization(ctx.provider, ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens)) {
465
- ctx.debugLog('chat', 'AUTO-SUMMARIZING', `inputTokens=${response.usage.inputTokens}`);
466
- const postCompactLimit = Math.floor(getModelContextLimit(ctx.provider, ctx.model || DEFAULT_MODELS[ctx.provider]) * 0.6);
467
- const result = summarization.summarizeConversation(ctx.llmMessages.current, { maxTokens: postCompactLimit });
468
- if (result.summarizedCount > 0) {
469
- ctx.llmMessages.current = result.messages;
470
- ctx.debugLog('chat', 'SUMMARIZED', `removed=${result.summarizedCount} messages`);
205
+ flusher?.push(token);
206
+ },
207
+ onRetry: (attempt, error, delayMs) => ctx.setThinkingState({ status: `Retrying... (attempt ${attempt + 1})`,
208
+ detail: `${approvalDisplayText(classifyError(error).message)} Waiting ${Math.round(delayMs / 1000)}s`, iteration,
209
+ maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined }),
210
+ onUsage: (response, request, cost) => {
211
+ lastResponseCost = response.usage ? cost : undefined;
212
+ if (!response.usage)
213
+ return;
214
+ const { inputTokens, outputTokens } = response.usage;
215
+ ctx.setStats(s => ({ ...s, inputTokens: s.inputTokens + inputTokens, outputTokens: s.outputTokens + outputTokens, cost: s.cost + cost }));
216
+ ctx.ledger?.recordTokens(inputTokens, outputTokens, cost);
217
+ storage.recordCost(cost, request.provider, sessionId);
218
+ },
219
+ onWarning: warning => {
220
+ const key = `${sessionId}::${warning}`;
221
+ if (!surfacedWarnings.has(key)) {
222
+ surfacedWarnings.add(key);
223
+ ctx.addMessage('system', warning);
224
+ }
225
+ },
226
+ onResponse: (response, index) => {
227
+ flusher?.flush();
228
+ if (ctx.circuitBreaker) {
229
+ const breaker = ctx.circuitBreaker.check({ iteration: index, inputTokens: response.usage?.inputTokens,
230
+ outputTokens: response.usage?.outputTokens,
231
+ cost: lastResponseCost,
232
+ toolCalls: response.toolCalls?.map(call => ({ name: call.name, arguments: call.arguments })),
233
+ content: response.content, timestamp: new Date() });
234
+ ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
235
+ if (breaker.tripped) {
236
+ ctx.addMessage('system', `⚠️ Circuit breaker tripped: ${breaker.breaker}\n${breaker.message}\n\nUse /breaker resume to continue, /breaker status for details.`);
237
+ runErrorSummary = breaker.message;
238
+ return 'stop';
471
239
  }
472
240
  }
473
- }
474
- // Governance: accrue run spend, audit the assistant turn, evaluate budget.
475
- {
476
- const turnCost = response.usage
477
- ? calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens)
478
- : 0;
479
- if (response.usage) {
480
- runInputTokens += response.usage.inputTokens;
481
- runOutputTokens += response.usage.outputTokens;
482
- runCostUsd += turnCost;
483
- if (trackProjectBudget)
484
- recordProjectSpend(projectDir, turnCost);
241
+ if (fleetActive() && response.content)
242
+ fleetMirrorAssistant(response.content);
243
+ if (response.toolCalls?.length) {
244
+ if (response.content)
245
+ ctx.addMessage('assistant', response.content);
246
+ ctx.setStreamingResponse('');
247
+ ctx.setThinkingState(null);
248
+ return;
485
249
  }
486
- runlog.assistantMessage({
487
- content: response.content,
488
- tokens: { input: response.usage?.inputTokens ?? 0, output: response.usage?.outputTokens ?? 0 },
489
- cost: turnCost,
490
- });
491
- if (hasBudgetCaps(budgetCaps)) {
492
- const verdict = evaluateBudget(budgetCaps, {
493
- runCostUsd,
494
- runTokens: runInputTokens + runOutputTokens,
495
- projectCostUsd: loadProjectSpend(projectDir).spentUsd,
496
- });
497
- if (verdict.exceeded)
498
- budgetVerdict = verdict;
250
+ finalResponse = true;
251
+ ctx.setThinkingState(null);
252
+ ctx.addMessage('assistant', response.content);
253
+ ctx.setStreamingResponse('');
254
+ ctx.setContextTokens(ctx.estimateContextTokens());
255
+ checkAndWarnContextLimit(provider, model, ctx.estimateContextTokens(), ctx.addMessage);
256
+ if (response.finishReason === 'length')
257
+ ctx.addMessage('system', '(auto-continuing...)');
258
+ else if (!ctx.onCheckpoint)
259
+ storage.saveMessageHistory(ctx.llmMessages.current);
260
+ return undefined;
261
+ },
262
+ onRepair: event => {
263
+ if (event.status === 'started') {
264
+ ctx.addMessage('system', `🔧 Malformed tool call from local model (${event.reason}); attempting one repair…`);
499
265
  }
500
- }
501
- // Circuit breaker check after each iteration
502
- if (ctx.circuitBreaker) {
503
- const iterData = {
504
- iteration: i + 1,
505
- inputTokens: response.usage?.inputTokens,
506
- outputTokens: response.usage?.outputTokens,
507
- cost: response.usage ? calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens) : undefined,
508
- toolCalls: response.toolCalls?.map(tc => ({ name: tc.name, arguments: tc.arguments })),
509
- content: response.content,
510
- timestamp: new Date(),
511
- };
512
- const breakerResult = ctx.circuitBreaker.check(iterData);
513
- ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
514
- if (breakerResult.tripped) {
515
- ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue, /breaker status for details.`);
516
- runStatus = 'stopped';
517
- runErrorSummary = breakerResult.message;
518
- completedNaturally = true;
519
- break;
266
+ else {
267
+ ctx.ledger?.recordAction('repair', { tool: event.call.name, ...(event.corrected ? { to: event.corrected.name } : {}) }, event.status, event.status === 'ok' ? undefined : event.reason);
268
+ ctx.addMessage('system', event.status === 'ok' ? `🔧 Repaired tool call → ${event.corrected.name}`
269
+ : event.corrected ? '🔧 Repair did not resolve the malformed call; surfacing the error.' : '🔧 Repair produced no usable tool call; surfacing the original error.');
520
270
  }
521
- }
522
- // Handle tool calls with parallel execution support
523
- if (response.toolCalls?.length) {
524
- ctx.llmMessages.current.push({
525
- role: 'assistant',
526
- content: response.content,
527
- toolCalls: response.toolCalls,
528
- });
529
- // Mirror any text the assistant produced alongside the tool calls
530
- if (fleetActive() && response.content) {
531
- fleetMirrorAssistant(response.content);
271
+ },
272
+ onToolStart: call => { toolProgress.start(call.id, call.name); showToolProgress(); },
273
+ onToolRetry: call => { toolProgress.retry(call.id); showToolProgress(); },
274
+ onPermission: (call, decision) => {
275
+ const risk = assessToolRisk(call);
276
+ const display = risk.level !== 'none' ? ` [${RISK_CONFIG[risk.level].bar}]` : '';
277
+ const preview = approvalDisplayText(String(call.arguments.command || call.arguments.path || '...'));
278
+ if (decision.decision !== 'allow') {
279
+ blocked.add(call.id);
280
+ ctx.addMessage('tool', `${call.name}: ${preview}${display}\n${decision.reason}`);
281
+ ctx.ledger?.recordAction(call.name, call.arguments, 'blocked', decision.reason);
532
282
  }
533
- const preChecks = [];
534
- const executableTools = [];
535
- for (const toolCall of response.toolCalls) {
536
- const args = toolCall.arguments;
537
- runlog.toolCall({ id: toolCall.id, name: toolCall.name, args });
538
- runToolCalls++;
539
- const toolPreview = String(args.command || args.path || '...');
540
- const risk = assessToolRisk(toolCall);
541
- const riskConfig = RISK_CONFIG[risk.level];
542
- const riskDisplay = risk.level !== 'none' ? ` [${riskConfig.bar}]` : '';
543
- const preCheck = {
544
- toolCall,
545
- args,
546
- preview: toolPreview,
547
- risk,
548
- riskDisplay,
549
- blocked: false,
550
- };
551
- // Check blocking conditions
552
- const PLAN_MODE_ALLOWED = new Set(['think', 'ask_question', 'create_plan', 'read_file', 'list_files']);
553
- if (ctx.mode === 'plan' && !PLAN_MODE_ALLOWED.has(toolCall.name)) {
554
- preCheck.blocked = true;
555
- preCheck.blockReason = 'plan mode';
556
- preCheck.blockContent = '[Plan mode: Tool not executed. Describe what this would do.]';
557
- ctx.addMessage('tool', `📋 ${toolCall.name}: ${toolPreview}${riskDisplay} (plan mode - not executed)`);
558
- }
559
- else if (ctx.confirmMode && requiresConfirmation(risk, false) && toolCall.name !== 'think') {
560
- preCheck.blocked = true;
561
- preCheck.blockReason = 'confirmation required';
562
- preCheck.blockContent = `[Operation blocked - ${risk.level} risk: ${risk.reason}. User confirmation required.]`;
563
- const riskIcon = risk.level === 'critical' ? '🛑' : '⚠️';
564
- ctx.addMessage('tool', `${riskIcon} ${toolCall.name}: ${toolPreview}${riskDisplay}\n → Requires confirmation (use /confirm off to disable)`);
565
- }
566
- else {
567
- // Check pre-tool hooks
568
- const preHookResult = await hooks.checkHooksAllow('pre-tool', {
569
- tool: toolCall.name,
570
- toolArgs: args,
571
- });
572
- if (!preHookResult.allowed) {
573
- preCheck.blocked = true;
574
- preCheck.blockReason = 'blocked by hook';
575
- preCheck.blockContent = `[Blocked by hook: ${preHookResult.reason}]`;
576
- ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
577
- ctx.addMessage('tool', `🛑 Blocked by hook: ${preHookResult.reason}`);
578
- }
579
- else {
580
- // Pre-tool policy gate (fail closed). Distinct from the hook above:
581
- // the policy engine receives the tool-call JSON on stdin and denies
582
- // via a non-zero exit code (see policy.ts / docs/governance.md).
583
- const policyResult = isPolicyEnabled() ? await evaluatePolicy(toolCall) : undefined;
584
- if (policyResult) {
585
- runlog.policyEvent({
586
- tool: toolCall.name,
587
- decision: policyResult.decision,
588
- source: policyResult.source,
589
- reason: policyResult.reason,
590
- durationMs: policyResult.durationMs,
591
- });
592
- }
593
- if (policyResult && policyResult.decision === 'deny') {
594
- preCheck.blocked = true;
595
- preCheck.blockReason = 'denied by policy';
596
- preCheck.blockContent = `[Denied by policy: ${policyResult.reason || 'no reason given'}]`;
597
- ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
598
- ctx.addMessage('tool', `⛨ Denied by policy: ${policyResult.reason || 'no reason given'}`);
599
- }
600
- else {
601
- // Tool can be executed
602
- executableTools.push(toolCall);
603
- ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
604
- }
605
- }
606
- }
607
- preChecks.push(preCheck);
608
- // Record blocked tools in ledger and add to LLM messages
609
- if (preCheck.blocked) {
610
- ctx.ledger?.recordAction(toolCall.name, args, 'blocked', preCheck.blockReason);
611
- runlog.toolResult({ id: toolCall.id, result: preCheck.blockContent, isError: true, durationMs: 0 });
612
- ctx.llmMessages.current.push({
613
- role: 'tool',
614
- content: preCheck.blockContent,
615
- toolCallId: toolCall.id,
616
- });
617
- }
283
+ else
284
+ ctx.addMessage('tool', `⚡ ${call.name}: ${preview}${display}`);
285
+ },
286
+ beforeTool: call => {
287
+ const args = call.arguments;
288
+ const thought = call.name === 'think' ? String(args.thought || '') : undefined;
289
+ toolProgress.running(call.id);
290
+ showToolProgress();
291
+ ctx.setThinkingState({ status: thought ? 'Reasoning...' : `Executing ${call.name}...`, thinking: thought,
292
+ detail: (thought || String(args.path || args.command || '...')).substring(0, 60), iteration,
293
+ maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined });
294
+ if (shouldCheckpoint(call.name, args, projectDir))
295
+ createCheckpoint(call.name, args, projectDir);
296
+ },
297
+ onToolOutput: (call, chunk) => {
298
+ if (!ctx.signal?.aborted) {
299
+ toolProgress.output(call.id, chunk);
300
+ showToolProgress();
618
301
  }
619
- // ============================================================
620
- // Phase 2: Execute tools (parallel when beneficial)
621
- // ============================================================
622
- if (executableTools.length > 0) {
623
- const parallelStats = getParallelizationStats(executableTools);
624
- const useParallel = parallelStats.maxParallel > 1 && executableTools.length > 1;
625
- if (useParallel) {
626
- // Show parallelization info
627
- ctx.setThinkingState({
628
- status: `Executing ${executableTools.length} tools in parallel...`,
629
- detail: `${parallelStats.stages} stages, up to ${parallelStats.maxParallel}x speedup`,
630
- iteration: i + 1,
631
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
632
- });
633
- ctx.setActivityState({
634
- action: `Executing ${executableTools.length} tools`,
635
- target: 'in parallel',
636
- startTime: Date.now(),
637
- });
638
- // Execute in parallel using dependency-aware staging
639
- ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
640
- const results = await executeParallel(executableTools, async (call) => {
641
- const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, undefined, executeOptions);
642
- return { result: result.result, isError: result.isError };
643
- }, (completed, total, current) => {
644
- const args = current.arguments;
645
- const target = args.path || args.command?.substring(0, 30) || current.name;
646
- ctx.setActivityState({
647
- action: `Running ${current.name}`,
648
- target: target,
649
- startTime: Date.now(),
650
- });
651
- ctx.setThinkingState({
652
- status: `Executing tools... (${completed + 1}/${total})`,
653
- detail: current.name,
654
- iteration: i + 1,
655
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
656
- });
657
- });
658
- ctx.debugLog('tools', 'PARALLEL exec done', `results=${results.length}`);
659
- // Process results sequentially for UI and LLM messages
660
- for (const result of results) {
661
- const toolCall = result.toolCall;
662
- const args = toolCall.arguments;
663
- // A tool-level failure (result.isError) or a thrown exception (result.error)
664
- // both count as a failure — mirror the sequential branch.
665
- const failed = result.isError || !!result.error;
666
- const failureText = result.error || result.result;
667
- // Record in iteration ledger
668
- ctx.ledger?.recordAction(toolCall.name, args, failed ? 'error' : 'ok', failed ? failureText : undefined);
669
- // Audit the tool result (parallel exec has no per-call duration).
670
- runlog.toolResult({ id: toolCall.id, result: failed ? failureText : result.result, isError: failed, durationMs: 0 });
671
- // Execute post-tool hooks
672
- hooks.executeHooks('post-tool', {
673
- tool: toolCall.name,
674
- toolArgs: args,
675
- toolResult: result.result,
676
- }).catch((err) => {
677
- ctx.debugLog('hooks', `post-tool hook failed for ${toolCall.name}:`, err instanceof Error ? err.message : err);
678
- });
679
- // Display result
680
- if (toolCall.name === 'think') {
681
- const thought = String(args.thought || '');
682
- ctx.addMessage('tool', thought);
683
- }
684
- else if (result.error) {
685
- ctx.addMessage('tool', `Error: ${result.error}`, true);
686
- }
687
- else {
688
- const preview = result.result.split('\n').slice(0, 3).join('\n');
689
- ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''), failed);
690
- }
691
- ctx.llmMessages.current.push({
692
- role: 'tool',
693
- content: truncateToolResult(result.error ? `Error: ${result.error}` : result.result, modelLimit),
694
- toolCallId: toolCall.id,
695
- });
696
- }
697
- }
698
- else {
699
- // Sequential execution (single tool or dependencies prevent parallelization)
700
- ctx.debugLog('tools', 'SEQUENTIAL exec start', `count=${executableTools.length}`);
701
- for (const toolCall of executableTools) {
702
- const args = toolCall.arguments;
703
- const toolPreview = String(args.command || args.path || args.content?.toString().substring(0, 30) || '...');
704
- // Set activity state for streaming indicator
705
- const actionMap = {
706
- read_file: 'Reading',
707
- write_file: 'Writing',
708
- edit_file: 'Editing',
709
- bash: 'Running',
710
- search: 'Searching',
711
- glob: 'Finding',
712
- think: 'Thinking',
713
- };
714
- const action = actionMap[toolCall.name] || `Executing ${toolCall.name}`;
715
- const target = toolCall.name === 'bash'
716
- ? args.command?.substring(0, 40) + (args.command?.length > 40 ? '...' : '')
717
- : toolCall.name === 'think'
718
- ? undefined
719
- : args.path || args.pattern;
720
- ctx.setActivityState({ action, target, startTime: Date.now() });
721
- // Special handling for think tool UI
722
- if (toolCall.name === 'think') {
723
- const thought = String(args.thought || '');
724
- ctx.setThinkingState({
725
- status: 'Reasoning...',
726
- detail: thought.substring(0, 60) + (thought.length > 60 ? '...' : ''),
727
- thinking: thought,
728
- iteration: i + 1,
729
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
730
- });
731
- }
732
- else {
733
- ctx.setThinkingState({
734
- status: `Executing ${toolCall.name}...`,
735
- detail: toolPreview.substring(0, 60),
736
- thinking: undefined,
737
- iteration: i + 1,
738
- maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
739
- });
740
- }
741
- ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
742
- // Auto-checkpoint before destructive operations
743
- if (shouldCheckpoint(toolCall.name, args)) {
744
- const hash = createCheckpoint(toolCall.name, args);
745
- if (hash) {
746
- ctx.debugLog('checkpoint', `checkpoint ${hash} before ${toolCall.name}`);
747
- }
748
- }
749
- // Stream shell output in real-time (#15)
750
- const shellStreamCallback = toolCall.name === 'shell' ? (chunk) => {
751
- ctx.setActivityState({
752
- action: 'Running shell',
753
- target: args.command?.substring(0, 40),
754
- startTime: Date.now(),
755
- detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
756
- });
757
- } : undefined;
758
- const seqStart = Date.now();
759
- const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback, executeOptions);
760
- ctx.debugLog('tools', 'DONE', toolCall.name);
761
- // Record in iteration ledger
762
- ctx.ledger?.recordAction(toolCall.name, args, result.isError ? 'error' : 'ok', result.isError ? result.result : undefined);
763
- // Audit the tool result.
764
- runlog.toolResult({ id: toolCall.id, result: result.result, isError: result.isError || false, durationMs: Date.now() - seqStart });
765
- // Execute post-tool hooks
766
- hooks.executeHooks('post-tool', {
767
- tool: toolCall.name,
768
- toolArgs: args,
769
- toolResult: result.result,
770
- }).catch((err) => {
771
- ctx.debugLog('hooks', `post-tool hook failed for ${toolCall.name}:`, err instanceof Error ? err.message : err);
772
- });
773
- // Display result - use displayResult for UI, full result for LLM (#25)
774
- if (toolCall.name === 'think') {
775
- const thought = String(args.thought || '');
776
- ctx.addMessage('tool', thought);
777
- }
778
- else if (toolCall.name === 'ask_question') {
779
- // Display question prominently (#42)
780
- const question = String(args.question || '');
781
- const options = Array.isArray(args.options) ? args.options : undefined;
782
- const contextNote = typeof args.context === 'string' ? args.context : undefined;
783
- let questionMsg = `❓ ${question}`;
784
- if (contextNote)
785
- questionMsg += `\n ${contextNote}`;
786
- if (options)
787
- questionMsg += '\n' + options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
788
- ctx.addMessage('assistant', questionMsg);
789
- // Tell the LLM that we're waiting for user input
790
- ctx.llmMessages.current.push({
791
- role: 'tool',
792
- content: '[Waiting for user response. The user will reply with their answer.]',
793
- toolCallId: toolCall.id,
794
- });
795
- // Break out of tool loop - let user respond naturally
796
- completedNaturally = true;
797
- }
798
- else if (toolCall.name === 'create_plan') {
799
- // Display plan as a checklist for user approval (#19)
800
- const planTitle = String(args.title || 'Plan');
801
- const planSteps = Array.isArray(args.steps) ? args.steps : [];
802
- const planReasoning = typeof args.reasoning === 'string' ? args.reasoning : undefined;
803
- let planMsg = `📋 Plan: ${planTitle}\n`;
804
- if (planReasoning)
805
- planMsg += `\n ${planReasoning}\n`;
806
- planMsg += '\n' + planSteps.map((s, idx) => ` ${idx + 1}. [ ] ${s}`).join('\n');
807
- planMsg += '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.';
808
- ctx.addMessage('assistant', planMsg);
809
- // Tell the LLM to wait for user approval
810
- ctx.llmMessages.current.push({
811
- role: 'tool',
812
- content: '[Plan displayed to user. Waiting for approval. The user will reply to approve and execute the plan (they may switch to work mode first), or provide feedback to revise it. Do NOT proceed with execution until the user approves.]',
813
- toolCallId: toolCall.id,
814
- });
815
- // Break out of tool loop - wait for user approval
816
- completedNaturally = true;
817
- }
818
- else {
819
- const display = result.displayResult || result.result;
820
- const preview = display.split('\n').slice(0, 5).join('\n');
821
- ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''), result.isError);
822
- }
823
- if (toolCall.name !== 'ask_question') {
824
- ctx.llmMessages.current.push({
825
- role: 'tool',
826
- content: truncateToolResult(result.result, modelLimit),
827
- toolCallId: toolCall.id,
828
- });
829
- }
830
- }
831
- }
302
+ },
303
+ onToolResult: (call, result, _iteration, output) => {
304
+ toolProgress.finish(call.id);
305
+ showToolProgress();
306
+ if (blocked.has(call.id))
307
+ return;
308
+ const args = call.arguments;
309
+ ctx.ledger?.recordAction(call.name, args, result.isError ? 'error' : 'ok', result.isError ? result.result : undefined);
310
+ if (call.name === 'think' && !result.isError)
311
+ ctx.addMessage('tool', output?.record.content ?? approvalDisplayText(String(args.thought || '')), false, output);
312
+ else if (call.name === 'ask_question' && !result.isError) {
313
+ let question = `❓ ${String(args.question || '')}`;
314
+ if (typeof args.context === 'string')
315
+ question += `\n ${args.context}`;
316
+ if (Array.isArray(args.options))
317
+ question += '\n' + args.options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
318
+ ctx.addMessage('assistant', question);
832
319
  }
833
- ctx.ledger?.endIteration();
834
- // Budget halt: the current tool result is finished; stop before the next
835
- // provider call. Record a budget_event and summarize spend vs cap.
836
- if (budgetVerdict?.exceeded) {
837
- const summary = formatBudgetHalt(budgetVerdict);
838
- runlog.budgetEvent({
839
- scope: budgetVerdict.scope ?? 'run',
840
- kind: budgetVerdict.kind ?? 'cost',
841
- spent: budgetVerdict.spent ?? 0,
842
- cap: budgetVerdict.cap ?? 0,
843
- message: budgetVerdict.message ?? summary,
844
- });
845
- ctx.addMessage('system', `‖ ${summary}`);
846
- runStatus = 'stopped';
847
- runErrorSummary = summary;
848
- runExitReason = 'budget';
849
- completedNaturally = true;
850
- break;
320
+ else if (call.name === 'create_plan' && !result.isError) {
321
+ let plan = `📋 Plan: ${String(args.title || 'Plan')}\n`;
322
+ if (typeof args.reasoning === 'string')
323
+ plan += `\n ${args.reasoning}\n`;
324
+ if (Array.isArray(args.steps))
325
+ plan += '\n' + args.steps.map((step, i) => ` ${i + 1}. [ ] ${step}`).join('\n');
326
+ ctx.addMessage('assistant', plan + '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.');
851
327
  }
852
- if (completedNaturally)
853
- break; // ask_question pauses for user input (#42)
854
- continue;
855
- }
856
- // Final response - move streaming content to message history
857
- ctx.setThinkingState(null);
858
- ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
859
- ctx.addMessage('assistant', response.content);
860
- // Mirror assistant message to the fleet channel
861
- if (fleetActive()) {
862
- fleetMirrorAssistant(response.content);
863
- }
864
- ctx.setStreamingResponse('');
865
- ctx.setContextTokens(ctx.estimateContextTokens());
866
- checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
867
- // Auto-continue if response was truncated due to length
868
- if (response.finishReason === 'length') {
869
- ctx.addMessage('system', '(auto-continuing...)');
870
- ctx.llmMessages.current.push({ role: 'user', content: 'Please continue where you left off.' });
871
- continue; // Loop again to get continuation
872
- }
873
- completedNaturally = true;
874
- runStatus = 'completed';
875
- // The answer is already shown; if this final turn crossed a budget cap,
876
- // still record it so the audit trail reflects why spend stopped here.
877
- if (budgetVerdict?.exceeded) {
878
- const summary = formatBudgetHalt(budgetVerdict);
879
- runlog.budgetEvent({
880
- scope: budgetVerdict.scope ?? 'run',
881
- kind: budgetVerdict.kind ?? 'cost',
882
- spent: budgetVerdict.spent ?? 0,
883
- cap: budgetVerdict.cap ?? 0,
884
- message: budgetVerdict.message ?? summary,
885
- });
886
- ctx.addMessage('system', `‖ ${summary}`);
887
- runExitReason = 'budget';
888
- }
889
- // End iteration ledger entry
890
- ctx.ledger?.endIteration('success');
891
- // Auto-save full message history for session persistence
892
- storage.saveMessageHistory(ctx.llmMessages.current);
893
- break;
894
- }
895
- catch (error) {
896
- streamFlusher.destroy(); // stop any pending flush timer on error/cancel
897
- ctx.setThinkingState(null);
898
- ctx.setActivityState(null);
899
- ctx.setStreamingResponse('');
900
- // End iteration ledger entry with error
901
- ctx.ledger?.endIteration('error');
902
- // Format error with provider context for better suggestions
903
- const errorMsg = formatError(error, { provider: ctx.actualProvider });
904
- ctx.addMessage('error', errorMsg);
905
- // Classify error to provide additional recovery suggestions
906
- const classified = classifyError(error);
907
- const availableProviders = getAvailableProviders();
908
- const otherProviders = availableProviders.filter(p => p !== ctx.actualProvider);
909
- // Feed error to circuit breaker
910
- if (ctx.circuitBreaker) {
911
- const errorIterData = {
912
- iteration: i + 1,
913
- error: errorMsg,
914
- timestamp: new Date(),
915
- };
916
- const breakerResult = ctx.circuitBreaker.check(errorIterData);
917
- ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
918
- if (breakerResult.tripped) {
919
- ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue.`);
920
- runStatus = 'stopped';
921
- runErrorSummary = breakerResult.message;
922
- completedNaturally = true;
923
- break;
328
+ else {
329
+ const display = output?.record.content ?? approvalDisplayText((result.displayResult || result.result).slice(0, 65536));
330
+ ctx.addMessage('tool', display, result.isError, output);
924
331
  }
925
- }
926
- // Retryable errors continue the loop (circuit breaker handles safety)
927
- const isRetryable = classified.category === 'rate_limit' || classified.category === 'server' || classified.category === 'timeout' || classified.category === 'network';
928
- // Suggest alternatives based on error type
929
- if (classified.category === 'rate_limit' || classified.category === 'server') {
930
- if (otherProviders.length > 0) {
931
- ctx.addMessage('system', `\u{1f4a1} Try switching providers: /provider ${otherProviders[0]} or /model to see alternatives`);
332
+ },
333
+ onIterationEnd: () => { ctx.ledger?.endIteration(finalResponse ? 'success' : undefined); },
334
+ onError: (error, index) => {
335
+ clearDisplay();
336
+ ctx.ledger?.endIteration('error');
337
+ const message = approvalDisplayText(formatError(error, { provider, retrying: false }));
338
+ ctx.addMessage('error', message);
339
+ errorShown = true;
340
+ runErrorSummary = message;
341
+ if (ctx.circuitBreaker) {
342
+ const breaker = ctx.circuitBreaker.check({ iteration: index, error: message, timestamp: new Date() });
343
+ ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
344
+ if (breaker.tripped) {
345
+ ctx.addMessage('system', `⚠️ Circuit breaker tripped: ${breaker.breaker}\n${breaker.message}\n\nUse /breaker resume to continue.`);
346
+ return 'stop';
347
+ }
932
348
  }
933
- }
934
- else if (classified.category === 'timeout' || classified.category === 'network') {
935
- ctx.addMessage('system', `\u{1f4a1} Network issue detected. Check connection and try again, or use /provider to switch.`);
936
- }
937
- else if (classified.category === 'auth') {
938
- ctx.addMessage('system', `\u{1f4a1} Run 'calliope --setup' to reconfigure API keys.`);
939
- }
940
- if (isRetryable && ctx.circuitBreaker) {
941
- // Retryable errors: continue the loop, circuit breaker will catch repeated failures
942
- ctx.addMessage('system', `Retrying... (circuit breaker will pause after ${ctx.circuitBreaker.getConfig().breakers['repeated-failure'].maxConsecutiveErrors} consecutive failures)`);
943
- await new Promise(r => setTimeout(r, 2000)); // Brief delay before retry
944
- continue;
945
- }
946
- // Non-retryable errors (auth, etc.) still kill the session
947
- completedNaturally = true;
948
- runStatus = 'failed';
949
- runErrorSummary = errorMsg;
950
- // On error, clear queued messages to prevent infinite retry loop
951
- const currentQueuedOnError = ctx.queuedMessagesRef.current;
952
- if (currentQueuedOnError.length > 0) {
953
- ctx.addMessage('system', `\u26a0\ufe0f Cleared ${currentQueuedOnError.length} queued message(s) due to error. Use /clear to reset conversation.`);
954
- ctx.setQueuedMessages([]);
955
- }
956
- if (runId && runStatus) {
957
- ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
958
- }
959
- finalizeRun(runExitReason ?? runStatus ?? 'error');
960
- return; // Exit early on error - don't process queued messages
349
+ const { category } = classifyError(error);
350
+ const others = getAvailableProviders().filter(p => p !== provider);
351
+ if (['rate_limit', 'server'].includes(category) && others.length)
352
+ ctx.addMessage('system', `💡 Try switching providers: /provider ${others[0]} or /model to see alternatives`);
353
+ else if (['timeout', 'network'].includes(category))
354
+ ctx.addMessage('system', '💡 Network issue detected. Check connection and try again, or use /provider to switch.');
355
+ else if (category === 'auth')
356
+ ctx.addMessage('system', "💡 Run 'calliope --setup' to reconfigure API keys.");
357
+ if (ctx.circuitBreaker && ['rate_limit', 'server', 'timeout', 'network'].includes(category)) {
358
+ ctx.addMessage('system', `Retrying... (circuit breaker will pause after ${ctx.circuitBreaker.getConfig().breakers['repeated-failure'].maxConsecutiveErrors} consecutive failures)`);
359
+ return 'retry';
360
+ }
361
+ return undefined;
362
+ },
363
+ });
364
+ runStatus = ['completed', 'waiting_for_user'].includes(result.reason) ? 'completed' : 'stopped';
365
+ if (result.reason === 'cancelled') {
366
+ runErrorSummary = 'Operation cancelled';
367
+ ctx.ledger?.endIteration('error');
368
+ ctx.validateAndRepairMessages();
961
369
  }
962
- }
963
- // Only show warning if we actually hit the iteration limit (not errors or natural completion)
964
- if (!completedNaturally && isFiniteIterationLimit(maxIterations)) {
965
- ctx.addMessage('system', `⚠️ Reached ${maxIterations} iterations limit. Task may be incomplete. Adjust with /set maxIterations <number>.`);
966
- if (!runStatus) {
967
- runStatus = 'stopped';
370
+ else if (result.reason === 'iteration_limit') {
968
371
  runErrorSummary = `Reached ${maxIterations} iterations limit`;
372
+ ctx.addMessage('system', `⚠️ Reached ${maxIterations} iterations limit. Task may be incomplete. Adjust with /set maxIterations <number>.`);
969
373
  }
374
+ else if (result.reason === 'budget' && result.budget) {
375
+ runErrorSummary = formatBudgetHalt(result.budget);
376
+ ctx.addMessage('system', `‖ ${runErrorSummary}`);
377
+ }
378
+ if (runId)
379
+ ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
380
+ ctx.setContextTokens(ctx.estimateContextTokens());
381
+ if (ctx.mode === 'plan' && result.totals.toolCalls === 0 && result.reason !== 'cancelled')
382
+ ctx.addMessage('system', '⚠ Unverified plan — the agent read nothing to produce this. Ask it to verify (it can read files in plan mode), or treat claims as assumptions.');
383
+ return ['completed', 'waiting_for_user'].includes(result.reason);
970
384
  }
971
- if (runId) {
972
- ctx.ledger?.finishRun(runId, runStatus || 'completed', { errorSummary: runErrorSummary });
973
- }
974
- finalizeRun(runExitReason ?? runStatus ?? 'completed');
975
- // Update context tokens after agent run
976
- ctx.setContextTokens(ctx.estimateContextTokens());
977
- // Process any queued messages (human-in-the-loop feedback)
978
- // CRITICAL: Use ref to get current value, not stale closure
979
- // #224: a plan produced with zero tool calls carries no evidence — say so.
980
- if (ctx.mode === 'plan' && runToolCalls === 0) {
981
- ctx.addMessage('system', '⚠ Unverified plan — the agent read nothing to produce this. Ask it to verify (it can read files in plan mode), or treat claims as assumptions.');
385
+ catch (error) {
386
+ const recoveryFailure = error instanceof SessionRecoveryError;
387
+ const cancelled = !recoveryFailure && (ctx.signal?.aborted || isCancellation(error));
388
+ if (recoveryFailure || !cancelled && !errorShown)
389
+ ctx.addMessage('error', approvalDisplayText(formatError(error, { provider, retrying: false })));
390
+ if (runId)
391
+ ctx.ledger?.finishRun(runId, cancelled ? 'stopped' : 'failed', { errorSummary: cancelled ? 'Operation cancelled' : String(error) });
392
+ return false;
982
393
  }
983
- const currentQueued = ctx.queuedMessagesRef.current;
984
- ctx.debugLog('runAgent', 'EXIT loop', `queued=${currentQueued.length}`);
985
- if (currentQueued.length > 0) {
986
- const queued = [...currentQueued];
987
- ctx.setQueuedMessages([]); // Clear the queue
988
- ctx.queuedMessagesRef.current = []; // Also clear ref immediately
989
- // Combine queued messages into a single follow-up
990
- const followUp = queued.length === 1
991
- ? queued[0]
992
- : `[Multiple follow-up messages from user:]\n${queued.map((m, i) => `${i + 1}. ${m}`).join('\n')}`;
993
- ctx.addMessage('system', `📨 Processing ${queued.length} queued message${queued.length > 1 ? 's' : ''}...`);
994
- // Recursively run agent with follow-up
995
- // Use setTimeout to avoid stack overflow and allow UI to update
996
- // Note: handleSubmit's finally will set isProcessing=false, so we need to re-enable it
997
- ctx.debugLog('runAgent', 'SCHEDULING recursive call for queued messages');
998
- setTimeout(() => {
999
- ctx.debugLog('runAgent', 'RECURSIVE call starting');
1000
- ctx.setIsProcessing(true);
1001
- runAgentImpl(ctx, followUp).finally(() => {
1002
- ctx.setIsProcessing(false);
1003
- ctx.setThinkingState(null);
1004
- ctx.setActivityState(null);
1005
- ctx.setStreamingResponse('');
1006
- ctx.setEditingQueueIndex(null);
1007
- });
1008
- }, 100);
394
+ finally {
395
+ ctx.signal?.removeEventListener('abort', clearDisplay);
396
+ clearDisplay();
1009
397
  }
1010
- ctx.debugLog('runAgent', 'RETURN');
1011
398
  }
1012
399
  // ============================================================================
1013
400
  // Run Loop
@@ -1029,7 +416,7 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
1029
416
  try {
1030
417
  for (let i = 0; i < maxIter; i++) {
1031
418
  // Check if cancelled
1032
- if (ctx.loopCancelledRef.current) {
419
+ if (ctx.loopCancelledRef.current || ctx.signal?.aborted) {
1033
420
  ctx.addMessage('system', '🛑 Loop cancelled by user');
1034
421
  loopOutcome = 'cancelled';
1035
422
  break;
@@ -1043,7 +430,11 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
1043
430
  : `Continue working on the task: "${prompt}"\n\nThis is iteration ${i + 1}. Review what you've done so far and continue making progress.`;
1044
431
  try {
1045
432
  // Run the agent
1046
- await runAgentImpl(ctx, iterationPrompt);
433
+ if (!await runAgentImpl(ctx, iterationPrompt) || ctx.afterLoopTurn && !await ctx.afterLoopTurn()) {
434
+ loopOutcome = ctx.signal?.aborted ? 'cancelled' : 'error';
435
+ loopErrorSummary = 'Turn stopped; queued work and remaining loop iterations are paused';
436
+ break;
437
+ }
1047
438
  // Check for completion promise in the last assistant message
1048
439
  if (completionPromise) {
1049
440
  const lastMessage = ctx.llmMessages.current[ctx.llmMessages.current.length - 1];
@@ -1059,17 +450,22 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
1059
450
  }
1060
451
  }
1061
452
  // Check cancelled again after agent run
1062
- if (ctx.loopCancelledRef.current) {
453
+ if (ctx.loopCancelledRef.current || ctx.signal?.aborted) {
1063
454
  ctx.addMessage('system', '🛑 Loop cancelled by user');
1064
455
  loopOutcome = 'cancelled';
1065
456
  break;
1066
457
  }
1067
458
  // Small delay between iterations
1068
459
  if (i + 1 < maxIter) {
1069
- await new Promise(r => setTimeout(r, 500));
460
+ await cancellableDelay(500, ctx.signal);
1070
461
  }
1071
462
  }
1072
463
  catch (error) {
464
+ if (ctx.signal?.aborted || isCancellation(error)) {
465
+ ctx.addMessage('system', '🛑 Loop cancelled by user');
466
+ loopOutcome = 'cancelled';
467
+ break;
468
+ }
1073
469
  loopErrorSummary = error instanceof Error ? error.message : String(error);
1074
470
  ctx.addMessage('error', `Loop error: ${loopErrorSummary}`);
1075
471
  loopOutcome = 'error';