@calliopelabs/cli 2.5.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/README.md +45 -661
  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/bin.d.ts +3 -7
  7. package/dist/bin.d.ts.map +1 -1
  8. package/dist/bin.js +56 -79
  9. package/dist/bin.js.map +1 -1
  10. package/dist/budget.d.ts +64 -0
  11. package/dist/budget.d.ts.map +1 -0
  12. package/dist/budget.js +157 -0
  13. package/dist/budget.js.map +1 -0
  14. package/dist/checkpoint.d.ts +63 -30
  15. package/dist/checkpoint.d.ts.map +1 -1
  16. package/dist/checkpoint.js +247 -160
  17. package/dist/checkpoint.js.map +1 -1
  18. package/dist/circuit-breaker/breaker.d.ts.map +1 -1
  19. package/dist/circuit-breaker/breaker.js.map +1 -1
  20. package/dist/config.d.ts +86 -81
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +236 -304
  23. package/dist/config.js.map +1 -1
  24. package/dist/cost.d.ts +114 -0
  25. package/dist/cost.d.ts.map +1 -0
  26. package/dist/cost.js +499 -0
  27. package/dist/cost.js.map +1 -0
  28. package/dist/diff.d.ts.map +1 -1
  29. package/dist/diff.js +11 -7
  30. package/dist/diff.js.map +1 -1
  31. package/dist/env-expansion.js.map +1 -1
  32. package/dist/errors.js +3 -3
  33. package/dist/errors.js.map +1 -1
  34. package/dist/files.js.map +1 -1
  35. package/dist/fleet.d.ts +30 -0
  36. package/dist/fleet.d.ts.map +1 -0
  37. package/dist/fleet.js +86 -0
  38. package/dist/fleet.js.map +1 -0
  39. package/dist/fuzzy-search.d.ts.map +1 -1
  40. package/dist/fuzzy-search.js +2 -1
  41. package/dist/fuzzy-search.js.map +1 -1
  42. package/dist/git-status.js.map +1 -1
  43. package/dist/headless.d.ts +1 -2
  44. package/dist/headless.d.ts.map +1 -1
  45. package/dist/headless.js +166 -24
  46. package/dist/headless.js.map +1 -1
  47. package/dist/hud/api.d.ts +3 -18
  48. package/dist/hud/api.d.ts.map +1 -1
  49. package/dist/hud/api.js +9 -352
  50. package/dist/hud/api.js.map +1 -1
  51. package/dist/hud/palettes.d.ts.map +1 -1
  52. package/dist/hud/palettes.js +0 -180
  53. package/dist/hud/palettes.js.map +1 -1
  54. package/dist/hud/skins.d.ts.map +1 -1
  55. package/dist/hud/skins.js +0 -263
  56. package/dist/hud/skins.js.map +1 -1
  57. package/dist/hud/types.d.ts +2 -5
  58. package/dist/hud/types.d.ts.map +1 -1
  59. package/dist/index.d.ts +1 -4
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -4
  62. package/dist/index.js.map +1 -1
  63. package/dist/iteration-ledger.d.ts.map +1 -1
  64. package/dist/iteration-ledger.js +3 -2
  65. package/dist/iteration-ledger.js.map +1 -1
  66. package/dist/local-model.d.ts +150 -0
  67. package/dist/local-model.d.ts.map +1 -0
  68. package/dist/local-model.js +408 -0
  69. package/dist/local-model.js.map +1 -0
  70. package/dist/mcp.d.ts.map +1 -1
  71. package/dist/mcp.js +5 -2
  72. package/dist/mcp.js.map +1 -1
  73. package/dist/memory.d.ts.map +1 -1
  74. package/dist/memory.js +6 -4
  75. package/dist/memory.js.map +1 -1
  76. package/dist/model-detection.js +2 -2
  77. package/dist/model-detection.js.map +1 -1
  78. package/dist/parallel-tools.js.map +1 -1
  79. package/dist/plugins.d.ts +32 -1
  80. package/dist/plugins.d.ts.map +1 -1
  81. package/dist/plugins.js +116 -5
  82. package/dist/plugins.js.map +1 -1
  83. package/dist/policy.d.ts +43 -0
  84. package/dist/policy.d.ts.map +1 -0
  85. package/dist/policy.js +139 -0
  86. package/dist/policy.js.map +1 -0
  87. package/dist/project-config.d.ts +1 -2
  88. package/dist/project-config.d.ts.map +1 -1
  89. package/dist/project-config.js +0 -3
  90. package/dist/project-config.js.map +1 -1
  91. package/dist/providers/bedrock.d.ts.map +1 -1
  92. package/dist/providers/bedrock.js +2 -5
  93. package/dist/providers/bedrock.js.map +1 -1
  94. package/dist/providers/compat.d.ts +21 -0
  95. package/dist/providers/compat.d.ts.map +1 -1
  96. package/dist/providers/compat.js +176 -6
  97. package/dist/providers/compat.js.map +1 -1
  98. package/dist/providers/google.js.map +1 -1
  99. package/dist/providers/index.d.ts +19 -4
  100. package/dist/providers/index.d.ts.map +1 -1
  101. package/dist/providers/index.js +61 -10
  102. package/dist/providers/index.js.map +1 -1
  103. package/dist/providers/ollama.d.ts +2 -2
  104. package/dist/providers/ollama.d.ts.map +1 -1
  105. package/dist/providers/ollama.js +26 -6
  106. package/dist/providers/ollama.js.map +1 -1
  107. package/dist/providers/openai.d.ts +10 -10
  108. package/dist/providers/openai.d.ts.map +1 -1
  109. package/dist/providers/openai.js +18 -10
  110. package/dist/providers/openai.js.map +1 -1
  111. package/dist/providers/types.d.ts +8 -0
  112. package/dist/providers/types.d.ts.map +1 -1
  113. package/dist/providers/types.js.map +1 -1
  114. package/dist/replay.d.ts +37 -0
  115. package/dist/replay.d.ts.map +1 -0
  116. package/dist/replay.js +168 -0
  117. package/dist/replay.js.map +1 -0
  118. package/dist/router.d.ts +133 -0
  119. package/dist/router.d.ts.map +1 -0
  120. package/dist/{model-router.js → router.js} +329 -3
  121. package/dist/router.js.map +1 -0
  122. package/dist/runlog.d.ts +185 -0
  123. package/dist/runlog.d.ts.map +1 -0
  124. package/dist/runlog.js +398 -0
  125. package/dist/runlog.js.map +1 -0
  126. package/dist/{sandbox.d.ts → sandbox/docker.d.ts} +1 -1
  127. package/dist/sandbox/docker.d.ts.map +1 -0
  128. package/dist/{sandbox.js → sandbox/docker.js} +1 -1
  129. package/dist/sandbox/docker.js.map +1 -0
  130. package/dist/sandbox/index.d.ts +36 -0
  131. package/dist/sandbox/index.d.ts.map +1 -0
  132. package/dist/sandbox/index.js +62 -0
  133. package/dist/sandbox/index.js.map +1 -0
  134. package/dist/{sandbox-native.d.ts → sandbox/native.d.ts} +1 -1
  135. package/dist/sandbox/native.d.ts.map +1 -0
  136. package/dist/{sandbox-native.js → sandbox/native.js} +1 -1
  137. package/dist/sandbox/native.js.map +1 -0
  138. package/dist/scope.d.ts.map +1 -1
  139. package/dist/scope.js +1 -1
  140. package/dist/scope.js.map +1 -1
  141. package/dist/scuttlebot/client.d.ts.map +1 -1
  142. package/dist/scuttlebot/client.js.map +1 -1
  143. package/dist/scuttlebot/config.js.map +1 -1
  144. package/dist/scuttlebot/http-client.d.ts.map +1 -1
  145. package/dist/scuttlebot/irc-client.d.ts +5 -5
  146. package/dist/scuttlebot/irc-client.d.ts.map +1 -1
  147. package/dist/scuttlebot/irc-client.js.map +1 -1
  148. package/dist/setup.d.ts.map +1 -1
  149. package/dist/setup.js +25 -54
  150. package/dist/setup.js.map +1 -1
  151. package/dist/skills.d.ts +39 -0
  152. package/dist/skills.d.ts.map +1 -1
  153. package/dist/skills.js +90 -8
  154. package/dist/skills.js.map +1 -1
  155. package/dist/storage.d.ts.map +1 -1
  156. package/dist/storage.js +6 -3
  157. package/dist/storage.js.map +1 -1
  158. package/dist/streaming.d.ts +25 -0
  159. package/dist/streaming.d.ts.map +1 -1
  160. package/dist/streaming.js +59 -0
  161. package/dist/streaming.js.map +1 -1
  162. package/dist/styles.d.ts +62 -56
  163. package/dist/styles.d.ts.map +1 -1
  164. package/dist/styles.js +11 -0
  165. package/dist/styles.js.map +1 -1
  166. package/dist/summarization.d.ts.map +1 -1
  167. package/dist/summarization.js.map +1 -1
  168. package/dist/terminal-image.js +1 -1
  169. package/dist/terminal-image.js.map +1 -1
  170. package/dist/themes.d.ts +5 -4
  171. package/dist/themes.d.ts.map +1 -1
  172. package/dist/themes.js +35 -90
  173. package/dist/themes.js.map +1 -1
  174. package/dist/tools.d.ts +27 -3
  175. package/dist/tools.d.ts.map +1 -1
  176. package/dist/tools.js +128 -179
  177. package/dist/tools.js.map +1 -1
  178. package/dist/trust.js.map +1 -1
  179. package/dist/types.d.ts +4 -3
  180. package/dist/types.d.ts.map +1 -1
  181. package/dist/types.js +21 -40
  182. package/dist/types.js.map +1 -1
  183. package/dist/ui/agent.d.ts +3 -6
  184. package/dist/ui/agent.d.ts.map +1 -1
  185. package/dist/ui/agent.js +328 -38
  186. package/dist/ui/agent.js.map +1 -1
  187. package/dist/ui/animations.d.ts +37 -0
  188. package/dist/ui/animations.d.ts.map +1 -0
  189. package/dist/ui/animations.js +227 -0
  190. package/dist/ui/animations.js.map +1 -0
  191. package/dist/ui/chat-input-keys.d.ts +42 -0
  192. package/dist/ui/chat-input-keys.d.ts.map +1 -0
  193. package/dist/ui/chat-input-keys.js +360 -0
  194. package/dist/ui/chat-input-keys.js.map +1 -0
  195. package/dist/ui/chat-input.d.ts +4 -2
  196. package/dist/ui/chat-input.d.ts.map +1 -1
  197. package/dist/ui/chat-input.js +31 -350
  198. package/dist/ui/chat-input.js.map +1 -1
  199. package/dist/ui/commands.d.ts +14 -46
  200. package/dist/ui/commands.d.ts.map +1 -1
  201. package/dist/ui/commands.js +416 -2899
  202. package/dist/ui/commands.js.map +1 -1
  203. package/dist/ui/completions.d.ts +1 -0
  204. package/dist/ui/completions.d.ts.map +1 -1
  205. package/dist/ui/completions.js +36 -107
  206. package/dist/ui/completions.js.map +1 -1
  207. package/dist/ui/components.d.ts +20 -12
  208. package/dist/ui/components.d.ts.map +1 -1
  209. package/dist/ui/components.js +17 -226
  210. package/dist/ui/components.js.map +1 -1
  211. package/dist/ui/context.js +6 -6
  212. package/dist/ui/context.js.map +1 -1
  213. package/dist/ui/debug-log.d.ts +11 -0
  214. package/dist/ui/debug-log.d.ts.map +1 -0
  215. package/dist/ui/debug-log.js +21 -0
  216. package/dist/ui/debug-log.js.map +1 -0
  217. package/dist/ui/error-boundary.d.ts +1 -1
  218. package/dist/ui/error-boundary.d.ts.map +1 -1
  219. package/dist/ui/error-boundary.js.map +1 -1
  220. package/dist/ui/frame.d.ts +1 -1
  221. package/dist/ui/frame.d.ts.map +1 -1
  222. package/dist/ui/frame.js +1 -3
  223. package/dist/ui/frame.js.map +1 -1
  224. package/dist/ui/index.d.ts +11 -3
  225. package/dist/ui/index.d.ts.map +1 -1
  226. package/dist/ui/index.js +45 -1076
  227. package/dist/ui/index.js.map +1 -1
  228. package/dist/ui/messages.d.ts +5 -8
  229. package/dist/ui/messages.d.ts.map +1 -1
  230. package/dist/ui/messages.js +15 -30
  231. package/dist/ui/messages.js.map +1 -1
  232. package/dist/ui/modals/index.d.ts +11 -0
  233. package/dist/ui/modals/index.d.ts.map +1 -0
  234. package/dist/ui/modals/index.js +10 -0
  235. package/dist/ui/modals/index.js.map +1 -0
  236. package/dist/ui/modals/prompts.d.ts +38 -0
  237. package/dist/ui/modals/prompts.d.ts.map +1 -0
  238. package/dist/ui/modals/prompts.js +124 -0
  239. package/dist/ui/modals/prompts.js.map +1 -0
  240. package/dist/ui/modals/selectors.d.ts +34 -0
  241. package/dist/ui/modals/selectors.d.ts.map +1 -0
  242. package/dist/ui/modals/selectors.js +111 -0
  243. package/dist/ui/modals/selectors.js.map +1 -0
  244. package/dist/ui/modals/setup.d.ts +18 -0
  245. package/dist/ui/modals/setup.d.ts.map +1 -0
  246. package/dist/ui/modals/setup.js +51 -0
  247. package/dist/ui/modals/setup.js.map +1 -0
  248. package/dist/ui/regions/input-region.d.ts +40 -0
  249. package/dist/ui/regions/input-region.d.ts.map +1 -0
  250. package/dist/ui/regions/input-region.js +116 -0
  251. package/dist/ui/regions/input-region.js.map +1 -0
  252. package/dist/ui/regions/modal-host.d.ts +41 -0
  253. package/dist/ui/regions/modal-host.d.ts.map +1 -0
  254. package/dist/ui/regions/modal-host.js +51 -0
  255. package/dist/ui/regions/modal-host.js.map +1 -0
  256. package/dist/ui/regions/render-probe.d.ts +22 -0
  257. package/dist/ui/regions/render-probe.d.ts.map +1 -0
  258. package/dist/ui/regions/render-probe.js +45 -0
  259. package/dist/ui/regions/render-probe.js.map +1 -0
  260. package/dist/ui/regions/static-scrollback.d.ts +39 -0
  261. package/dist/ui/regions/static-scrollback.d.ts.map +1 -0
  262. package/dist/ui/regions/static-scrollback.js +68 -0
  263. package/dist/ui/regions/static-scrollback.js.map +1 -0
  264. package/dist/ui/regions/status-region.d.ts +24 -0
  265. package/dist/ui/regions/status-region.d.ts.map +1 -0
  266. package/dist/ui/regions/status-region.js +17 -0
  267. package/dist/ui/regions/status-region.js.map +1 -0
  268. package/dist/ui/regions/transcript-region.d.ts +41 -0
  269. package/dist/ui/regions/transcript-region.d.ts.map +1 -0
  270. package/dist/ui/regions/transcript-region.js +52 -0
  271. package/dist/ui/regions/transcript-region.js.map +1 -0
  272. package/dist/ui/self-restart.d.ts +9 -0
  273. package/dist/ui/self-restart.d.ts.map +1 -0
  274. package/dist/ui/self-restart.js +24 -0
  275. package/dist/ui/self-restart.js.map +1 -0
  276. package/dist/ui/state/use-chat-controller.d.ts +23 -0
  277. package/dist/ui/state/use-chat-controller.d.ts.map +1 -0
  278. package/dist/ui/state/use-chat-controller.js +643 -0
  279. package/dist/ui/state/use-chat-controller.js.map +1 -0
  280. package/dist/ui/state/use-loop-state.d.ts +23 -0
  281. package/dist/ui/state/use-loop-state.d.ts.map +1 -0
  282. package/dist/ui/state/use-loop-state.js +39 -0
  283. package/dist/ui/state/use-loop-state.js.map +1 -0
  284. package/dist/ui/state/use-modal-state.d.ts +52 -0
  285. package/dist/ui/state/use-modal-state.d.ts.map +1 -0
  286. package/dist/ui/state/use-modal-state.js +44 -0
  287. package/dist/ui/state/use-modal-state.js.map +1 -0
  288. package/dist/ui/state/use-model-state.d.ts +29 -0
  289. package/dist/ui/state/use-model-state.d.ts.map +1 -0
  290. package/dist/ui/state/use-model-state.js +47 -0
  291. package/dist/ui/state/use-model-state.js.map +1 -0
  292. package/dist/ui/state/use-processing-state.d.ts +24 -0
  293. package/dist/ui/state/use-processing-state.d.ts.map +1 -0
  294. package/dist/ui/state/use-processing-state.js +46 -0
  295. package/dist/ui/state/use-processing-state.js.map +1 -0
  296. package/dist/ui/state/use-queue-state.d.ts +19 -0
  297. package/dist/ui/state/use-queue-state.d.ts.map +1 -0
  298. package/dist/ui/state/use-queue-state.js +30 -0
  299. package/dist/ui/state/use-queue-state.js.map +1 -0
  300. package/dist/ui/state/use-session-init.d.ts +22 -0
  301. package/dist/ui/state/use-session-init.d.ts.map +1 -0
  302. package/dist/ui/state/use-session-init.js +77 -0
  303. package/dist/ui/state/use-session-init.js.map +1 -0
  304. package/dist/ui/state/use-session-stats.d.ts +16 -0
  305. package/dist/ui/state/use-session-stats.d.ts.map +1 -0
  306. package/dist/ui/state/use-session-stats.js +18 -0
  307. package/dist/ui/state/use-session-stats.js.map +1 -0
  308. package/dist/ui/state/use-terminal-width.d.ts +8 -0
  309. package/dist/ui/state/use-terminal-width.d.ts.map +1 -0
  310. package/dist/ui/state/use-terminal-width.js +22 -0
  311. package/dist/ui/state/use-terminal-width.js.map +1 -0
  312. package/dist/ui/state/use-transcript-state.d.ts +21 -0
  313. package/dist/ui/state/use-transcript-state.d.ts.map +1 -0
  314. package/dist/ui/state/use-transcript-state.js +49 -0
  315. package/dist/ui/state/use-transcript-state.js.map +1 -0
  316. package/dist/ui/status-bar.d.ts +1 -1
  317. package/dist/ui/status-bar.d.ts.map +1 -1
  318. package/dist/ui/status-bar.js +3 -4
  319. package/dist/ui/status-bar.js.map +1 -1
  320. package/dist/version-check.d.ts.map +1 -1
  321. package/dist/version-check.js +10 -0
  322. package/dist/version-check.js.map +1 -1
  323. package/package.json +17 -10
  324. package/dist/agents/agent-config-loader.d.ts +0 -60
  325. package/dist/agents/agent-config-loader.d.ts.map +0 -1
  326. package/dist/agents/agent-config-loader.js +0 -402
  327. package/dist/agents/agent-config-loader.js.map +0 -1
  328. package/dist/agents/agent-config-presets.d.ts +0 -10
  329. package/dist/agents/agent-config-presets.d.ts.map +0 -1
  330. package/dist/agents/agent-config-presets.js +0 -940
  331. package/dist/agents/agent-config-presets.js.map +0 -1
  332. package/dist/agents/agent-config-types.d.ts +0 -145
  333. package/dist/agents/agent-config-types.d.ts.map +0 -1
  334. package/dist/agents/agent-config-types.js +0 -12
  335. package/dist/agents/agent-config-types.js.map +0 -1
  336. package/dist/agents/agent-detection.d.ts +0 -35
  337. package/dist/agents/agent-detection.d.ts.map +0 -1
  338. package/dist/agents/agent-detection.js +0 -110
  339. package/dist/agents/agent-detection.js.map +0 -1
  340. package/dist/agents/aggregator.d.ts +0 -19
  341. package/dist/agents/aggregator.d.ts.map +0 -1
  342. package/dist/agents/aggregator.js +0 -141
  343. package/dist/agents/aggregator.js.map +0 -1
  344. package/dist/agents/cli-backend.d.ts +0 -32
  345. package/dist/agents/cli-backend.d.ts.map +0 -1
  346. package/dist/agents/cli-backend.js +0 -271
  347. package/dist/agents/cli-backend.js.map +0 -1
  348. package/dist/agents/council-types.d.ts +0 -115
  349. package/dist/agents/council-types.d.ts.map +0 -1
  350. package/dist/agents/council-types.js +0 -81
  351. package/dist/agents/council-types.js.map +0 -1
  352. package/dist/agents/council.d.ts +0 -112
  353. package/dist/agents/council.d.ts.map +0 -1
  354. package/dist/agents/council.js +0 -722
  355. package/dist/agents/council.js.map +0 -1
  356. package/dist/agents/decomposer.d.ts +0 -33
  357. package/dist/agents/decomposer.d.ts.map +0 -1
  358. package/dist/agents/decomposer.js +0 -138
  359. package/dist/agents/decomposer.js.map +0 -1
  360. package/dist/agents/dynamic-tools.d.ts +0 -52
  361. package/dist/agents/dynamic-tools.d.ts.map +0 -1
  362. package/dist/agents/dynamic-tools.js +0 -424
  363. package/dist/agents/dynamic-tools.js.map +0 -1
  364. package/dist/agents/index.d.ts +0 -29
  365. package/dist/agents/index.d.ts.map +0 -1
  366. package/dist/agents/index.js +0 -29
  367. package/dist/agents/index.js.map +0 -1
  368. package/dist/agents/installer.d.ts +0 -39
  369. package/dist/agents/installer.d.ts.map +0 -1
  370. package/dist/agents/installer.js +0 -205
  371. package/dist/agents/installer.js.map +0 -1
  372. package/dist/agents/orchestrator.d.ts +0 -106
  373. package/dist/agents/orchestrator.d.ts.map +0 -1
  374. package/dist/agents/orchestrator.js +0 -363
  375. package/dist/agents/orchestrator.js.map +0 -1
  376. package/dist/agents/sdk-backend.d.ts +0 -63
  377. package/dist/agents/sdk-backend.d.ts.map +0 -1
  378. package/dist/agents/sdk-backend.js +0 -489
  379. package/dist/agents/sdk-backend.js.map +0 -1
  380. package/dist/agents/swarm-types.d.ts +0 -84
  381. package/dist/agents/swarm-types.d.ts.map +0 -1
  382. package/dist/agents/swarm-types.js +0 -20
  383. package/dist/agents/swarm-types.js.map +0 -1
  384. package/dist/agents/swarm.d.ts +0 -79
  385. package/dist/agents/swarm.d.ts.map +0 -1
  386. package/dist/agents/swarm.js +0 -375
  387. package/dist/agents/swarm.js.map +0 -1
  388. package/dist/agents/tools.d.ts +0 -26
  389. package/dist/agents/tools.d.ts.map +0 -1
  390. package/dist/agents/tools.js +0 -776
  391. package/dist/agents/tools.js.map +0 -1
  392. package/dist/agents/types.d.ts +0 -108
  393. package/dist/agents/types.d.ts.map +0 -1
  394. package/dist/agents/types.js +0 -43
  395. package/dist/agents/types.js.map +0 -1
  396. package/dist/api-server.d.ts +0 -45
  397. package/dist/api-server.d.ts.map +0 -1
  398. package/dist/api-server.js +0 -326
  399. package/dist/api-server.js.map +0 -1
  400. package/dist/auto-checkpoint.d.ts +0 -35
  401. package/dist/auto-checkpoint.d.ts.map +0 -1
  402. package/dist/auto-checkpoint.js +0 -176
  403. package/dist/auto-checkpoint.js.map +0 -1
  404. package/dist/background-jobs.d.ts +0 -45
  405. package/dist/background-jobs.d.ts.map +0 -1
  406. package/dist/background-jobs.js +0 -127
  407. package/dist/background-jobs.js.map +0 -1
  408. package/dist/branching.d.ts +0 -56
  409. package/dist/branching.d.ts.map +0 -1
  410. package/dist/branching.js +0 -224
  411. package/dist/branching.js.map +0 -1
  412. package/dist/cli/agent.d.ts +0 -9
  413. package/dist/cli/agent.d.ts.map +0 -1
  414. package/dist/cli/agent.js +0 -330
  415. package/dist/cli/agent.js.map +0 -1
  416. package/dist/cli/commands.d.ts +0 -12
  417. package/dist/cli/commands.d.ts.map +0 -1
  418. package/dist/cli/commands.js +0 -1042
  419. package/dist/cli/commands.js.map +0 -1
  420. package/dist/cli/index.d.ts +0 -8
  421. package/dist/cli/index.d.ts.map +0 -1
  422. package/dist/cli/index.js +0 -268
  423. package/dist/cli/index.js.map +0 -1
  424. package/dist/cli/types.d.ts +0 -33
  425. package/dist/cli/types.d.ts.map +0 -1
  426. package/dist/cli/types.js +0 -21
  427. package/dist/cli/types.js.map +0 -1
  428. package/dist/companions.d.ts +0 -54
  429. package/dist/companions.d.ts.map +0 -1
  430. package/dist/companions.js +0 -440
  431. package/dist/companions.js.map +0 -1
  432. package/dist/file-watcher.d.ts +0 -91
  433. package/dist/file-watcher.d.ts.map +0 -1
  434. package/dist/file-watcher.js +0 -269
  435. package/dist/file-watcher.js.map +0 -1
  436. package/dist/hud/theme-packs/api.d.ts +0 -51
  437. package/dist/hud/theme-packs/api.d.ts.map +0 -1
  438. package/dist/hud/theme-packs/api.js +0 -145
  439. package/dist/hud/theme-packs/api.js.map +0 -1
  440. package/dist/hud/theme-packs/index.d.ts +0 -18
  441. package/dist/hud/theme-packs/index.d.ts.map +0 -1
  442. package/dist/hud/theme-packs/index.js +0 -38
  443. package/dist/hud/theme-packs/index.js.map +0 -1
  444. package/dist/hud/theme-packs/types.d.ts +0 -29
  445. package/dist/hud/theme-packs/types.d.ts.map +0 -1
  446. package/dist/hud/theme-packs/types.js +0 -9
  447. package/dist/hud/theme-packs/types.js.map +0 -1
  448. package/dist/idle-eviction.d.ts +0 -34
  449. package/dist/idle-eviction.d.ts.map +0 -1
  450. package/dist/idle-eviction.js +0 -85
  451. package/dist/idle-eviction.js.map +0 -1
  452. package/dist/model-router.d.ts +0 -67
  453. package/dist/model-router.d.ts.map +0 -1
  454. package/dist/model-router.js.map +0 -1
  455. package/dist/providers/openai-compat-shims.d.ts +0 -31
  456. package/dist/providers/openai-compat-shims.d.ts.map +0 -1
  457. package/dist/providers/openai-compat-shims.js +0 -179
  458. package/dist/providers/openai-compat-shims.js.map +0 -1
  459. package/dist/sandbox-native.d.ts.map +0 -1
  460. package/dist/sandbox-native.js.map +0 -1
  461. package/dist/sandbox.d.ts.map +0 -1
  462. package/dist/sandbox.js.map +0 -1
  463. package/dist/session-timeout.d.ts +0 -31
  464. package/dist/session-timeout.d.ts.map +0 -1
  465. package/dist/session-timeout.js +0 -100
  466. package/dist/session-timeout.js.map +0 -1
  467. package/dist/smart-router.d.ts +0 -73
  468. package/dist/smart-router.d.ts.map +0 -1
  469. package/dist/smart-router.js +0 -332
  470. package/dist/smart-router.js.map +0 -1
  471. package/dist/terminal-recording.d.ts +0 -55
  472. package/dist/terminal-recording.d.ts.map +0 -1
  473. package/dist/terminal-recording.js +0 -182
  474. package/dist/terminal-recording.js.map +0 -1
  475. package/dist/tmux.d.ts +0 -35
  476. package/dist/tmux.d.ts.map +0 -1
  477. package/dist/tmux.js +0 -106
  478. package/dist/tmux.js.map +0 -1
  479. package/dist/ui/modals.d.ts +0 -72
  480. package/dist/ui/modals.d.ts.map +0 -1
  481. package/dist/ui/modals.js +0 -266
  482. package/dist/ui/modals.js.map +0 -1
  483. package/dist/ui/pack-picker.d.ts +0 -12
  484. package/dist/ui/pack-picker.d.ts.map +0 -1
  485. package/dist/ui/pack-picker.js +0 -101
  486. package/dist/ui/pack-picker.js.map +0 -1
  487. package/dist/ui/theme-picker.d.ts +0 -24
  488. package/dist/ui/theme-picker.d.ts.map +0 -1
  489. package/dist/ui/theme-picker.js +0 -190
  490. package/dist/ui/theme-picker.js.map +0 -1
package/dist/ui/index.js CHANGED
@@ -1,1080 +1,65 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * UI Module - Entry Point
4
4
  *
5
- * TerminalChat (main component), App wrapper, printBanner, startInkCLI.
6
- * Imports extracted modules and wires state through context bags.
5
+ * TerminalChat (composition), App wrapper, printBanner, startInkCLI. All state
6
+ * and orchestration live in useChatController; the JSX here just wires the four
7
+ * memoized regions together inside the HUD frame.
7
8
  */
8
- import React, { useState, useCallback, useRef, useEffect } from 'react';
9
- import { render, Box, Text, useApp, useStdout } from 'ink';
10
- import * as fs from 'fs';
9
+ import { useEffect } from 'react';
10
+ import { render, Box } from 'ink';
11
11
  import * as config from '../config.js';
12
- import { selectProvider } from '../providers/index.js';
13
- import { getSystemPrompt, DEFAULT_MODELS, supportsVision } from '../types.js';
12
+ import { selectProvider, ProviderUnavailableError } from '../providers/index.js';
13
+ import { DEFAULT_MODELS } from '../types.js';
14
14
  import { getVersion } from '../version-check.js';
15
- import { getModelContextLimit, preWarmModelCache } from '../model-detection.js';
16
- import { detectComplexity } from '../risk.js';
17
- import * as storage from '../storage.js';
18
- import { parseFileReferences, processFilesForMessage, formatFileInfo } from '../files.js';
19
- import * as memory from '../memory.js';
20
- import * as hooks from '../hooks.js';
21
- import { getCurrentSkin, getCurrentPalette, paletteColorize, applySkin, applyPalette } from '../hud/api.js';
15
+ import { getCurrentSkin, paletteColorize } from '../hud/api.js';
22
16
  import { renderColoredBanner, renderSplashAnimation, renderTransition, colorFg } from '../terminal-image.js';
17
+ import { fleetPostOffline } from '../fleet.js';
23
18
  import { HUDFrame } from './frame.js';
24
- import { getCurrentCompanion, applyCompanion, setEmojiConfig } from '../companions.js';
25
- import { CircuitBreaker } from '../circuit-breaker.js';
26
- import { IterationLedger } from '../iteration-ledger.js';
27
- import { getDefaultSmartRoutingConfig } from '../smart-router.js';
28
- import * as recording from '../terminal-recording.js';
29
- import * as sessionTimeout from '../session-timeout.js';
30
- import * as idleEviction from '../idle-eviction.js';
31
- import { isTmux, getTmuxInfo } from '../tmux.js';
32
- import { scuttlebotClient } from '../scuttlebot/index.js';
33
19
  import { ErrorBoundary } from './error-boundary.js';
34
- import { ThinkingDisplay, ProcessingIndicator, StreamingIndicator, StateTransition } from './components.js';
35
- import { MessageHistory } from './messages.js';
36
- import { ModelSelector, SessionSelector, UpgradePrompt, ComplexityWarning, SessionResumePrompt, KeybindingsModal, ProviderSelector, ApiKeySetup, } from './modals.js';
37
- import { ThemePicker } from './theme-picker.js';
38
- import { PackPicker } from './pack-picker.js';
39
- import { applyThemePack, getCurrentPack, getCompanionMode, getThemePack } from '../hud/theme-packs/api.js';
40
- import { ChatInput } from './chat-input.js';
41
- import { StatusBar } from './status-bar.js';
42
- import { handleCommand } from './commands.js';
43
- import { runAgentImpl, runLoopImpl, validateAndRepairMessagesImpl } from './agent.js';
44
- import { resolveIterationLimit } from '../iteration-limit.js';
45
- // Wire emoji config at module load (breaks circular dep: companions → config)
46
- setEmojiConfig(config);
47
- // Module-level state for agterm mode
48
- let moduleAgtermEnabled = false;
49
- let pendingRestartArgs = null;
50
- function requestSelfRestart(args = process.argv.slice(1)) {
51
- pendingRestartArgs = [...args];
52
- }
53
- async function spawnPendingRestart() {
54
- if (!pendingRestartArgs) {
55
- return;
56
- }
57
- const restartArgs = pendingRestartArgs;
58
- pendingRestartArgs = null;
59
- const { spawn } = await import('child_process');
60
- const child = spawn(process.argv[0], restartArgs, {
61
- stdio: 'inherit',
62
- detached: true,
63
- });
64
- child.unref();
65
- }
66
- // Debug logging for flow control issues
67
- let debugEnabled = process.env.CALLIOPE_DEBUG === '1';
68
- const debugLog = (label, ...args) => {
69
- if (debugEnabled) {
70
- const timestamp = new Date().toISOString().split('T')[1].slice(0, 12);
71
- console.error(`[${timestamp}] ${label}:`, ...args);
72
- }
73
- };
74
- // ============================================================================
75
- // Main Chat Component
76
- // ============================================================================
77
- function TerminalChat() {
78
- const { exit } = useApp();
79
- const { stdout } = useStdout();
80
- // Reactive terminal width - re-renders on resize via SIGWINCH
81
- const [width, setWidth] = useState(() => stdout?.columns || 80);
82
- useEffect(() => {
83
- const onResize = () => {
84
- const cols = stdout?.columns || process.stdout.columns || 80;
85
- setWidth(cols);
86
- };
87
- process.stdout.on('resize', onResize);
88
- return () => { process.stdout.off('resize', onResize); };
89
- }, [stdout]);
90
- // Core state
91
- // Input value is held primarily in a ref so that keystrokes don't force
92
- // a full parent re-render (which cascades through modals, layouts, status bar
93
- // and causes visible paint lag on every character). We only bump `inputVersion`
94
- // when we need the parent to re-render with a programmatic value change
95
- // (history nav, clear on submit) — keystrokes never trigger a parent render.
96
- const inputRef = useRef('');
97
- const [inputVersion, setInputVersion] = useState(0);
98
- const setInputValue = useCallback((value) => {
99
- inputRef.current = value;
100
- setInputVersion(v => v + 1);
101
- }, []);
102
- const input = inputRef.current;
103
- const [suggestions, setSuggestions] = useState([]);
104
- const [messages, setMessages] = useState([]);
105
- const [isProcessing, setIsProcessing] = useState(false);
106
- const [thinkingState, setThinkingState] = useState(null);
107
- const [streamingResponse, setStreamingResponse] = useState('');
108
- const [activityState, setActivityState] = useState(null);
109
- // State transition tracking
110
- const prevProcessingState = useRef('idle');
111
- const [transition, setTransition] = useState(null);
112
- useEffect(() => {
113
- const current = isProcessing && thinkingState && !streamingResponse ? 'thinking' :
114
- isProcessing && streamingResponse ? 'streaming' :
115
- !isProcessing && prevProcessingState.current !== 'idle' ? 'done' : 'idle';
116
- if (current !== prevProcessingState.current) {
117
- const from = prevProcessingState.current;
118
- // Only show transitions for meaningful state changes
119
- if (from !== 'idle' || current !== 'idle') {
120
- setTransition({ from, to: current });
121
- }
122
- prevProcessingState.current = current;
123
- }
124
- }, [isProcessing, thinkingState, streamingResponse]);
125
- // Input history for up/down arrow navigation
126
- const [inputHistory, setInputHistory] = useState([]);
127
- const [historyIndex, setHistoryIndex] = useState(-1);
128
- const [savedInput, setSavedInput] = useState(''); // Save current input when navigating
129
- // Smart suggestions context
130
- const [hasGitRepo] = useState(() => {
131
- try {
132
- return fs.existsSync('.git') || fs.existsSync('../.git');
133
- }
134
- catch {
135
- return false;
136
- }
137
- });
138
- const recentCommands = React.useMemo(() => inputHistory.filter(cmd => cmd.startsWith('/')).slice(-10), [inputHistory]);
139
- // Handle changes coming from the ChatInput. During typing, we update the
140
- // ref only — ChatInput paints itself synchronously from its own ref, so we
141
- // don't need to re-render the parent. We still want to clear stale slash
142
- // suggestions and reset history navigation, but those setters bail out
143
- // via functional updates when the value is already current.
144
- const handleInputChange = useCallback((newValue) => {
145
- inputRef.current = newValue;
146
- if (!newValue || !newValue.startsWith('/')) {
147
- setSuggestions(prev => prev.length > 0 ? [] : prev);
148
- }
149
- setHistoryIndex(prev => prev === -1 ? prev : -1);
150
- }, []);
151
- // Navigate input history
152
- const navigateHistory = useCallback((direction) => {
153
- if (inputHistory.length === 0)
154
- return;
155
- if (direction === 'up') {
156
- if (historyIndex === -1) {
157
- // Save current input before navigating
158
- setSavedInput(inputRef.current);
159
- setHistoryIndex(inputHistory.length - 1);
160
- setInputValue(inputHistory[inputHistory.length - 1]);
161
- }
162
- else if (historyIndex > 0) {
163
- setHistoryIndex(historyIndex - 1);
164
- setInputValue(inputHistory[historyIndex - 1]);
165
- }
166
- }
167
- else {
168
- if (historyIndex === -1)
169
- return;
170
- if (historyIndex < inputHistory.length - 1) {
171
- setHistoryIndex(historyIndex + 1);
172
- setInputValue(inputHistory[historyIndex + 1]);
173
- }
174
- else {
175
- // Return to saved input
176
- setHistoryIndex(-1);
177
- setInputValue(savedInput);
178
- }
179
- }
180
- }, [inputHistory, historyIndex, savedInput, setInputValue]);
181
- // Add to history when submitting
182
- const addToHistory = useCallback((value) => {
183
- if (value.trim() && (inputHistory.length === 0 || inputHistory[inputHistory.length - 1] !== value)) {
184
- setInputHistory(prev => [...prev.slice(-100), value]); // Keep last 100 entries
185
- }
186
- setHistoryIndex(-1);
187
- setSavedInput('');
188
- }, [inputHistory]);
189
- // Config state
190
- // Use lazy initializers to avoid calling config.get() on every render
191
- const [provider, setProvider] = useState(() => process.env.CALLIOPE_PROVIDER || config.get('defaultProvider'));
192
- const [model, setModel] = useState(() => process.env.CALLIOPE_MODEL || config.get('defaultModel'));
193
- const [persona, setPersona] = useState(() => config.get('persona'));
194
- const [mode, setMode] = useState('hybrid'); // Default to hybrid mode
195
- const [confirmMode, setConfirmMode] = useState(true); // Require confirmation for risky ops
196
- const [layout, setLayout] = useState(() => config.get('layout') || 'response-bottom');
197
- const [density, setDensity] = useState(() => config.get('density') || 'normal');
198
- const [collapseSettings, setCollapseSettings] = useState(() => ({
199
- collapseTools: config.get('collapseTools') ?? false,
200
- collapseThinking: config.get('collapseThinking') ?? false,
201
- toolDisplayLimit: config.get('toolDisplayLimit') ?? 0,
202
- }));
203
- // Modal state
204
- const [modalMode, setModalMode] = useState('none');
205
- const [providerEntries, setProviderEntries] = useState([]);
206
- const [pendingSetupProvider, setPendingSetupProvider] = useState(null);
207
- const [pendingComplexPrompt, setPendingComplexPrompt] = useState(null);
208
- const [previousSession, setPreviousSession] = useState(null);
209
- const [pendingToolCall, setPendingToolCall] = useState(null);
210
- const [availableModels, setAvailableModels] = useState([]);
211
- const [availableSessions, setAvailableSessions] = useState([]);
212
- const [latestVersion, setLatestVersion] = useState(null);
213
- // Stats
214
- const [stats, setStats] = useState({
215
- inputTokens: 0,
216
- outputTokens: 0,
217
- cost: 0,
218
- messageCount: 0,
219
- });
220
- const [contextTokens, setContextTokens] = useState(0);
221
- // Message queue for human-in-the-loop feedback during processing
222
- const [queuedMessages, setQueuedMessages] = useState([]);
223
- const queuedMessagesRef = useRef([]); // Ref to avoid stale closure in runAgent
224
- const [queueInput, setQueueInput] = useState('');
225
- const [editingQueueIndex, setEditingQueueIndex] = useState(null);
226
- // Keep ref in sync with state
227
- useEffect(() => {
228
- queuedMessagesRef.current = queuedMessages;
229
- }, [queuedMessages]);
230
- // Refs for scuttlebot polling — avoids stale closures across re-renders
231
- const isProcessingRef = useRef(false);
232
- const handleSubmitRef = useRef(async () => { });
233
- const openProviderPickerRef = useRef(null);
234
- // Keep isProcessingRef in sync
235
- useEffect(() => {
236
- isProcessingRef.current = isProcessing;
237
- }, [isProcessing]);
238
- // Undo/Redo history - stores snapshots of conversation state
239
- const undoStack = useRef([]);
240
- const redoStack = useRef([]);
241
- const MAX_UNDO_HISTORY = 10;
242
- // Conversation bookmarks
243
- const [bookmarks, setBookmarks] = useState([]);
244
- // Prompt templates
245
- const [templates, setTemplates] = useState([]);
246
- // Save state before changes (call before modifying messages)
247
- const saveUndoState = useCallback(() => {
248
- undoStack.current.push({
249
- messages: [...messages],
250
- llmMessages: [...llmMessages.current],
251
- timestamp: new Date(),
252
- });
253
- // Limit stack size
254
- if (undoStack.current.length > MAX_UNDO_HISTORY) {
255
- undoStack.current.shift();
256
- }
257
- // Clear redo stack on new action
258
- redoStack.current = [];
259
- }, [messages]);
260
- // LLM conversation history
261
- const llmMessages = useRef([
262
- { role: 'system', content: getSystemPrompt(persona) }
263
- ]);
264
- // Keep system prompt in sync when persona changes (fixes #46 - persona lost on model fallback)
265
- useEffect(() => {
266
- const firstMsg = llmMessages.current[0];
267
- if (firstMsg && firstMsg.role === 'system') {
268
- // Preserve any appended memory context
269
- const currentContent = typeof firstMsg.content === 'string' ? firstMsg.content : '';
270
- const memoryIdx = currentContent.indexOf('\n\n--- Project Context ---\n');
271
- const memoryPart = memoryIdx >= 0 ? currentContent.slice(memoryIdx) : '';
272
- llmMessages.current[0] = {
273
- role: 'system',
274
- content: getSystemPrompt(persona) + memoryPart,
275
- };
276
- }
277
- }, [persona]);
278
- // Estimate context tokens (conservative: ~2.5 chars per token + 1.35x overhead)
279
- const estimateContextTokens = useCallback(() => {
280
- let chars = 0;
281
- let msgCount = 0;
282
- for (const msg of llmMessages.current) {
283
- msgCount++;
284
- if (typeof msg.content === 'string') {
285
- chars += msg.content.length;
286
- }
287
- else if (Array.isArray(msg.content)) {
288
- for (const block of msg.content) {
289
- if (block.type === 'text') {
290
- chars += block.text.length;
291
- }
292
- else if (block.type === 'image') {
293
- chars += 1000; // Images count as ~250 tokens
294
- }
295
- }
296
- }
297
- // Include tool call arguments in estimation
298
- if (msg.toolCalls) {
299
- for (const tool of msg.toolCalls) {
300
- chars += JSON.stringify(tool.arguments || {}).length;
301
- }
302
- }
303
- }
304
- // Conservative: 2.5 chars/token, 1.35x overhead for formatting/metadata, +50 per message
305
- return Math.round((chars / 2.5) * 1.35 + msgCount * 50);
306
- }, []);
307
- // Session state
308
- const sessionRef = useRef(null);
309
- const [autoRoute, setAutoRoute] = useState(false); // Auto model routing
310
- const [smartRouteActive, setSmartRouteActive] = useState(() => config.get('smartRoutingEnabled') ?? false);
311
- const [breakerHealth, setBreakerHealth] = useState('ok');
312
- const ledgerRef = useRef(new IterationLedger());
313
- const circuitBreakerRef = useRef(config.get('circuitBreakersEnabled') !== false ? (() => {
314
- const iterTimeSec = config.get('maxIterationTime');
315
- const cb = new CircuitBreaker();
316
- if (typeof iterTimeSec === 'number' && iterTimeSec > 0) {
317
- cb.adjust('wall-clock', { maxIterationDurationMs: iterTimeSec * 1000 });
318
- }
319
- // Local/free providers: disable cost breaker, relax token limits
320
- const prov = config.get('defaultProvider');
321
- if (prov === 'ollama' || prov === 'litellm') {
322
- cb.adjust('cost-runaway', { maxSessionCost: 999999, maxCostPerMinute: 999999 });
323
- cb.adjust('token-burn', { maxTokensPerIteration: 500_000, maxTotalTokens: 20_000_000 });
324
- }
325
- return cb;
326
- })() : null);
327
- const smartRoutingConfigRef = useRef({
328
- ...getDefaultSmartRoutingConfig(),
329
- enabled: config.get('smartRoutingEnabled') ?? false,
330
- costSensitivity: config.get('smartRoutingCostSensitivity') ?? 0.3,
331
- });
332
- const [memoryLoaded, setMemoryLoaded] = useState(false);
333
- // Agent loop state
334
- const [loopActive, setLoopActive] = useState(false);
335
- const [loopPrompt, setLoopPrompt] = useState('');
336
- const [loopMaxIterations, setLoopMaxIterations] = useState(() => resolveIterationLimit(config.get('maxIterations')));
337
- const [loopCompletionPromise, setLoopCompletionPromise] = useState();
338
- const [loopIteration, setLoopIteration] = useState(0);
339
- const loopCancelledRef = useRef(false);
340
- // Initialize session and load memory on mount
341
- useEffect(() => {
342
- const cwd = process.cwd();
343
- // Always start fresh session - skip resume dialog
344
- // Note: Previous session data is still available via storage APIs if needed
345
- const session = storage.getOrCreateSession(cwd);
346
- sessionRef.current = session;
347
- ledgerRef.current.setRetentionLimit(config.get('sessionLogLimit') ?? 0);
348
- ledgerRef.current.setOnChange(() => {
349
- const activeSessionId = sessionRef.current?.id;
350
- if (activeSessionId) {
351
- storage.saveIterationLedger(ledgerRef.current, activeSessionId);
352
- }
353
- });
354
- ledgerRef.current.loadSnapshot(storage.loadIterationLedger(session.id));
355
- storage.saveIterationLedger(ledgerRef.current, session.id);
356
- // Load memory context into system prompt
357
- if (!memoryLoaded) {
358
- const cwdMem = process.cwd();
359
- const memoryContext = memory.buildMemoryContext(cwdMem);
360
- if (memoryContext.trim()) {
361
- // Append memory context to system prompt
362
- const currentSystem = llmMessages.current[0];
363
- if (currentSystem && currentSystem.role === 'system') {
364
- const systemContent = typeof currentSystem.content === 'string'
365
- ? currentSystem.content
366
- : '';
367
- llmMessages.current[0] = {
368
- role: 'system',
369
- content: systemContent + '\n\n--- Project Context ---\n' + memoryContext,
370
- };
371
- }
372
- }
373
- setMemoryLoaded(true);
374
- // Execute session start hooks
375
- hooks.executeHooks('session-start', {}).catch((err) => {
376
- debugLog('hooks', 'session-start hook failed:', err instanceof Error ? err.message : err);
377
- });
378
- // Start session recording (audit log) — respects config
379
- recording.setRecordingEnabled(config.get('recordSessions') !== false);
380
- recording.setRetentionDays(config.get('recordingRetentionDays') ?? 0);
381
- recording.startRecording({
382
- provider: selectProvider(provider),
383
- model: model || DEFAULT_MODELS[selectProvider(provider)],
384
- cwd: cwdMem,
385
- });
386
- // Initialize scuttlebot integration
387
- scuttlebotClient.initialize(session.id, cwdMem).then((enabled) => {
388
- if (enabled) {
389
- const status = scuttlebotClient.getStatus();
390
- debugLog('scuttlebot', `enabled, nick=${status.nick}, irc=${status.config?.ircAddr}`);
391
- // Show nick in system messages so operators know how to address calliope
392
- addMessage('system', `IRC connected — address me as: ${status.nick}`);
393
- scuttlebotClient.postOnline().catch(() => { });
394
- scuttlebotClient.postMessage(`connected — address me as: ${status.nick}`).catch(() => { });
395
- // Route incoming IRC instructions into the agent loop
396
- scuttlebotClient.startPolling((instruction) => {
397
- if (isProcessingRef.current) {
398
- setQueuedMessages(prev => [...prev, instruction]);
399
- }
400
- else {
401
- void handleSubmitRef.current(instruction);
402
- }
403
- });
404
- }
405
- }).catch((err) => {
406
- debugLog('scuttlebot', 'initialization failed:', err instanceof Error ? err.message : err);
407
- });
408
- // Configure session timeout (opt-in via config)
409
- const timeoutMs = config.get('sessionTimeoutMs');
410
- if (timeoutMs) {
411
- sessionTimeout.configureTimeout({
412
- enabled: true,
413
- idleTimeoutMs: typeof timeoutMs === 'number' ? timeoutMs : 2 * 60 * 60 * 1000,
414
- });
415
- sessionTimeout.onTimeout((type) => {
416
- if (type === 'warning') {
417
- addMessage('system', `\u23f1\ufe0f Session will timeout in ${sessionTimeout.formatTimeRemaining()}`);
418
- }
419
- else {
420
- addMessage('system', '\ud83d\udeaa Session timeout. Saving and exiting...');
421
- storage.saveMessageHistory(llmMessages.current);
422
- }
423
- });
424
- }
425
- // Start idle eviction monitor
426
- idleEviction.configureEviction({ enabled: true });
427
- idleEviction.onEviction((action) => {
428
- if (action === 'auto-save') {
429
- storage.saveMessageHistory(llmMessages.current);
430
- }
431
- });
432
- // Log tmux context if applicable
433
- if (isTmux()) {
434
- const info = getTmuxInfo();
435
- if (info) {
436
- debugLog('tmux', `session=${info.session}, windows=${info.windows}, panes=${info.panes}`);
437
- }
438
- }
439
- // Load templates from storage
440
- const savedTemplates = storage.getTemplates();
441
- if (savedTemplates.length > 0) {
442
- setTemplates(savedTemplates.map(t => ({
443
- name: t.name,
444
- prompt: t.prompt,
445
- createdAt: new Date(t.createdAt),
446
- })));
447
- }
448
- // Pre-warm model cache in background for faster model switching
449
- preWarmModelCache().catch((err) => {
450
- debugLog('cache', 'model cache pre-warm failed:', err instanceof Error ? err.message : err);
451
- });
452
- }
453
- }, [memoryLoaded]);
454
- // Derived values
455
- const actualProvider = selectProvider(provider);
456
- const actualModel = model || DEFAULT_MODELS[actualProvider];
457
- const isModalActive = modalMode !== 'none';
458
- // Add message helper
459
- const addMessage = useCallback((type, content, isError) => {
460
- setMessages(prev => [...prev, {
461
- id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
462
- type,
463
- content,
464
- // isError is plumbed through to the renderer (messages.tsx) so the tool
465
- // status icon is driven by the authoritative executeTool flag rather than
466
- // string-matching the output. Omitted (undefined) for non-tool messages.
467
- ...(isError !== undefined ? { isError } : {}),
468
- }]);
469
- // Persist user and assistant messages to storage for session history
470
- if (type === 'user' || type === 'assistant') {
471
- storage.addChatMessage({ role: type, content });
472
- }
473
- }, []);
474
- // Handler to edit or delete a queued message
475
- const handleEditQueuedMessage = useCallback((index, newMsg) => {
476
- if (newMsg === '') {
477
- // Delete the message
478
- setQueuedMessages(prev => prev.filter((_, i) => i !== index));
479
- addMessage('system', `🗑️ Deleted queued message #${index + 1}`);
480
- }
481
- else {
482
- // Update the message
483
- setQueuedMessages(prev => prev.map((msg, i) => i === index ? newMsg : msg));
484
- addMessage('system', `✏️ Updated queued message #${index + 1}`);
485
- }
486
- }, [addMessage]);
487
- // Validate and repair message history
488
- const validateAndRepairMessages = useCallback(() => {
489
- return validateAndRepairMessagesImpl({
490
- llmMessages,
491
- addMessage,
492
- debugLog,
493
- });
494
- }, [addMessage]);
495
- // Build agent context
496
- const buildAgentContext = useCallback(() => ({
497
- provider,
498
- model,
499
- mode,
500
- confirmMode,
501
- autoRoute,
502
- actualProvider,
503
- actualModel,
504
- stats,
505
- agtermEnabled: moduleAgtermEnabled,
506
- ledger: ledgerRef.current,
507
- circuitBreaker: circuitBreakerRef.current || undefined,
508
- smartRouteActive,
509
- smartRoutingConfig: smartRoutingConfigRef.current,
510
- setBreakerHealth,
511
- setStats,
512
- setStreamingResponse,
513
- setThinkingState,
514
- setActivityState,
515
- setContextTokens,
516
- setIsProcessing,
517
- setQueuedMessages,
518
- setEditingQueueIndex,
519
- setLoopIteration,
520
- setLoopActive,
521
- llmMessages,
522
- queuedMessagesRef,
523
- loopCancelledRef,
524
- sessionRef,
525
- addMessage,
526
- estimateContextTokens,
527
- validateAndRepairMessages,
528
- debugLog,
529
- }), [provider, model, mode, confirmMode, autoRoute, smartRouteActive, actualProvider, actualModel, stats, addMessage, estimateContextTokens, validateAndRepairMessages]);
530
- // Run agent with user prompt
531
- const runAgent = useCallback(async (content) => {
532
- const ctx = buildAgentContext();
533
- await runAgentImpl(ctx, content);
534
- }, [buildAgentContext]);
535
- // Agent loop - runs prompt repeatedly until completion promise or max iterations
536
- const runLoop = useCallback(async (prompt, maxIter, completionPromise) => {
537
- const ctx = buildAgentContext();
538
- await runLoopImpl(ctx, prompt, maxIter, completionPromise);
539
- }, [buildAgentContext]);
540
- // Build command context
541
- const buildCommandContext = useCallback(() => ({
542
- actualProvider,
543
- actualModel,
544
- provider,
545
- model,
546
- persona,
547
- mode,
548
- confirmMode,
549
- autoRoute,
550
- layout,
551
- density,
552
- collapseSettings,
553
- messages,
554
- stats,
555
- loopActive,
556
- isProcessing,
557
- thinkingState,
558
- streamingResponse,
559
- queuedMessages,
560
- bookmarks,
561
- templates,
562
- agtermEnabled: moduleAgtermEnabled,
563
- debugEnabled,
564
- modalMode,
565
- circuitBreaker: circuitBreakerRef.current || undefined,
566
- smartRouteActive,
567
- smartRoutingConfig: smartRoutingConfigRef.current,
568
- ledger: ledgerRef.current,
569
- setProvider,
570
- setModel,
571
- setPersona,
572
- setMode,
573
- setConfirmMode,
574
- setAutoRoute,
575
- setLayout: setLayout,
576
- setDensity: setDensity,
577
- setCollapseSettings,
578
- setMessages,
579
- setStats,
580
- setModalMode: setModalMode,
581
- setPendingComplexPrompt,
582
- setAvailableModels,
583
- setAvailableSessions,
584
- setLatestVersion,
585
- setLoopActive,
586
- setLoopPrompt,
587
- setLoopMaxIterations,
588
- setLoopCompletionPromise,
589
- setLoopIteration,
590
- setIsProcessing,
591
- setThinkingState,
592
- setStreamingResponse,
593
- setQueuedMessages,
594
- setInput: setInputValue,
595
- setBookmarks,
596
- setTemplates,
597
- setContextTokens,
598
- setDebugEnabled: (v) => { debugEnabled = v; },
599
- setSmartRouteActive,
600
- setBreakerHealth,
601
- llmMessages,
602
- undoStack,
603
- redoStack,
604
- loopCancelledRef,
605
- sessionRef,
606
- addMessage,
607
- estimateContextTokens,
608
- saveUndoState,
609
- runAgent,
610
- runLoop,
611
- exit,
612
- startScuttlebotPolling: () => {
613
- scuttlebotClient.startPolling((instruction) => {
614
- if (isProcessingRef.current) {
615
- setQueuedMessages(prev => [...prev, instruction]);
616
- }
617
- else {
618
- void handleSubmitRef.current(instruction);
619
- }
620
- });
621
- },
622
- openProviderPicker: () => openProviderPickerRef.current?.(),
623
- }), [actualProvider, actualModel, provider, model, persona, mode, confirmMode, autoRoute, smartRouteActive,
624
- layout, density, collapseSettings, messages, stats, loopActive, isProcessing,
625
- thinkingState, streamingResponse, queuedMessages, bookmarks, templates, modalMode,
626
- addMessage, estimateContextTokens, saveUndoState, runAgent, runLoop, exit]);
627
- // Handle slash commands
628
- const handleCommandWrapped = useCallback(async (cmd) => {
629
- const ctx = buildCommandContext();
630
- await handleCommand(cmd, ctx);
631
- }, [buildCommandContext]);
632
- // Handle input submission
633
- const handleSubmit = useCallback(async (value) => {
634
- const trimmed = value.trim();
635
- if (!trimmed || isProcessing)
636
- return;
637
- // Record activity for timeout/eviction and audit log
638
- sessionTimeout.recordActivity();
639
- idleEviction.recordActivity();
640
- recording.recordEvent('input', trimmed);
641
- // Add to history for up/down arrow navigation
642
- addToHistory(trimmed);
643
- setInputValue('');
644
- if (trimmed.startsWith('/')) {
645
- await handleCommandWrapped(trimmed);
646
- return;
647
- }
648
- // ! prefix executes shell commands directly
649
- if (trimmed.startsWith('!')) {
650
- const shellCmd = trimmed.slice(1).trim();
651
- if (shellCmd) {
652
- addMessage('system', `$ ${shellCmd}`);
653
- try {
654
- const { execSync } = await import('child_process');
655
- const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
656
- const output = execSync(shellCmd, {
657
- cwd: activeCwd,
658
- encoding: 'utf-8',
659
- timeout: 30000,
660
- stdio: ['pipe', 'pipe', 'pipe'],
661
- maxBuffer: 10 * 1024 * 1024,
662
- }).trim();
663
- addMessage('system', output || '(no output)');
664
- }
665
- catch (err) {
666
- const execErr = err;
667
- addMessage('error', execErr.stderr?.trim() || execErr.message || String(err));
668
- }
669
- }
670
- return;
671
- }
672
- // In hybrid mode, check for complex operations
673
- if (mode === 'hybrid') {
674
- const complexity = detectComplexity(trimmed);
675
- if (complexity.isComplex) {
676
- setPendingComplexPrompt({ prompt: trimmed, complexity });
677
- setModalMode('complexity-warning');
678
- return;
679
- }
680
- }
681
- // Save state for undo before modifying conversation
682
- saveUndoState();
683
- // Parse file references from input
684
- const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
685
- const { text: cleanText, files } = parseFileReferences(trimmed, activeCwd);
686
- // Show user message (with file info if any)
687
- if (files.length > 0) {
688
- const fileInfo = formatFileInfo(files);
689
- addMessage('user', `${cleanText}\n📎 ${fileInfo}`);
690
- }
691
- else {
692
- addMessage('user', trimmed);
693
- }
694
- // Mirror user input to IRC so observers see what prompted each agent run
695
- if (scuttlebotClient.isEnabled()) {
696
- scuttlebotClient.postMessage(cleanText || trimmed).catch(() => { });
697
- }
698
- setIsProcessing(true);
699
- try {
700
- // Build message content (with file/image support)
701
- let messageContent;
702
- if (files.length > 0) {
703
- const visionSupported = supportsVision(provider, model);
704
- const { content, warnings } = processFilesForMessage(cleanText || trimmed, files, visionSupported);
705
- // Show any warnings about files
706
- for (const warning of warnings) {
707
- addMessage('system', warning);
708
- }
709
- messageContent = content;
710
- }
711
- else {
712
- messageContent = trimmed;
713
- }
714
- await runAgent(messageContent);
715
- }
716
- finally {
717
- setIsProcessing(false);
718
- setThinkingState(null);
719
- setStreamingResponse('');
720
- }
721
- }, [isProcessing, handleCommandWrapped, runAgent, addMessage, provider, model, saveUndoState, addToHistory, mode, setInputValue]);
722
- // Keep handleSubmitRef current so scuttlebot polling never captures a stale closure
20
+ import { spawnPendingRestart } from './self-restart.js';
21
+ import { useChatController } from './state/use-chat-controller.js';
22
+ import { TranscriptRegion } from './regions/transcript-region.js';
23
+ import { StatusRegion } from './regions/status-region.js';
24
+ import { InputRegion } from './regions/input-region.js';
25
+ import { ModalHost } from './regions/modal-host.js';
26
+ import { probeMount } from './regions/render-probe.js';
27
+ export function TerminalChat({ controllerRef }) {
28
+ const c = useChatController();
29
+ // Mount counter for the reset-without-remount assertion (no-op in production).
30
+ useEffect(() => { probeMount('terminal-chat'); }, []);
31
+ // Expose resetSession for tests/automation; production renders without a ref.
723
32
  useEffect(() => {
724
- handleSubmitRef.current = handleSubmit;
725
- }, [handleSubmit]);
726
- // Modal handlers
727
- const handleModelSelect = useCallback((selectedModel) => {
728
- setModel(selectedModel);
729
- addMessage('system', `Model: ${selectedModel}`);
730
- setModalMode('none');
731
- setAvailableModels([]);
732
- }, [addMessage]);
733
- const handleModalCancel = useCallback(() => {
734
- setModalMode('none');
735
- setAvailableModels([]);
736
- setLatestVersion(null);
737
- }, []);
738
- const handleUpgradeConfirm = useCallback(async () => {
739
- setModalMode('none');
740
- addMessage('system', 'Upgrading...');
741
- try {
742
- const { performUpgrade } = await import('../version-check.js');
743
- const success = await performUpgrade();
744
- if (success) {
745
- addMessage('system', 'Upgrade complete! Restarting...');
746
- requestSelfRestart(process.argv.slice(1));
747
- exit();
748
- return;
749
- }
750
- else {
751
- addMessage('error', 'Upgrade failed. Try: npm install -g @calliopelabs/cli@latest');
752
- }
753
- }
754
- catch (e) {
755
- addMessage('error', `Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
756
- }
757
- setLatestVersion(null);
758
- }, [addMessage]);
759
- // Cycle through modes
760
- const cycleMode = useCallback(() => {
761
- setMode(current => {
762
- const modes = ['plan', 'hybrid', 'work'];
763
- const idx = modes.indexOf(current);
764
- const next = modes[(idx + 1) % modes.length];
765
- return next;
766
- });
767
- }, []);
768
- // Handle Escape key - cancel operation if processing, otherwise show hint
769
- const handleEscape = useCallback(() => {
770
- if (isProcessing) {
771
- // Cancel current operation
772
- setIsProcessing(false);
773
- setThinkingState(null);
774
- setStreamingResponse('');
775
- setLoopActive(false);
776
- setEditingQueueIndex(null);
777
- addMessage('system', '⏹ Operation cancelled. Press Ctrl+C again to quit.');
778
- }
779
- else if (modalMode !== 'none') {
780
- // Close any open modal
781
- setModalMode('none');
782
- setPendingComplexPrompt(null);
783
- }
784
- else {
785
- // Not processing - show hint. Second Ctrl+C within 2s will actually exit.
786
- addMessage('system', '💡 Press Ctrl+C again to quit, or /exit.');
787
- }
788
- }, [isProcessing, modalMode, addMessage]);
789
- const handleExit = useCallback(() => {
790
- exit();
791
- }, [exit]);
792
- // Build the list of providers with their configuration status for the picker.
793
- // Ordering: Ollama first (recommended onboarding), then others by rough popularity.
794
- const buildProviderEntries = useCallback(() => {
795
- const hasBedrock = !!(config.getApiKey('bedrock') || config.getBaseUrl('bedrock')
796
- || process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE);
797
- const entries = [
798
- { id: 'ollama', label: 'Ollama', configured: !!config.getBaseUrl('ollama'), configHint: 'OLLAMA_BASE_URL', recommended: true, note: 'local, free' },
799
- { id: 'anthropic', label: 'Anthropic', configured: !!config.getApiKey('anthropic'), configHint: 'ANTHROPIC_API_KEY' },
800
- { id: 'openai', label: 'OpenAI', configured: !!config.getApiKey('openai'), configHint: 'OPENAI_API_KEY' },
801
- { id: 'google', label: 'Google', configured: !!config.getApiKey('google'), configHint: 'GOOGLE_API_KEY' },
802
- { id: 'mistral', label: 'Mistral', configured: !!config.getApiKey('mistral'), configHint: 'MISTRAL_API_KEY' },
803
- { id: 'openrouter', label: 'OpenRouter', configured: !!config.getApiKey('openrouter'), configHint: 'OPENROUTER_API_KEY' },
804
- { id: 'together', label: 'Together', configured: !!config.getApiKey('together'), configHint: 'TOGETHER_API_KEY' },
805
- { id: 'groq', label: 'Groq', configured: !!config.getApiKey('groq'), configHint: 'GROQ_API_KEY' },
806
- { id: 'fireworks', label: 'Fireworks', configured: !!config.getApiKey('fireworks'), configHint: 'FIREWORKS_API_KEY' },
807
- { id: 'ai21', label: 'AI21', configured: !!config.getApiKey('ai21'), configHint: 'AI21_API_KEY' },
808
- { id: 'huggingface', label: 'HuggingFace', configured: !!config.getApiKey('huggingface'), configHint: 'HUGGINGFACE_API_KEY' },
809
- { id: 'bedrock', label: 'AWS Bedrock', configured: hasBedrock, configHint: 'AWS_PROFILE or AWS_ACCESS_KEY_ID', note: 'AWS credentials' },
810
- { id: 'litellm', label: 'LiteLLM', configured: !!config.getBaseUrl('litellm'), configHint: 'LITELLM_BASE_URL' },
811
- ];
812
- return entries;
813
- }, []);
814
- const openProviderPicker = useCallback(() => {
815
- setProviderEntries(buildProviderEntries());
816
- setModalMode('provider');
817
- }, [buildProviderEntries]);
818
- const handleProviderSelect = useCallback((entry) => {
819
- if (entry.configured) {
820
- setProvider(entry.id);
821
- addMessage('system', `Provider: ${entry.label}${entry.id === 'ollama' ? ' (local)' : ''}`);
822
- setModalMode('none');
823
- setProviderEntries([]);
824
- return;
825
- }
826
- // Not configured — open inline setup.
827
- setPendingSetupProvider(entry);
828
- setModalMode('api-key-setup');
829
- }, [addMessage, setProvider]);
830
- const handleApiKeySubmit = useCallback((value) => {
831
- const entry = pendingSetupProvider;
832
- if (!entry) {
833
- setModalMode('none');
33
+ if (!controllerRef)
834
34
  return;
835
- }
836
- try {
837
- // Map provider → config key. Bedrock/Ollama/LiteLLM are special (base URL or AWS).
838
- if (entry.id === 'ollama') {
839
- config.set('ollamaBaseUrl', value);
840
- }
841
- else if (entry.id === 'litellm') {
842
- config.set('litellmBaseUrl', value);
843
- }
844
- else if (entry.id === 'bedrock') {
845
- // Simplest path: user enters AWS_PROFILE name. Write to env for this session;
846
- // persistence is user's responsibility (profile lives in ~/.aws).
847
- process.env.AWS_PROFILE = value;
848
- // Clear any stale env-var credentials (e.g. from a prior `aws sso login`
849
- // export that's since expired) so the profile actually wins.
850
- delete process.env.AWS_ACCESS_KEY_ID;
851
- delete process.env.AWS_SECRET_ACCESS_KEY;
852
- delete process.env.AWS_SESSION_TOKEN;
853
- addMessage('system', `AWS_PROFILE=${value} set for this session. Add to shell rc to persist.`);
854
- }
855
- else {
856
- const keyMap = {
857
- anthropic: 'anthropicApiKey',
858
- openai: 'openaiApiKey',
859
- google: 'googleApiKey',
860
- mistral: 'mistralApiKey',
861
- openrouter: 'openrouterApiKey',
862
- together: 'togetherApiKey',
863
- groq: 'groqApiKey',
864
- fireworks: 'fireworksApiKey',
865
- ai21: 'ai21ApiKey',
866
- huggingface: 'huggingfaceApiKey',
867
- };
868
- const configKey = keyMap[entry.id];
869
- if (!configKey)
870
- throw new Error(`No config mapping for ${entry.id}`);
871
- // Cast through any — the mapping above guarantees a valid ApiKey field.
872
- config.set(configKey, value);
873
- }
874
- setProvider(entry.id);
875
- addMessage('system', `✓ Configured ${entry.label}. Provider switched.`);
876
- }
877
- catch (e) {
878
- addMessage('error', `Failed to configure ${entry.label}: ${e instanceof Error ? e.message : String(e)}`);
879
- }
880
- setPendingSetupProvider(null);
881
- setModalMode('none');
882
- }, [pendingSetupProvider, addMessage, setProvider]);
883
- const handleApiKeyCancel = useCallback(() => {
884
- setPendingSetupProvider(null);
885
- setModalMode('none');
886
- }, []);
887
- // Forward-declared ref so buildCommandContext (defined earlier) can open the
888
- // provider picker without a TDZ on the openProviderPicker callback.
889
- useEffect(() => {
890
- openProviderPickerRef.current = openProviderPicker;
891
- }, [openProviderPicker]);
892
- // Handle direct send (Shift+Enter) - interrupts current operation and sends immediately
893
- const handleDirectSend = useCallback((msg) => {
894
- // Stop current processing
895
- setIsProcessing(false);
896
- setThinkingState(null);
897
- setStreamingResponse('');
898
- setEditingQueueIndex(null);
899
- // Show what happened
900
- addMessage('system', '⚡ Direct send - interrupting current operation');
901
- addMessage('user', msg);
902
- // Start new agent run with this message
903
- setIsProcessing(true);
904
- runAgent(msg).finally(() => {
905
- setIsProcessing(false);
906
- setThinkingState(null);
907
- setStreamingResponse('');
908
- setEditingQueueIndex(null);
909
- });
910
- }, [addMessage, runAgent]);
911
- // Streaming response component (reused across layouts)
912
- const StreamingResponseBox = streamingResponse ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["\u2727 ", getCurrentCompanion().name, ":"] }), streamingResponse.split('\n').map((line, i) => (_jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "\u2502" }), " ", line] }, i))), _jsx(Text, { color: "cyan", children: "\u258C" })] })) : null;
913
- // Thinking/Processing indicator component with state transitions
914
- const ProcessingBox = (_jsxs(_Fragment, { children: [transition && (_jsx(StateTransition, { from: transition.from, to: transition.to, onComplete: () => setTransition(null) })), isProcessing && thinkingState && !streamingResponse && _jsx(ThinkingDisplay, { state: thinkingState }), isProcessing && !thinkingState && !streamingResponse && _jsx(ProcessingIndicator, { label: "Waiting for response" }), isProcessing && streamingResponse && _jsx(StreamingIndicator, { activity: activityState ?? undefined })] }));
915
- // Render based on layout
916
- return (_jsx(HUDFrame, { width: width, children: _jsxs(Box, { flexDirection: "column", width: width, children: [layout === 'split' && (_jsxs(Box, { flexDirection: "row", width: width, children: [_jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsx(Text, { color: "yellow", dimColor: true, children: "\u2500 Tools \u2500" }), _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox] }), _jsxs(Box, { flexDirection: "column", width: "50%", borderStyle: "single", borderLeft: true, borderColor: "gray", children: [_jsx(Text, { color: "cyan", dimColor: true, children: "\u2500 Response \u2500" }), StreamingResponseBox] })] })), layout === 'classic' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), layout === 'response-top' && (_jsxs(_Fragment, { children: [StreamingResponseBox, _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox] })), layout === 'response-bottom' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), layout === 'zen' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages.filter(m => m.type === 'user' || m.type === 'assistant'), collapseSettings: { collapseTools: true, collapseThinking: true, toolDisplayLimit: 0 } }), ProcessingBox, StreamingResponseBox] })), layout === 'focus' && (_jsxs(_Fragment, { children: [StreamingResponseBox, ProcessingBox, _jsx(MessageHistory, { messages: messages, collapseSettings: { collapseTools: true, collapseThinking: true, toolDisplayLimit: 3 } })] })), layout === 'dashboard' && (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: [' ', stats.inputTokens ? `tokens: ${stats.inputTokens}/${stats.outputTokens}` : '', stats.cost ? ` | cost: $${stats.cost.toFixed(4)}` : '', model ? ` | ${model}` : '', ` | ${getCurrentCompanion().name}`] }), StreamingResponseBox, ProcessingBox, _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings })] })), layout === 'minimal' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), debugEnabled && (_jsx(Box, { marginY: 0, children: _jsxs(Text, { dimColor: true, children: ["[dbg] proc=", isProcessing ? 'Y' : 'N', " think=", thinkingState ? 'Y' : 'N', " stream=", streamingResponse.length, " mode=", mode, " queue=", queuedMessages.length, " activity=", activityState?.action || 'none'] }) })), modalMode === 'model' && availableModels.length > 0 && (_jsx(ModelSelector, { models: availableModels, onSelect: handleModelSelect, onCancel: handleModalCancel })), modalMode === 'sessions' && (_jsx(SessionSelector, { sessions: availableSessions, onSelect: (session) => {
917
- // Load history from selected session
918
- addMessage('system', `Loading session: ${session.projectName}...`);
919
- addMessage('system', `Session path: ${session.projectPath}\nTo load this session, run calliope from that directory.`);
920
- setModalMode('none');
921
- }, onDelete: (session) => {
922
- if (storage.deleteSession(session.id)) {
923
- addMessage('system', `🗑️ Deleted session: ${session.projectName}`);
924
- // Refresh the list
925
- setAvailableSessions(prev => prev.filter(s => s.id !== session.id));
926
- }
927
- else {
928
- addMessage('error', `Failed to delete session: ${session.projectName}`);
929
- }
930
- }, onCancel: handleModalCancel })), modalMode === 'upgrade' && latestVersion && (_jsx(UpgradePrompt, { currentVersion: getVersion(), latestVersion: latestVersion, onConfirm: handleUpgradeConfirm, onCancel: handleModalCancel })), modalMode === 'session-resume' && previousSession && (_jsx(SessionResumePrompt, { session: previousSession, onResume: () => {
931
- const savedMessages = storage.loadMessageHistory();
932
- if (savedMessages && savedMessages.length > 0) {
933
- llmMessages.current.length = 0;
934
- for (const msg of savedMessages) {
935
- llmMessages.current.push(msg);
936
- }
937
- addMessage('system', `✓ Resumed session with ${savedMessages.length} saved messages loaded`);
938
- const activeSessionId = sessionRef.current?.id;
939
- if (activeSessionId) {
940
- ledgerRef.current.loadSnapshot(storage.loadIterationLedger(activeSessionId));
941
- storage.saveIterationLedger(ledgerRef.current, activeSessionId);
942
- }
943
- setContextTokens(estimateContextTokens());
944
- }
945
- else {
946
- const history = storage.getChatHistory(20);
947
- if (history.length > 0) {
948
- llmMessages.current.length = 0;
949
- const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
950
- const basePrompt = getSystemPrompt(persona);
951
- const memoryContext = memory.buildMemoryContext(activeCwd);
952
- llmMessages.current.push({
953
- role: 'system',
954
- content: memoryContext.trim()
955
- ? `${basePrompt}\n\n--- Project Context ---\n${memoryContext}`
956
- : basePrompt,
957
- });
958
- for (const msg of history) {
959
- if (msg.role === 'user' || msg.role === 'assistant') {
960
- llmMessages.current.push({
961
- role: msg.role,
962
- content: msg.content,
963
- });
964
- }
965
- }
966
- addMessage('system', `✓ Resumed session with ${history.length} chat messages loaded`);
967
- setContextTokens(estimateContextTokens());
968
- }
969
- }
970
- setModalMode('none');
971
- setPreviousSession(null);
972
- }, onNew: () => {
973
- addMessage('system', '✓ Starting fresh session');
974
- setModalMode('none');
975
- setPreviousSession(null);
976
- } })), modalMode === 'complexity-warning' && pendingComplexPrompt && (_jsx(ComplexityWarning, { reason: pendingComplexPrompt.complexity.reason || 'Complex operation detected', prompt: typeof pendingComplexPrompt.prompt === 'string' ? pendingComplexPrompt.prompt : undefined, onProceed: async () => {
977
- setModalMode('none');
978
- const prompt = pendingComplexPrompt.prompt;
979
- setPendingComplexPrompt(null);
980
- // Proceed with execution
981
- saveUndoState();
982
- addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
983
- setIsProcessing(true);
984
- try {
985
- await runAgent(prompt);
986
- }
987
- finally {
988
- setIsProcessing(false);
989
- }
990
- }, onPlan: () => {
991
- setModalMode('none');
992
- const prompt = pendingComplexPrompt.prompt;
993
- setPendingComplexPrompt(null);
994
- // Switch to plan mode and proceed
995
- setMode('plan');
996
- addMessage('system', '📋 Switched to Plan mode - I\'ll describe what I would do without executing.');
997
- saveUndoState();
998
- addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
999
- setIsProcessing(true);
1000
- runAgent(prompt).finally(() => setIsProcessing(false));
1001
- }, onCancel: () => {
1002
- setModalMode('none');
1003
- setPendingComplexPrompt(null);
1004
- addMessage('system', 'Operation cancelled.');
1005
- } })), modalMode === 'keys' && (_jsx(KeybindingsModal, { onClose: () => setModalMode('none') })), modalMode === 'provider' && providerEntries.length > 0 && (_jsx(ProviderSelector, { providers: providerEntries, onSelect: handleProviderSelect, onCancel: () => {
1006
- setModalMode('none');
1007
- setProviderEntries([]);
1008
- } })), modalMode === 'api-key-setup' && pendingSetupProvider && (_jsx(ApiKeySetup, { provider: pendingSetupProvider.id, configHint: pendingSetupProvider.configHint, onSubmit: handleApiKeySubmit, onCancel: handleApiKeyCancel, extraInstructions: pendingSetupProvider.id === 'ollama'
1009
- ? 'e.g. http://localhost:11434 (start with: ollama serve)'
1010
- : pendingSetupProvider.id === 'bedrock'
1011
- ? 'Enter your AWS profile name. Ensure it exists in ~/.aws/credentials or ~/.aws/config.'
1012
- : pendingSetupProvider.id === 'litellm'
1013
- ? 'e.g. http://localhost:4000'
1014
- : undefined })), modalMode === 'theme-picker' && (_jsx(ThemePicker, { currentLayout: layout, currentSkin: getCurrentSkin().name, currentPalette: getCurrentPalette().name, currentCompanion: getCurrentCompanion().name, onApply: (selection) => {
1015
- // Apply all selections
1016
- setLayout(selection.layout);
1017
- config.set('layout', selection.layout);
1018
- applySkin(selection.skin);
1019
- config.set('activeSkin', selection.skin);
1020
- applyPalette(selection.palette);
1021
- config.set('activePalette', selection.palette);
1022
- applyCompanion(selection.companion);
1023
- config.set('activeCompanion', selection.companion);
1024
- const changes = [];
1025
- if (selection.layout !== layout)
1026
- changes.push(`layout=${selection.layout}`);
1027
- if (selection.skin !== getCurrentSkin().name)
1028
- changes.push(`skin=${selection.skin}`);
1029
- if (selection.palette !== getCurrentPalette().name)
1030
- changes.push(`palette=${selection.palette}`);
1031
- if (selection.companion !== getCurrentCompanion().name)
1032
- changes.push(`companion=${selection.companion}`);
1033
- addMessage('system', changes.length > 0
1034
- ? `Theme applied: ${changes.join(', ')}`
1035
- : 'Theme unchanged.');
1036
- setModalMode('none');
1037
- }, onCancel: () => setModalMode('none') })), modalMode === 'pack-picker' && (_jsx(PackPicker, { onApply: async (packName) => {
1038
- setModalMode('none');
1039
- // Run transition animation if defined
1040
- const targetPack = getThemePack(packName);
1041
- if (targetPack?.skin.splash?.transition) {
1042
- await renderTransition(targetPack.skin.splash.transition);
1043
- }
1044
- const success = applyThemePack(packName, getCompanionMode());
1045
- if (success) {
1046
- const pack = getCurrentPack();
1047
- config.set('activeThemePack', packName);
1048
- config.set('activeSkin', pack.skin.name);
1049
- config.set('activePalette', pack.palette.name);
1050
- const companion = getCompanionMode() === 'professional'
1051
- ? pack.companions.professional
1052
- : pack.companions.immersive;
1053
- config.set('activeCompanion', companion.name);
1054
- addMessage('system', `Theme pack: ${packName}\n` +
1055
- ` Skin: ${pack.skin.name}, Palette: ${pack.palette.name}, Companion: ${companion.name}\n` +
1056
- ` "${companion.greeting}"`);
1057
- }
1058
- }, onCancel: () => setModalMode('none') })), _jsx(ChatInput, { value: input, valueVersion: inputVersion, onChange: handleInputChange, onSubmit: handleSubmit, onEscape: handleEscape, onExit: handleExit, onCycleMode: cycleMode, disabled: isModalActive, isProcessing: isProcessing, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, editingQueueIndex: editingQueueIndex, onQueueMessage: (msg) => {
1059
- setQueuedMessages(prev => [...prev, msg]);
1060
- addMessage('system', `📨 Queued: "${msg.substring(0, 50)}${msg.length > 50 ? '...' : ''}"`);
1061
- }, onEditQueuedMessage: handleEditQueuedMessage, onSetEditingQueueIndex: setEditingQueueIndex, onDirectSend: handleDirectSend, cwd: process.cwd(), suggestions: suggestions, onSuggestionsChange: setSuggestions, onNavigateHistory: navigateHistory,
1062
- // Smart suggestions context
1063
- currentMode: mode, contextPercentage: Math.round((contextTokens / getModelContextLimit(actualProvider, actualModel)) * 100), recentCommands: recentCommands, hasGitRepo: hasGitRepo }), _jsx(StatusBar, { provider: actualProvider, model: actualModel, mode: mode, stats: stats, contextTokens: contextTokens, breakerHealth: config.get('circuitBreakersEnabled') !== false ? breakerHealth : undefined, smartRouteActive: smartRouteActive, width: width })] }) }));
35
+ controllerRef.current = { resetSession: c.resetSession };
36
+ return () => { controllerRef.current = null; };
37
+ }, [controllerRef, c.resetSession]);
38
+ return (_jsx(HUDFrame, { width: c.width, children: _jsxs(Box, { flexDirection: "column", width: c.width, children: [_jsx(TranscriptRegion, { ...c.transcript }), _jsx(ModalHost, { ...c.modal }), _jsx(InputRegion, { ...c.input }), _jsx(StatusRegion, { ...c.status })] }) }));
1064
39
  }
1065
40
  // ============================================================================
1066
41
  // App Wrapper & Entry Point
1067
42
  // ============================================================================
1068
43
  function App() {
1069
- const [resetKey, setResetKey] = React.useState(0);
1070
- const handleReset = React.useCallback(() => {
1071
- setResetKey(k => k + 1);
1072
- }, []);
1073
- return (_jsx(ErrorBoundary, { onReset: handleReset, children: _jsx(TerminalChat, {}, resetKey) }));
44
+ // The ErrorBoundary swaps in a fallback on a render crash; retrying clears its
45
+ // error state, which remounts TerminalChat fresh — no remount key needed.
46
+ return (_jsx(ErrorBoundary, { children: _jsx(TerminalChat, {}) }));
1074
47
  }
1075
48
  // Print banner before Ink takes over (stays fixed at top)
1076
49
  export async function printBanner() {
1077
- const provider = selectProvider(config.get('defaultProvider'));
50
+ const requested = config.get('defaultProvider');
51
+ // Never claim a provider that won't serve (#217). If the selected provider is
52
+ // unconfigured, show the real selection annotated as such rather than
53
+ // crashing the banner or pretending a working provider.
54
+ let provider;
55
+ let providerNote = '';
56
+ try {
57
+ provider = selectProvider(requested);
58
+ }
59
+ catch (err) {
60
+ provider = err instanceof ProviderUnavailableError ? err.provider : requested;
61
+ providerNote = ' (not configured — run calliope --setup)';
62
+ }
1078
63
  const model = config.get('defaultModel') || DEFAULT_MODELS[provider];
1079
64
  const skin = getCurrentSkin();
1080
65
  const dim = '\x1b[2m';
@@ -1122,17 +107,11 @@ export async function printBanner() {
1122
107
  console.log();
1123
108
  }
1124
109
  }
1125
- const companion = getCurrentCompanion();
1126
- console.log(`${dim} v${getVersion()} | ${provider}:${model}${reset}`);
110
+ console.log(`${dim} v${getVersion()} | ${provider}:${model}${providerNote}${reset}`);
1127
111
  console.log(`${dim} /help for commands | ESC to exit${reset}`);
1128
- if (companion.greeting) {
1129
- console.log(`${dim} ${companion.greeting}${reset}`);
1130
- }
1131
112
  console.log();
1132
113
  }
1133
114
  export async function startInkCLI(options = {}) {
1134
- // Set module-level agterm state
1135
- moduleAgtermEnabled = options.agtermEnabled ?? false;
1136
115
  // Print banner BEFORE Ink starts - it stays fixed at the top
1137
116
  await printBanner();
1138
117
  const { waitUntilExit } = render(_jsx(App, {}), {
@@ -1140,17 +119,7 @@ export async function startInkCLI(options = {}) {
1140
119
  });
1141
120
  await waitUntilExit();
1142
121
  // Session cleanup
1143
- if (scuttlebotClient.isEnabled()) {
1144
- await scuttlebotClient.postOffline().catch(() => {
1145
- // Silent fail
1146
- });
1147
- await scuttlebotClient.disconnect().catch(() => {
1148
- // Silent fail
1149
- });
1150
- }
1151
- recording.stopRecording();
1152
- sessionTimeout.clearTimers();
1153
- idleEviction.stopMonitor();
122
+ await fleetPostOffline();
1154
123
  await spawnPendingRestart();
1155
124
  }
1156
125
  //# sourceMappingURL=index.js.map