@calliopelabs/cli 2.3.0 → 3.0.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 (538) hide show
  1. package/README.md +45 -644
  2. package/dist/acp.d.ts +78 -0
  3. package/dist/acp.d.ts.map +1 -0
  4. package/dist/acp.js +572 -0
  5. package/dist/acp.js.map +1 -0
  6. package/dist/auto-compressor.d.ts.map +1 -1
  7. package/dist/auto-compressor.js +9 -5
  8. package/dist/auto-compressor.js.map +1 -1
  9. package/dist/bin.d.ts +9 -5
  10. package/dist/bin.d.ts.map +1 -1
  11. package/dist/bin.js +108 -76
  12. package/dist/bin.js.map +1 -1
  13. package/dist/budget.d.ts +64 -0
  14. package/dist/budget.d.ts.map +1 -0
  15. package/dist/budget.js +157 -0
  16. package/dist/budget.js.map +1 -0
  17. package/dist/checkpoint.d.ts +63 -30
  18. package/dist/checkpoint.d.ts.map +1 -1
  19. package/dist/checkpoint.js +249 -150
  20. package/dist/checkpoint.js.map +1 -1
  21. package/dist/circuit-breaker/breaker.js.map +1 -1
  22. package/dist/config.d.ts +86 -81
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +237 -305
  25. package/dist/config.js.map +1 -1
  26. package/dist/cost.d.ts +114 -0
  27. package/dist/cost.d.ts.map +1 -0
  28. package/dist/cost.js +499 -0
  29. package/dist/cost.js.map +1 -0
  30. package/dist/diff.d.ts.map +1 -1
  31. package/dist/diff.js +53 -11
  32. package/dist/diff.js.map +1 -1
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +33 -6
  35. package/dist/errors.js.map +1 -1
  36. package/dist/files.js.map +1 -1
  37. package/dist/fleet.d.ts +30 -0
  38. package/dist/fleet.d.ts.map +1 -0
  39. package/dist/fleet.js +86 -0
  40. package/dist/fleet.js.map +1 -0
  41. package/dist/fuzzy-search.d.ts.map +1 -1
  42. package/dist/fuzzy-search.js +2 -1
  43. package/dist/fuzzy-search.js.map +1 -1
  44. package/dist/git-status.js.map +1 -1
  45. package/dist/headless.d.ts +1 -2
  46. package/dist/headless.d.ts.map +1 -1
  47. package/dist/headless.js +222 -26
  48. package/dist/headless.js.map +1 -1
  49. package/dist/hooks.d.ts +8 -2
  50. package/dist/hooks.d.ts.map +1 -1
  51. package/dist/hooks.js +97 -11
  52. package/dist/hooks.js.map +1 -1
  53. package/dist/hud/api.d.ts +3 -18
  54. package/dist/hud/api.d.ts.map +1 -1
  55. package/dist/hud/api.js +9 -352
  56. package/dist/hud/api.js.map +1 -1
  57. package/dist/hud/palettes.d.ts.map +1 -1
  58. package/dist/hud/palettes.js +0 -180
  59. package/dist/hud/palettes.js.map +1 -1
  60. package/dist/hud/skins.d.ts.map +1 -1
  61. package/dist/hud/skins.js +0 -263
  62. package/dist/hud/skins.js.map +1 -1
  63. package/dist/hud/types.d.ts +2 -5
  64. package/dist/hud/types.d.ts.map +1 -1
  65. package/dist/index.d.ts +1 -4
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +1 -4
  68. package/dist/index.js.map +1 -1
  69. package/dist/iteration-ledger.d.ts.map +1 -1
  70. package/dist/iteration-ledger.js +3 -2
  71. package/dist/iteration-ledger.js.map +1 -1
  72. package/dist/local-model.d.ts +150 -0
  73. package/dist/local-model.d.ts.map +1 -0
  74. package/dist/local-model.js +408 -0
  75. package/dist/local-model.js.map +1 -0
  76. package/dist/markdown.d.ts.map +1 -1
  77. package/dist/markdown.js +32 -10
  78. package/dist/markdown.js.map +1 -1
  79. package/dist/mcp.d.ts +35 -5
  80. package/dist/mcp.d.ts.map +1 -1
  81. package/dist/mcp.js +190 -13
  82. package/dist/mcp.js.map +1 -1
  83. package/dist/memory.d.ts.map +1 -1
  84. package/dist/memory.js +6 -4
  85. package/dist/memory.js.map +1 -1
  86. package/dist/model-detection.d.ts +14 -1
  87. package/dist/model-detection.d.ts.map +1 -1
  88. package/dist/model-detection.js +308 -115
  89. package/dist/model-detection.js.map +1 -1
  90. package/dist/parallel-tools.d.ts +9 -1
  91. package/dist/parallel-tools.d.ts.map +1 -1
  92. package/dist/parallel-tools.js +6 -5
  93. package/dist/parallel-tools.js.map +1 -1
  94. package/dist/plugins.d.ts +68 -0
  95. package/dist/plugins.d.ts.map +1 -1
  96. package/dist/plugins.js +201 -3
  97. package/dist/plugins.js.map +1 -1
  98. package/dist/policy.d.ts +43 -0
  99. package/dist/policy.d.ts.map +1 -0
  100. package/dist/policy.js +139 -0
  101. package/dist/policy.js.map +1 -0
  102. package/dist/project-config.d.ts +1 -2
  103. package/dist/project-config.d.ts.map +1 -1
  104. package/dist/project-config.js +0 -3
  105. package/dist/project-config.js.map +1 -1
  106. package/dist/providers/anthropic.d.ts.map +1 -1
  107. package/dist/providers/anthropic.js +36 -2
  108. package/dist/providers/anthropic.js.map +1 -1
  109. package/dist/providers/bedrock.d.ts.map +1 -1
  110. package/dist/providers/bedrock.js +83 -22
  111. package/dist/providers/bedrock.js.map +1 -1
  112. package/dist/providers/compat.d.ts +21 -0
  113. package/dist/providers/compat.d.ts.map +1 -1
  114. package/dist/providers/compat.js +176 -6
  115. package/dist/providers/compat.js.map +1 -1
  116. package/dist/providers/google.js.map +1 -1
  117. package/dist/providers/index.d.ts +19 -4
  118. package/dist/providers/index.d.ts.map +1 -1
  119. package/dist/providers/index.js +62 -9
  120. package/dist/providers/index.js.map +1 -1
  121. package/dist/providers/ollama.d.ts +2 -2
  122. package/dist/providers/ollama.d.ts.map +1 -1
  123. package/dist/providers/ollama.js +26 -6
  124. package/dist/providers/ollama.js.map +1 -1
  125. package/dist/providers/openai.d.ts.map +1 -1
  126. package/dist/providers/openai.js +14 -10
  127. package/dist/providers/openai.js.map +1 -1
  128. package/dist/providers/types.d.ts +8 -0
  129. package/dist/providers/types.d.ts.map +1 -1
  130. package/dist/providers/types.js +19 -10
  131. package/dist/providers/types.js.map +1 -1
  132. package/dist/replay.d.ts +37 -0
  133. package/dist/replay.d.ts.map +1 -0
  134. package/dist/replay.js +168 -0
  135. package/dist/replay.js.map +1 -0
  136. package/dist/risk.d.ts.map +1 -1
  137. package/dist/risk.js +15 -5
  138. package/dist/risk.js.map +1 -1
  139. package/dist/router.d.ts +133 -0
  140. package/dist/router.d.ts.map +1 -0
  141. package/dist/{model-router.js → router.js} +336 -10
  142. package/dist/router.js.map +1 -0
  143. package/dist/runlog.d.ts +185 -0
  144. package/dist/runlog.d.ts.map +1 -0
  145. package/dist/runlog.js +398 -0
  146. package/dist/runlog.js.map +1 -0
  147. package/dist/{sandbox.d.ts → sandbox/docker.d.ts} +1 -1
  148. package/dist/sandbox/docker.d.ts.map +1 -0
  149. package/dist/{sandbox.js → sandbox/docker.js} +1 -1
  150. package/dist/sandbox/docker.js.map +1 -0
  151. package/dist/sandbox/index.d.ts +36 -0
  152. package/dist/sandbox/index.d.ts.map +1 -0
  153. package/dist/sandbox/index.js +62 -0
  154. package/dist/sandbox/index.js.map +1 -0
  155. package/dist/{sandbox-native.d.ts → sandbox/native.d.ts} +2 -1
  156. package/dist/sandbox/native.d.ts.map +1 -0
  157. package/dist/{sandbox-native.js → sandbox/native.js} +38 -6
  158. package/dist/sandbox/native.js.map +1 -0
  159. package/dist/scope.d.ts +10 -0
  160. package/dist/scope.d.ts.map +1 -1
  161. package/dist/scope.js +76 -16
  162. package/dist/scope.js.map +1 -1
  163. package/dist/scuttlebot/client.d.ts +83 -0
  164. package/dist/scuttlebot/client.d.ts.map +1 -0
  165. package/dist/scuttlebot/client.js +350 -0
  166. package/dist/scuttlebot/client.js.map +1 -0
  167. package/dist/scuttlebot/config.d.ts +28 -0
  168. package/dist/scuttlebot/config.d.ts.map +1 -0
  169. package/dist/scuttlebot/config.js +91 -0
  170. package/dist/scuttlebot/config.js.map +1 -0
  171. package/dist/scuttlebot/http-client.d.ts +63 -0
  172. package/dist/scuttlebot/http-client.d.ts.map +1 -0
  173. package/dist/scuttlebot/http-client.js +124 -0
  174. package/dist/scuttlebot/http-client.js.map +1 -0
  175. package/dist/scuttlebot/index.d.ts +13 -0
  176. package/dist/scuttlebot/index.d.ts.map +1 -0
  177. package/dist/scuttlebot/index.js +10 -0
  178. package/dist/scuttlebot/index.js.map +1 -0
  179. package/dist/scuttlebot/irc-client.d.ts +124 -0
  180. package/dist/scuttlebot/irc-client.d.ts.map +1 -0
  181. package/dist/scuttlebot/irc-client.js +599 -0
  182. package/dist/scuttlebot/irc-client.js.map +1 -0
  183. package/dist/setup.d.ts.map +1 -1
  184. package/dist/setup.js +22 -54
  185. package/dist/setup.js.map +1 -1
  186. package/dist/skills.d.ts +58 -0
  187. package/dist/skills.d.ts.map +1 -1
  188. package/dist/skills.js +186 -16
  189. package/dist/skills.js.map +1 -1
  190. package/dist/storage.d.ts +0 -4
  191. package/dist/storage.d.ts.map +1 -1
  192. package/dist/storage.js +87 -8
  193. package/dist/storage.js.map +1 -1
  194. package/dist/streaming.d.ts +25 -0
  195. package/dist/streaming.d.ts.map +1 -1
  196. package/dist/streaming.js +59 -0
  197. package/dist/streaming.js.map +1 -1
  198. package/dist/styles.d.ts +6 -0
  199. package/dist/styles.d.ts.map +1 -1
  200. package/dist/styles.js +11 -0
  201. package/dist/styles.js.map +1 -1
  202. package/dist/summarization.js.map +1 -1
  203. package/dist/terminal-image.js +1 -1
  204. package/dist/terminal-image.js.map +1 -1
  205. package/dist/themes.d.ts +5 -4
  206. package/dist/themes.d.ts.map +1 -1
  207. package/dist/themes.js +35 -90
  208. package/dist/themes.js.map +1 -1
  209. package/dist/tools.d.ts +27 -3
  210. package/dist/tools.d.ts.map +1 -1
  211. package/dist/tools.js +352 -209
  212. package/dist/tools.js.map +1 -1
  213. package/dist/trust.d.ts +16 -3
  214. package/dist/trust.d.ts.map +1 -1
  215. package/dist/trust.js +23 -4
  216. package/dist/trust.js.map +1 -1
  217. package/dist/types.d.ts +4 -3
  218. package/dist/types.d.ts.map +1 -1
  219. package/dist/types.js +34 -44
  220. package/dist/types.js.map +1 -1
  221. package/dist/ui/agent.d.ts +4 -7
  222. package/dist/ui/agent.d.ts.map +1 -1
  223. package/dist/ui/agent.js +354 -73
  224. package/dist/ui/agent.js.map +1 -1
  225. package/dist/ui/animations.d.ts +37 -0
  226. package/dist/ui/animations.d.ts.map +1 -0
  227. package/dist/ui/animations.js +227 -0
  228. package/dist/ui/animations.js.map +1 -0
  229. package/dist/ui/chat-input-keys.d.ts +42 -0
  230. package/dist/ui/chat-input-keys.d.ts.map +1 -0
  231. package/dist/ui/chat-input-keys.js +360 -0
  232. package/dist/ui/chat-input-keys.js.map +1 -0
  233. package/dist/ui/chat-input.d.ts +5 -2
  234. package/dist/ui/chat-input.d.ts.map +1 -1
  235. package/dist/ui/chat-input.js +54 -308
  236. package/dist/ui/chat-input.js.map +1 -1
  237. package/dist/ui/commands.d.ts +15 -45
  238. package/dist/ui/commands.d.ts.map +1 -1
  239. package/dist/ui/commands.js +461 -2636
  240. package/dist/ui/commands.js.map +1 -1
  241. package/dist/ui/completions.d.ts +1 -0
  242. package/dist/ui/completions.d.ts.map +1 -1
  243. package/dist/ui/completions.js +36 -107
  244. package/dist/ui/completions.js.map +1 -1
  245. package/dist/ui/components.d.ts +14 -6
  246. package/dist/ui/components.d.ts.map +1 -1
  247. package/dist/ui/components.js +17 -226
  248. package/dist/ui/components.js.map +1 -1
  249. package/dist/ui/context.js +6 -6
  250. package/dist/ui/context.js.map +1 -1
  251. package/dist/ui/debug-log.d.ts +11 -0
  252. package/dist/ui/debug-log.d.ts.map +1 -0
  253. package/dist/ui/debug-log.js +21 -0
  254. package/dist/ui/debug-log.js.map +1 -0
  255. package/dist/ui/error-boundary.d.ts.map +1 -1
  256. package/dist/ui/error-boundary.js.map +1 -1
  257. package/dist/ui/frame.d.ts.map +1 -1
  258. package/dist/ui/frame.js +1 -3
  259. package/dist/ui/frame.js.map +1 -1
  260. package/dist/ui/index.d.ts +11 -3
  261. package/dist/ui/index.d.ts.map +1 -1
  262. package/dist/ui/index.js +46 -887
  263. package/dist/ui/index.js.map +1 -1
  264. package/dist/ui/input-utils.d.ts +20 -0
  265. package/dist/ui/input-utils.d.ts.map +1 -0
  266. package/dist/ui/input-utils.js +35 -0
  267. package/dist/ui/input-utils.js.map +1 -0
  268. package/dist/ui/messages.d.ts +8 -7
  269. package/dist/ui/messages.d.ts.map +1 -1
  270. package/dist/ui/messages.js +51 -35
  271. package/dist/ui/messages.js.map +1 -1
  272. package/dist/ui/modals/index.d.ts +11 -0
  273. package/dist/ui/modals/index.d.ts.map +1 -0
  274. package/dist/ui/modals/index.js +10 -0
  275. package/dist/ui/modals/index.js.map +1 -0
  276. package/dist/ui/{modals.d.ts → modals/prompts.d.ts} +5 -20
  277. package/dist/ui/modals/prompts.d.ts.map +1 -0
  278. package/dist/ui/{modals.js → modals/prompts.js} +14 -94
  279. package/dist/ui/modals/prompts.js.map +1 -0
  280. package/dist/ui/modals/selectors.d.ts +33 -0
  281. package/dist/ui/modals/selectors.d.ts.map +1 -0
  282. package/dist/ui/modals/selectors.js +111 -0
  283. package/dist/ui/modals/selectors.js.map +1 -0
  284. package/dist/ui/modals/setup.d.ts +17 -0
  285. package/dist/ui/modals/setup.d.ts.map +1 -0
  286. package/dist/ui/modals/setup.js +51 -0
  287. package/dist/ui/modals/setup.js.map +1 -0
  288. package/dist/ui/regions/input-region.d.ts +40 -0
  289. package/dist/ui/regions/input-region.d.ts.map +1 -0
  290. package/dist/ui/regions/input-region.js +116 -0
  291. package/dist/ui/regions/input-region.js.map +1 -0
  292. package/dist/ui/regions/modal-host.d.ts +41 -0
  293. package/dist/ui/regions/modal-host.d.ts.map +1 -0
  294. package/dist/ui/regions/modal-host.js +51 -0
  295. package/dist/ui/regions/modal-host.js.map +1 -0
  296. package/dist/ui/regions/render-probe.d.ts +22 -0
  297. package/dist/ui/regions/render-probe.d.ts.map +1 -0
  298. package/dist/ui/regions/render-probe.js +45 -0
  299. package/dist/ui/regions/render-probe.js.map +1 -0
  300. package/dist/ui/regions/static-scrollback.d.ts +39 -0
  301. package/dist/ui/regions/static-scrollback.d.ts.map +1 -0
  302. package/dist/ui/regions/static-scrollback.js +68 -0
  303. package/dist/ui/regions/static-scrollback.js.map +1 -0
  304. package/dist/ui/regions/status-region.d.ts +24 -0
  305. package/dist/ui/regions/status-region.d.ts.map +1 -0
  306. package/dist/ui/regions/status-region.js +17 -0
  307. package/dist/ui/regions/status-region.js.map +1 -0
  308. package/dist/ui/regions/transcript-region.d.ts +41 -0
  309. package/dist/ui/regions/transcript-region.d.ts.map +1 -0
  310. package/dist/ui/regions/transcript-region.js +52 -0
  311. package/dist/ui/regions/transcript-region.js.map +1 -0
  312. package/dist/ui/self-restart.d.ts +9 -0
  313. package/dist/ui/self-restart.d.ts.map +1 -0
  314. package/dist/ui/self-restart.js +24 -0
  315. package/dist/ui/self-restart.js.map +1 -0
  316. package/dist/ui/state/use-chat-controller.d.ts +23 -0
  317. package/dist/ui/state/use-chat-controller.d.ts.map +1 -0
  318. package/dist/ui/state/use-chat-controller.js +643 -0
  319. package/dist/ui/state/use-chat-controller.js.map +1 -0
  320. package/dist/ui/state/use-loop-state.d.ts +23 -0
  321. package/dist/ui/state/use-loop-state.d.ts.map +1 -0
  322. package/dist/ui/state/use-loop-state.js +39 -0
  323. package/dist/ui/state/use-loop-state.js.map +1 -0
  324. package/dist/ui/state/use-modal-state.d.ts +52 -0
  325. package/dist/ui/state/use-modal-state.d.ts.map +1 -0
  326. package/dist/ui/state/use-modal-state.js +44 -0
  327. package/dist/ui/state/use-modal-state.js.map +1 -0
  328. package/dist/ui/state/use-model-state.d.ts +29 -0
  329. package/dist/ui/state/use-model-state.d.ts.map +1 -0
  330. package/dist/ui/state/use-model-state.js +47 -0
  331. package/dist/ui/state/use-model-state.js.map +1 -0
  332. package/dist/ui/state/use-processing-state.d.ts +24 -0
  333. package/dist/ui/state/use-processing-state.d.ts.map +1 -0
  334. package/dist/ui/state/use-processing-state.js +46 -0
  335. package/dist/ui/state/use-processing-state.js.map +1 -0
  336. package/dist/ui/state/use-queue-state.d.ts +19 -0
  337. package/dist/ui/state/use-queue-state.d.ts.map +1 -0
  338. package/dist/ui/state/use-queue-state.js +30 -0
  339. package/dist/ui/state/use-queue-state.js.map +1 -0
  340. package/dist/ui/state/use-session-init.d.ts +22 -0
  341. package/dist/ui/state/use-session-init.d.ts.map +1 -0
  342. package/dist/ui/state/use-session-init.js +77 -0
  343. package/dist/ui/state/use-session-init.js.map +1 -0
  344. package/dist/ui/state/use-session-stats.d.ts +16 -0
  345. package/dist/ui/state/use-session-stats.d.ts.map +1 -0
  346. package/dist/ui/state/use-session-stats.js +18 -0
  347. package/dist/ui/state/use-session-stats.js.map +1 -0
  348. package/dist/ui/state/use-terminal-width.d.ts +8 -0
  349. package/dist/ui/state/use-terminal-width.d.ts.map +1 -0
  350. package/dist/ui/state/use-terminal-width.js +22 -0
  351. package/dist/ui/state/use-terminal-width.js.map +1 -0
  352. package/dist/ui/state/use-transcript-state.d.ts +21 -0
  353. package/dist/ui/state/use-transcript-state.d.ts.map +1 -0
  354. package/dist/ui/state/use-transcript-state.js +49 -0
  355. package/dist/ui/state/use-transcript-state.js.map +1 -0
  356. package/dist/ui/status-bar.d.ts +4 -1
  357. package/dist/ui/status-bar.d.ts.map +1 -1
  358. package/dist/ui/status-bar.js +13 -3
  359. package/dist/ui/status-bar.js.map +1 -1
  360. package/dist/ui/types.d.ts +3 -0
  361. package/dist/ui/types.d.ts.map +1 -1
  362. package/dist/version-check.d.ts.map +1 -1
  363. package/dist/version-check.js +10 -0
  364. package/dist/version-check.js.map +1 -1
  365. package/package.json +12 -8
  366. package/dist/agents/agent-config-loader.d.ts +0 -60
  367. package/dist/agents/agent-config-loader.d.ts.map +0 -1
  368. package/dist/agents/agent-config-loader.js +0 -402
  369. package/dist/agents/agent-config-loader.js.map +0 -1
  370. package/dist/agents/agent-config-presets.d.ts +0 -10
  371. package/dist/agents/agent-config-presets.d.ts.map +0 -1
  372. package/dist/agents/agent-config-presets.js +0 -940
  373. package/dist/agents/agent-config-presets.js.map +0 -1
  374. package/dist/agents/agent-config-types.d.ts +0 -145
  375. package/dist/agents/agent-config-types.d.ts.map +0 -1
  376. package/dist/agents/agent-config-types.js +0 -12
  377. package/dist/agents/agent-config-types.js.map +0 -1
  378. package/dist/agents/agent-detection.d.ts +0 -35
  379. package/dist/agents/agent-detection.d.ts.map +0 -1
  380. package/dist/agents/agent-detection.js +0 -110
  381. package/dist/agents/agent-detection.js.map +0 -1
  382. package/dist/agents/aggregator.d.ts +0 -19
  383. package/dist/agents/aggregator.d.ts.map +0 -1
  384. package/dist/agents/aggregator.js +0 -141
  385. package/dist/agents/aggregator.js.map +0 -1
  386. package/dist/agents/cli-backend.d.ts +0 -32
  387. package/dist/agents/cli-backend.d.ts.map +0 -1
  388. package/dist/agents/cli-backend.js +0 -271
  389. package/dist/agents/cli-backend.js.map +0 -1
  390. package/dist/agents/council-types.d.ts +0 -115
  391. package/dist/agents/council-types.d.ts.map +0 -1
  392. package/dist/agents/council-types.js +0 -81
  393. package/dist/agents/council-types.js.map +0 -1
  394. package/dist/agents/council.d.ts +0 -112
  395. package/dist/agents/council.d.ts.map +0 -1
  396. package/dist/agents/council.js +0 -722
  397. package/dist/agents/council.js.map +0 -1
  398. package/dist/agents/decomposer.d.ts +0 -33
  399. package/dist/agents/decomposer.d.ts.map +0 -1
  400. package/dist/agents/decomposer.js +0 -138
  401. package/dist/agents/decomposer.js.map +0 -1
  402. package/dist/agents/dynamic-tools.d.ts +0 -52
  403. package/dist/agents/dynamic-tools.d.ts.map +0 -1
  404. package/dist/agents/dynamic-tools.js +0 -395
  405. package/dist/agents/dynamic-tools.js.map +0 -1
  406. package/dist/agents/index.d.ts +0 -29
  407. package/dist/agents/index.d.ts.map +0 -1
  408. package/dist/agents/index.js +0 -29
  409. package/dist/agents/index.js.map +0 -1
  410. package/dist/agents/installer.d.ts +0 -39
  411. package/dist/agents/installer.d.ts.map +0 -1
  412. package/dist/agents/installer.js +0 -205
  413. package/dist/agents/installer.js.map +0 -1
  414. package/dist/agents/orchestrator.d.ts +0 -106
  415. package/dist/agents/orchestrator.d.ts.map +0 -1
  416. package/dist/agents/orchestrator.js +0 -363
  417. package/dist/agents/orchestrator.js.map +0 -1
  418. package/dist/agents/sdk-backend.d.ts +0 -63
  419. package/dist/agents/sdk-backend.d.ts.map +0 -1
  420. package/dist/agents/sdk-backend.js +0 -489
  421. package/dist/agents/sdk-backend.js.map +0 -1
  422. package/dist/agents/swarm-types.d.ts +0 -84
  423. package/dist/agents/swarm-types.d.ts.map +0 -1
  424. package/dist/agents/swarm-types.js +0 -20
  425. package/dist/agents/swarm-types.js.map +0 -1
  426. package/dist/agents/swarm.d.ts +0 -79
  427. package/dist/agents/swarm.d.ts.map +0 -1
  428. package/dist/agents/swarm.js +0 -375
  429. package/dist/agents/swarm.js.map +0 -1
  430. package/dist/agents/tools.d.ts +0 -26
  431. package/dist/agents/tools.d.ts.map +0 -1
  432. package/dist/agents/tools.js +0 -776
  433. package/dist/agents/tools.js.map +0 -1
  434. package/dist/agents/types.d.ts +0 -108
  435. package/dist/agents/types.d.ts.map +0 -1
  436. package/dist/agents/types.js +0 -43
  437. package/dist/agents/types.js.map +0 -1
  438. package/dist/api-server.d.ts +0 -36
  439. package/dist/api-server.d.ts.map +0 -1
  440. package/dist/api-server.js +0 -255
  441. package/dist/api-server.js.map +0 -1
  442. package/dist/auto-checkpoint.d.ts +0 -35
  443. package/dist/auto-checkpoint.d.ts.map +0 -1
  444. package/dist/auto-checkpoint.js +0 -143
  445. package/dist/auto-checkpoint.js.map +0 -1
  446. package/dist/background-jobs.d.ts +0 -45
  447. package/dist/background-jobs.d.ts.map +0 -1
  448. package/dist/background-jobs.js +0 -127
  449. package/dist/background-jobs.js.map +0 -1
  450. package/dist/branching.d.ts +0 -56
  451. package/dist/branching.d.ts.map +0 -1
  452. package/dist/branching.js +0 -211
  453. package/dist/branching.js.map +0 -1
  454. package/dist/cli/agent.d.ts +0 -9
  455. package/dist/cli/agent.d.ts.map +0 -1
  456. package/dist/cli/agent.js +0 -314
  457. package/dist/cli/agent.js.map +0 -1
  458. package/dist/cli/commands.d.ts +0 -12
  459. package/dist/cli/commands.d.ts.map +0 -1
  460. package/dist/cli/commands.js +0 -943
  461. package/dist/cli/commands.js.map +0 -1
  462. package/dist/cli/index.d.ts +0 -8
  463. package/dist/cli/index.d.ts.map +0 -1
  464. package/dist/cli/index.js +0 -237
  465. package/dist/cli/index.js.map +0 -1
  466. package/dist/cli/types.d.ts +0 -33
  467. package/dist/cli/types.d.ts.map +0 -1
  468. package/dist/cli/types.js +0 -21
  469. package/dist/cli/types.js.map +0 -1
  470. package/dist/companions.d.ts +0 -54
  471. package/dist/companions.d.ts.map +0 -1
  472. package/dist/companions.js +0 -440
  473. package/dist/companions.js.map +0 -1
  474. package/dist/completion.d.ts +0 -75
  475. package/dist/completion.d.ts.map +0 -1
  476. package/dist/completion.js +0 -234
  477. package/dist/completion.js.map +0 -1
  478. package/dist/file-watcher.d.ts +0 -91
  479. package/dist/file-watcher.d.ts.map +0 -1
  480. package/dist/file-watcher.js +0 -269
  481. package/dist/file-watcher.js.map +0 -1
  482. package/dist/hud/theme-packs/api.d.ts +0 -51
  483. package/dist/hud/theme-packs/api.d.ts.map +0 -1
  484. package/dist/hud/theme-packs/api.js +0 -145
  485. package/dist/hud/theme-packs/api.js.map +0 -1
  486. package/dist/hud/theme-packs/index.d.ts +0 -18
  487. package/dist/hud/theme-packs/index.d.ts.map +0 -1
  488. package/dist/hud/theme-packs/index.js +0 -38
  489. package/dist/hud/theme-packs/index.js.map +0 -1
  490. package/dist/hud/theme-packs/types.d.ts +0 -29
  491. package/dist/hud/theme-packs/types.d.ts.map +0 -1
  492. package/dist/hud/theme-packs/types.js +0 -9
  493. package/dist/hud/theme-packs/types.js.map +0 -1
  494. package/dist/idle-eviction.d.ts +0 -34
  495. package/dist/idle-eviction.d.ts.map +0 -1
  496. package/dist/idle-eviction.js +0 -78
  497. package/dist/idle-eviction.js.map +0 -1
  498. package/dist/keyboard.d.ts +0 -57
  499. package/dist/keyboard.d.ts.map +0 -1
  500. package/dist/keyboard.js +0 -265
  501. package/dist/keyboard.js.map +0 -1
  502. package/dist/model-router.d.ts +0 -67
  503. package/dist/model-router.d.ts.map +0 -1
  504. package/dist/model-router.js.map +0 -1
  505. package/dist/providers/openai-compat-shims.d.ts +0 -31
  506. package/dist/providers/openai-compat-shims.d.ts.map +0 -1
  507. package/dist/providers/openai-compat-shims.js +0 -179
  508. package/dist/providers/openai-compat-shims.js.map +0 -1
  509. package/dist/sandbox-native.d.ts.map +0 -1
  510. package/dist/sandbox-native.js.map +0 -1
  511. package/dist/sandbox.d.ts.map +0 -1
  512. package/dist/sandbox.js.map +0 -1
  513. package/dist/session-timeout.d.ts +0 -31
  514. package/dist/session-timeout.d.ts.map +0 -1
  515. package/dist/session-timeout.js +0 -100
  516. package/dist/session-timeout.js.map +0 -1
  517. package/dist/smart-router.d.ts +0 -73
  518. package/dist/smart-router.d.ts.map +0 -1
  519. package/dist/smart-router.js +0 -332
  520. package/dist/smart-router.js.map +0 -1
  521. package/dist/terminal-recording.d.ts +0 -55
  522. package/dist/terminal-recording.d.ts.map +0 -1
  523. package/dist/terminal-recording.js +0 -182
  524. package/dist/terminal-recording.js.map +0 -1
  525. package/dist/tmux.d.ts +0 -35
  526. package/dist/tmux.d.ts.map +0 -1
  527. package/dist/tmux.js +0 -106
  528. package/dist/tmux.js.map +0 -1
  529. package/dist/ui/modals.d.ts.map +0 -1
  530. package/dist/ui/modals.js.map +0 -1
  531. package/dist/ui/pack-picker.d.ts +0 -12
  532. package/dist/ui/pack-picker.d.ts.map +0 -1
  533. package/dist/ui/pack-picker.js +0 -101
  534. package/dist/ui/pack-picker.js.map +0 -1
  535. package/dist/ui/theme-picker.d.ts +0 -24
  536. package/dist/ui/theme-picker.d.ts.map +0 -1
  537. package/dist/ui/theme-picker.js +0 -190
  538. package/dist/ui/theme-picker.js.map +0 -1
package/dist/ui/agent.js CHANGED
@@ -15,16 +15,21 @@ import { formatError, classifyError } from '../errors.js';
15
15
  import { getAvailableProviders } from '../providers/index.js';
16
16
  import * as storage from '../storage.js';
17
17
  import * as hooks from '../hooks.js';
18
- import * as modelRouter from '../model-router.js';
18
+ import * as router from '../router.js';
19
+ import { fleetActive, fleetMirrorAssistant } from '../fleet.js';
19
20
  import * as summarization from '../summarization.js';
21
+ import { autoCompress } from '../auto-compressor.js';
22
+ import { createStreamFlusher } from '../streaming.js';
20
23
  import { executeParallel, getParallelizationStats } from '../parallel-tools.js';
21
- import { setMood } from '../companions.js';
22
24
  import { checkAndWarnContextLimit } from './context.js';
23
- import { smartRoute } from '../smart-router.js';
24
- import { shouldCheckpoint, createCheckpoint } from '../auto-checkpoint.js';
25
- import { recordEvent } from '../terminal-recording.js';
25
+ import { smartRoute } from '../router.js';
26
+ import { isLocalBackend, detectMalformedToolCall, buildRepairMessage, buildToolCallEnvelopeSchema, extractRepairedToolCall, getLocalModelProfile, } from '../local-model.js';
27
+ import { shouldCheckpoint, createCheckpoint } from '../checkpoint.js';
26
28
  import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
27
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';
28
33
  // ============================================================================
29
34
  // Tool Result Truncation
30
35
  // ============================================================================
@@ -99,15 +104,116 @@ export function validateAndRepairMessagesImpl(ctx) {
99
104
  return repaired;
100
105
  }
101
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
+ 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
+ // ============================================================================
102
201
  // Run Agent
103
202
  // ============================================================================
104
203
  /**
105
204
  * Run agent with user prompt. Core execution loop that handles LLM calls,
106
205
  * tool execution (parallel + sequential), auto-compaction, and queued messages.
107
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
+ let previousTurnMode = null;
211
+ /** Test seam: clear the plan-to-work transition tracking. */
212
+ export function _resetModeTracking() {
213
+ previousTurnMode = null;
214
+ }
108
215
  export async function runAgentImpl(ctx, content) {
109
216
  ctx.debugLog('runAgent', 'ENTER', typeof content === 'string' ? content.substring(0, 50) : '[complex]');
110
- setMood('thinking');
111
217
  // Validate message history before adding new content
112
218
  ctx.validateAndRepairMessages();
113
219
  ctx.llmMessages.current.push({ role: 'user', content });
@@ -128,7 +234,7 @@ export async function runAgentImpl(ctx, content) {
128
234
  }
129
235
  }
130
236
  else if (ctx.autoRoute && typeof content === 'string') {
131
- const routeDecision = modelRouter.routeRequest(content, ctx.provider, {
237
+ const routeDecision = router.routeRequest(content, ctx.provider, {
132
238
  messageCount: ctx.stats.messageCount,
133
239
  hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
134
240
  });
@@ -150,6 +256,57 @@ export async function runAgentImpl(ctx, content) {
150
256
  : undefined;
151
257
  let runStatus;
152
258
  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
+ const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
268
+ 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.
292
+ const justLeftPlanMode = previousTurnMode === 'plan' && ctx.mode !== 'plan';
293
+ 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();
309
+ };
153
310
  // Check context limit and warn if approaching capacity
154
311
  // Uses model's actual context length from API when available
155
312
  let currentContextTokens = ctx.estimateContextTokens();
@@ -157,36 +314,18 @@ export async function runAgentImpl(ctx, content) {
157
314
  let contextPercentage = (currentContextTokens / modelLimit) * 100;
158
315
  // Adaptive preserveRecent: small models keep fewer messages to leave room for output
159
316
  const preserveRecent = modelLimit < 8000 ? 2 : modelLimit < 16000 ? 4 : modelLimit < 32000 ? 6 : modelLimit < 64000 ? 10 : 15;
160
- // Auto-compact if we're over 75% capacity to prevent API errors
161
- if (contextPercentage > 75) {
162
- ctx.addMessage('system', `🔄 Context at ${Math.round(contextPercentage)}% - auto-compacting to prevent errors...`);
163
- const result = summarization.summarizeConversation(ctx.llmMessages.current, {
164
- maxTokens: Math.floor(modelLimit * 0.7), // Target 70% of limit after compaction
165
- preserveRecent,
166
- });
167
- if (result.summarizedCount > 0) {
168
- ctx.llmMessages.current = result.messages;
169
- currentContextTokens = ctx.estimateContextTokens();
170
- contextPercentage = (currentContextTokens / modelLimit) * 100;
171
- ctx.setContextTokens(currentContextTokens);
172
- ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages. Now at ${Math.round(contextPercentage)}% (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K)`);
173
- }
174
- else {
175
- // If compaction didn't help enough, force-trim old messages
176
- if (contextPercentage > 98) {
177
- const systemMsgs = ctx.llmMessages.current.filter(m => m.role === 'system');
178
- const recentMsgs = ctx.llmMessages.current.filter(m => m.role !== 'system').slice(-5);
179
- ctx.llmMessages.current = [...systemMsgs, ...recentMsgs];
180
- currentContextTokens = ctx.estimateContextTokens();
181
- contextPercentage = (currentContextTokens / modelLimit) * 100;
182
- ctx.setContextTokens(currentContextTokens);
183
- ctx.addMessage('system', `⚠️ Force-trimmed to last 5 messages (${Math.round(contextPercentage)}%). Use /clear for a full reset.`);
184
- }
185
- }
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)`);
186
325
  }
187
326
  else if (contextPercentage > 65) {
188
327
  ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
189
- Consider: /summarize compact | /clear | shorter messages`);
328
+ Consider: /compact | /clear | shorter messages`);
190
329
  }
191
330
  // Inject failed approaches into context so the agent avoids repeating mistakes
192
331
  if (ctx.ledger) {
@@ -203,20 +342,21 @@ export async function runAgentImpl(ctx, content) {
203
342
  // Safety check at start of each iteration - context may have grown from tool results
204
343
  if (i > 0) {
205
344
  const iterContextTokens = ctx.estimateContextTokens();
206
- const iterContextPercentage = (iterContextTokens / modelLimit) * 100;
207
- if (iterContextPercentage > 75) {
208
- ctx.addMessage('system', `🔄 Context grew to ${Math.round(iterContextPercentage)}% - auto-compacting...`);
209
- const result = summarization.summarizeConversation(ctx.llmMessages.current, {
210
- maxTokens: Math.floor(modelLimit * 0.7),
211
- preserveRecent,
212
- });
213
- if (result.summarizedCount > 0) {
214
- ctx.llmMessages.current = result.messages;
215
- ctx.setContextTokens(ctx.estimateContextTokens());
216
- ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages during iteration ${i + 1}`);
217
- }
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})`);
218
350
  }
219
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
+ });
220
360
  try {
221
361
  // Update thinking state for LLM call
222
362
  ctx.setThinkingState({
@@ -230,10 +370,15 @@ export async function runAgentImpl(ctx, content) {
230
370
  target: `iteration ${i + 1}`,
231
371
  startTime: Date.now(),
232
372
  });
233
- // Streaming callback for final response
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;
234
376
  const onToken = (token) => {
235
- ctx.setThinkingState(null); // Clear thinking when streaming starts
236
- ctx.setStreamingResponse(prev => prev + token);
377
+ if (!streamStarted) {
378
+ ctx.setThinkingState(null); // Clear thinking when streaming starts
379
+ streamStarted = true;
380
+ }
381
+ streamFlusher.push(token);
237
382
  };
238
383
  // Retry callback for error recovery
239
384
  const onRetry = (attempt, error, delayMs) => {
@@ -251,8 +396,36 @@ export async function runAgentImpl(ctx, content) {
251
396
  ctx.debugLog('chat', 'CLEANED orphaned tool results', `removed=${ctx.llmMessages.current.length - validatedMessages.length}`);
252
397
  ctx.llmMessages.current = validatedMessages;
253
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. '
408
+ + 'Read-only tools (read_file, list_files, think, create_plan, ask_question) ARE available — use them. '
409
+ + 'Before proposing any plan, read the files you intend to change and cite file:line for every claim. '
410
+ + '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. '
421
+ + 'Treat their message as approval to execute the plan discussed above. '
422
+ + '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
+ }
254
427
  // Pre-request summarization check - summarize BEFORE sending if context is too large
255
- const tools = getTools(ctx.agtermEnabled);
428
+ const tools = getTools();
256
429
  const contextCheck = estimateContextUsage(ctx.provider, effectiveModel || DEFAULT_MODELS[ctx.provider], validatedMessages, tools);
257
430
  ctx.debugLog('chat', 'CONTEXT CHECK', `estimated=${contextCheck.estimated}, limit=${contextCheck.limit}, percent=${contextCheck.percent}%`);
258
431
  if (contextCheck.needsSummarization) {
@@ -264,8 +437,16 @@ export async function runAgentImpl(ctx, content) {
264
437
  ctx.debugLog('chat', 'PRE-SUMMARIZED', `removed=${result.summarizedCount} messages, reduced from ${result.originalTokens} to ${result.reducedTokens}`);
265
438
  }
266
439
  }
267
- const response = await chat(ctx.provider, validatedMessages, tools, effectiveModel, onToken, onRetry);
440
+ let response = await chat(ctx.provider, validatedMessages, tools, effectiveModel, onToken, onRetry);
441
+ streamFlusher.flush(); // drain any batched tail before we swap to history
268
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);
269
450
  // Update token stats and cost
270
451
  if (response.usage) {
271
452
  const usageCost = calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens);
@@ -290,6 +471,33 @@ export async function runAgentImpl(ctx, content) {
290
471
  }
291
472
  }
292
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);
485
+ }
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;
499
+ }
500
+ }
293
501
  // Circuit breaker check after each iteration
294
502
  if (ctx.circuitBreaker) {
295
503
  const iterData = {
@@ -318,10 +526,16 @@ export async function runAgentImpl(ctx, content) {
318
526
  content: response.content,
319
527
  toolCalls: response.toolCalls,
320
528
  });
529
+ // Mirror any text the assistant produced alongside the tool calls
530
+ if (fleetActive() && response.content) {
531
+ fleetMirrorAssistant(response.content);
532
+ }
321
533
  const preChecks = [];
322
534
  const executableTools = [];
323
535
  for (const toolCall of response.toolCalls) {
324
536
  const args = toolCall.arguments;
537
+ runlog.toolCall({ id: toolCall.id, name: toolCall.name, args });
538
+ runToolCalls++;
325
539
  const toolPreview = String(args.command || args.path || '...');
326
540
  const risk = assessToolRisk(toolCall);
327
541
  const riskConfig = RISK_CONFIG[risk.level];
@@ -363,15 +577,38 @@ export async function runAgentImpl(ctx, content) {
363
577
  ctx.addMessage('tool', `🛑 Blocked by hook: ${preHookResult.reason}`);
364
578
  }
365
579
  else {
366
- // Tool can be executed
367
- executableTools.push(toolCall);
368
- ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
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
+ }
369
605
  }
370
606
  }
371
607
  preChecks.push(preCheck);
372
608
  // Record blocked tools in ledger and add to LLM messages
373
609
  if (preCheck.blocked) {
374
610
  ctx.ledger?.recordAction(toolCall.name, args, 'blocked', preCheck.blockReason);
611
+ runlog.toolResult({ id: toolCall.id, result: preCheck.blockContent, isError: true, durationMs: 0 });
375
612
  ctx.llmMessages.current.push({
376
613
  role: 'tool',
377
614
  content: preCheck.blockContent,
@@ -401,8 +638,8 @@ export async function runAgentImpl(ctx, content) {
401
638
  // Execute in parallel using dependency-aware staging
402
639
  ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
403
640
  const results = await executeParallel(executableTools, async (call) => {
404
- const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd());
405
- return result.result;
641
+ const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, undefined, executeOptions);
642
+ return { result: result.result, isError: result.isError };
406
643
  }, (completed, total, current) => {
407
644
  const args = current.arguments;
408
645
  const target = args.path || args.command?.substring(0, 30) || current.name;
@@ -423,10 +660,14 @@ export async function runAgentImpl(ctx, content) {
423
660
  for (const result of results) {
424
661
  const toolCall = result.toolCall;
425
662
  const args = toolCall.arguments;
426
- recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
427
- recordEvent('tool_result', (result.result || result.error || '').slice(0, 1000), { name: toolCall.name, isError: !!result.error });
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;
428
667
  // Record in iteration ledger
429
- ctx.ledger?.recordAction(toolCall.name, args, result.error ? 'error' : 'ok', result.error || undefined);
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 });
430
671
  // Execute post-tool hooks
431
672
  hooks.executeHooks('post-tool', {
432
673
  tool: toolCall.name,
@@ -441,11 +682,11 @@ export async function runAgentImpl(ctx, content) {
441
682
  ctx.addMessage('tool', thought);
442
683
  }
443
684
  else if (result.error) {
444
- ctx.addMessage('tool', `Error: ${result.error}`);
685
+ ctx.addMessage('tool', `Error: ${result.error}`, true);
445
686
  }
446
687
  else {
447
688
  const preview = result.result.split('\n').slice(0, 3).join('\n');
448
- ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''));
689
+ ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''), failed);
449
690
  }
450
691
  ctx.llmMessages.current.push({
451
692
  role: 'tool',
@@ -498,12 +739,11 @@ export async function runAgentImpl(ctx, content) {
498
739
  });
499
740
  }
500
741
  ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
501
- recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
502
742
  // Auto-checkpoint before destructive operations
503
743
  if (shouldCheckpoint(toolCall.name, args)) {
504
744
  const hash = createCheckpoint(toolCall.name, args);
505
745
  if (hash) {
506
- ctx.debugLog('checkpoint', `auto-checkpoint ${hash} before ${toolCall.name}`);
746
+ ctx.debugLog('checkpoint', `checkpoint ${hash} before ${toolCall.name}`);
507
747
  }
508
748
  }
509
749
  // Stream shell output in real-time (#15)
@@ -515,11 +755,13 @@ export async function runAgentImpl(ctx, content) {
515
755
  detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
516
756
  });
517
757
  } : undefined;
518
- const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback);
758
+ const seqStart = Date.now();
759
+ const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback, executeOptions);
519
760
  ctx.debugLog('tools', 'DONE', toolCall.name);
520
- recordEvent('tool_result', result.result.slice(0, 1000), { name: toolCall.name, isError: result.isError });
521
761
  // Record in iteration ledger
522
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 });
523
765
  // Execute post-tool hooks
524
766
  hooks.executeHooks('post-tool', {
525
767
  tool: toolCall.name,
@@ -562,12 +804,12 @@ export async function runAgentImpl(ctx, content) {
562
804
  if (planReasoning)
563
805
  planMsg += `\n ${planReasoning}\n`;
564
806
  planMsg += '\n' + planSteps.map((s, idx) => ` ${idx + 1}. [ ] ${s}`).join('\n');
565
- planMsg += '\n\n Type /approve to execute, or provide feedback to revise.';
807
+ planMsg += '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.';
566
808
  ctx.addMessage('assistant', planMsg);
567
809
  // Tell the LLM to wait for user approval
568
810
  ctx.llmMessages.current.push({
569
811
  role: 'tool',
570
- content: '[Plan displayed to user. Waiting for approval. The user will either type /approve to execute the plan, or provide feedback to revise it. Do NOT proceed with execution until the user approves.]',
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.]',
571
813
  toolCallId: toolCall.id,
572
814
  });
573
815
  // Break out of tool loop - wait for user approval
@@ -576,7 +818,7 @@ export async function runAgentImpl(ctx, content) {
576
818
  else {
577
819
  const display = result.displayResult || result.result;
578
820
  const preview = display.split('\n').slice(0, 5).join('\n');
579
- ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''));
821
+ ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''), result.isError);
580
822
  }
581
823
  if (toolCall.name !== 'ask_question') {
582
824
  ctx.llmMessages.current.push({
@@ -589,6 +831,24 @@ export async function runAgentImpl(ctx, content) {
589
831
  }
590
832
  }
591
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;
851
+ }
592
852
  if (completedNaturally)
593
853
  break; // ask_question pauses for user input (#42)
594
854
  continue;
@@ -597,11 +857,13 @@ export async function runAgentImpl(ctx, content) {
597
857
  ctx.setThinkingState(null);
598
858
  ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
599
859
  ctx.addMessage('assistant', response.content);
600
- recordEvent('output', response.content.slice(0, 5000));
860
+ // Mirror assistant message to the fleet channel
861
+ if (fleetActive()) {
862
+ fleetMirrorAssistant(response.content);
863
+ }
601
864
  ctx.setStreamingResponse('');
602
865
  ctx.setContextTokens(ctx.estimateContextTokens());
603
866
  checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
604
- setMood('success');
605
867
  // Auto-continue if response was truncated due to length
606
868
  if (response.finishReason === 'length') {
607
869
  ctx.addMessage('system', '(auto-continuing...)');
@@ -610,6 +872,20 @@ export async function runAgentImpl(ctx, content) {
610
872
  }
611
873
  completedNaturally = true;
612
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
+ }
613
889
  // End iteration ledger entry
614
890
  ctx.ledger?.endIteration('success');
615
891
  // Auto-save full message history for session persistence
@@ -617,13 +893,13 @@ export async function runAgentImpl(ctx, content) {
617
893
  break;
618
894
  }
619
895
  catch (error) {
896
+ streamFlusher.destroy(); // stop any pending flush timer on error/cancel
620
897
  ctx.setThinkingState(null);
621
898
  ctx.setActivityState(null);
622
899
  ctx.setStreamingResponse('');
623
900
  // End iteration ledger entry with error
624
901
  ctx.ledger?.endIteration('error');
625
902
  // Format error with provider context for better suggestions
626
- setMood('error');
627
903
  const errorMsg = formatError(error, { provider: ctx.actualProvider });
628
904
  ctx.addMessage('error', errorMsg);
629
905
  // Classify error to provide additional recovery suggestions
@@ -652,7 +928,7 @@ export async function runAgentImpl(ctx, content) {
652
928
  // Suggest alternatives based on error type
653
929
  if (classified.category === 'rate_limit' || classified.category === 'server') {
654
930
  if (otherProviders.length > 0) {
655
- ctx.addMessage('system', `\u{1f4a1} Try switching providers: /provider ${otherProviders[0]} or /models to see alternatives`);
931
+ ctx.addMessage('system', `\u{1f4a1} Try switching providers: /provider ${otherProviders[0]} or /model to see alternatives`);
656
932
  }
657
933
  }
658
934
  else if (classified.category === 'timeout' || classified.category === 'network') {
@@ -680,6 +956,7 @@ export async function runAgentImpl(ctx, content) {
680
956
  if (runId && runStatus) {
681
957
  ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
682
958
  }
959
+ finalizeRun(runExitReason ?? runStatus ?? 'error');
683
960
  return; // Exit early on error - don't process queued messages
684
961
  }
685
962
  }
@@ -694,10 +971,15 @@ export async function runAgentImpl(ctx, content) {
694
971
  if (runId) {
695
972
  ctx.ledger?.finishRun(runId, runStatus || 'completed', { errorSummary: runErrorSummary });
696
973
  }
974
+ finalizeRun(runExitReason ?? runStatus ?? 'completed');
697
975
  // Update context tokens after agent run
698
976
  ctx.setContextTokens(ctx.estimateContextTokens());
699
977
  // Process any queued messages (human-in-the-loop feedback)
700
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.');
982
+ }
701
983
  const currentQueued = ctx.queuedMessagesRef.current;
702
984
  ctx.debugLog('runAgent', 'EXIT loop', `queued=${currentQueued.length}`);
703
985
  if (currentQueued.length > 0) {
@@ -735,7 +1017,6 @@ export async function runAgentImpl(ctx, content) {
735
1017
  */
736
1018
  export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
737
1019
  ctx.setIsProcessing(true);
738
- setMood('focused');
739
1020
  // Prevent system sleep during long agent loops (macOS)
740
1021
  startPreventSleep();
741
1022
  let completedIterations = 0;