whale-code 6.5.5 → 6.5.6

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 (847) hide show
  1. package/README.md +39 -31
  2. package/bin/{swagmanager-mcp.js → whale-code.js} +17 -2
  3. package/dist/cli/app.js +148 -72
  4. package/dist/cli/app.js.map +1 -0
  5. package/dist/cli/chat/AgentSelector.js +105 -10
  6. package/dist/cli/chat/AgentSelector.js.map +1 -0
  7. package/dist/cli/chat/ChatApp.d.ts +31 -0
  8. package/dist/cli/chat/ChatApp.js +539 -286
  9. package/dist/cli/chat/ChatApp.js.map +1 -0
  10. package/dist/cli/chat/ChatInput.js +1088 -770
  11. package/dist/cli/chat/ChatInput.js.map +1 -0
  12. package/dist/cli/chat/MarkdownText.js +39 -14
  13. package/dist/cli/chat/MarkdownText.js.map +1 -0
  14. package/dist/cli/chat/MemoryManager.js +181 -46
  15. package/dist/cli/chat/MemoryManager.js.map +1 -0
  16. package/dist/cli/chat/MessageList.d.ts +2 -3
  17. package/dist/cli/chat/MessageList.js +186 -45
  18. package/dist/cli/chat/MessageList.js.map +1 -0
  19. package/dist/cli/chat/ModelSelector.js +282 -63
  20. package/dist/cli/chat/ModelSelector.js.map +1 -0
  21. package/dist/cli/chat/NodeManager.js +165 -75
  22. package/dist/cli/chat/NodeManager.js.map +1 -0
  23. package/dist/cli/chat/NodeSelector.js +171 -30
  24. package/dist/cli/chat/NodeSelector.js.map +1 -0
  25. package/dist/cli/chat/PlanApproval.js +281 -57
  26. package/dist/cli/chat/PlanApproval.js.map +1 -0
  27. package/dist/cli/chat/RewindViewer.js +559 -144
  28. package/dist/cli/chat/RewindViewer.js.map +1 -0
  29. package/dist/cli/chat/SessionManager.js +137 -30
  30. package/dist/cli/chat/SessionManager.js.map +1 -0
  31. package/dist/cli/chat/SlashMenu.js +293 -164
  32. package/dist/cli/chat/SlashMenu.js.map +1 -0
  33. package/dist/cli/chat/StatusBar.js +172 -9
  34. package/dist/cli/chat/StatusBar.js.map +1 -0
  35. package/dist/cli/chat/StoreSelector.js +147 -18
  36. package/dist/cli/chat/StoreSelector.js.map +1 -0
  37. package/dist/cli/chat/StreamingText.d.ts +1 -5
  38. package/dist/cli/chat/StreamingText.js +22 -7
  39. package/dist/cli/chat/StreamingText.js.map +1 -0
  40. package/dist/cli/chat/SubagentPanel.d.ts +1 -2
  41. package/dist/cli/chat/SubagentPanel.js +612 -72
  42. package/dist/cli/chat/SubagentPanel.js.map +1 -0
  43. package/dist/cli/chat/TeamPanel.d.ts +1 -0
  44. package/dist/cli/chat/TeamPanel.js +230 -30
  45. package/dist/cli/chat/TeamPanel.js.map +1 -0
  46. package/dist/cli/chat/ThemeSelector.js +84 -24
  47. package/dist/cli/chat/ThemeSelector.js.map +1 -0
  48. package/dist/cli/chat/ToolIndicator.js +1476 -371
  49. package/dist/cli/chat/ToolIndicator.js.map +1 -0
  50. package/dist/cli/chat/hooks/useAgentLoop.d.ts +1 -0
  51. package/dist/cli/chat/hooks/useAgentLoop.js +481 -367
  52. package/dist/cli/chat/hooks/useAgentLoop.js.map +1 -0
  53. package/dist/cli/chat/hooks/useSlashCommands.d.ts +3 -14
  54. package/dist/cli/chat/hooks/useSlashCommands.js +744 -572
  55. package/dist/cli/chat/hooks/useSlashCommands.js.map +1 -0
  56. package/dist/cli/commands/config-cmd.js +56 -57
  57. package/dist/cli/commands/config-cmd.js.map +1 -0
  58. package/dist/cli/commands/db.js +184 -169
  59. package/dist/cli/commands/db.js.map +1 -0
  60. package/dist/cli/commands/doctor.js +212 -122
  61. package/dist/cli/commands/doctor.js.map +1 -0
  62. package/dist/cli/commands/init.js +211 -244
  63. package/dist/cli/commands/init.js.map +1 -0
  64. package/dist/cli/commands/mcp.js +127 -122
  65. package/dist/cli/commands/mcp.js.map +1 -0
  66. package/dist/cli/login/LoginApp.js +355 -141
  67. package/dist/cli/login/LoginApp.js.map +1 -0
  68. package/dist/cli/print-mode.js +196 -177
  69. package/dist/cli/print-mode.js.map +1 -0
  70. package/dist/cli/serve-mode.js +615 -530
  71. package/dist/cli/serve-mode.js.map +1 -0
  72. package/dist/cli/services/agent-config.d.ts +5 -1
  73. package/dist/cli/services/agent-config.js +66 -36
  74. package/dist/cli/services/agent-config.js.map +1 -0
  75. package/dist/cli/services/agent-definitions.d.ts +4 -1
  76. package/dist/cli/services/agent-definitions.js +97 -56
  77. package/dist/cli/services/agent-definitions.js.map +1 -0
  78. package/dist/cli/services/agent-events.js +225 -162
  79. package/dist/cli/services/agent-events.js.map +1 -0
  80. package/dist/cli/services/agent-loop.js +976 -688
  81. package/dist/cli/services/agent-loop.js.map +1 -0
  82. package/dist/cli/services/agent-worker-base.d.ts +35 -5
  83. package/dist/cli/services/agent-worker-base.js +337 -153
  84. package/dist/cli/services/agent-worker-base.js.map +1 -0
  85. package/dist/cli/services/api-retry.js +69 -64
  86. package/dist/cli/services/api-retry.js.map +1 -0
  87. package/dist/cli/services/auth-service.d.ts +3 -3
  88. package/dist/cli/services/auth-service.js +209 -132
  89. package/dist/cli/services/auth-service.js.map +1 -0
  90. package/dist/cli/services/background-processes.js +343 -267
  91. package/dist/cli/services/background-processes.js.map +1 -0
  92. package/dist/cli/services/browser-auth.d.ts +2 -2
  93. package/dist/cli/services/browser-auth.js +159 -118
  94. package/dist/cli/services/browser-auth.js.map +1 -0
  95. package/dist/cli/services/claude-md-loader.js +40 -36
  96. package/dist/cli/services/claude-md-loader.js.map +1 -0
  97. package/dist/cli/services/config-store.d.ts +9 -4
  98. package/dist/cli/services/config-store.js +164 -117
  99. package/dist/cli/services/config-store.js.map +1 -0
  100. package/dist/cli/services/debug-log.d.ts +1 -1
  101. package/dist/cli/services/debug-log.js +34 -35
  102. package/dist/cli/services/debug-log.js.map +1 -0
  103. package/dist/cli/services/env-detect.d.ts +7 -0
  104. package/dist/cli/services/env-detect.js +9 -0
  105. package/dist/cli/services/env-detect.js.map +1 -0
  106. package/dist/cli/services/error-logger.js +187 -169
  107. package/dist/cli/services/error-logger.js.map +1 -0
  108. package/dist/cli/services/file-history.d.ts +1 -1
  109. package/dist/cli/services/file-history.js +50 -54
  110. package/dist/cli/services/file-history.js.map +1 -0
  111. package/dist/cli/services/format-server-response.js +332 -372
  112. package/dist/cli/services/format-server-response.js.map +1 -0
  113. package/dist/cli/services/git-context.js +61 -45
  114. package/dist/cli/services/git-context.js.map +1 -0
  115. package/dist/cli/services/hooks.d.ts +2 -2
  116. package/dist/cli/services/hooks.js +195 -180
  117. package/dist/cli/services/hooks.js.map +1 -0
  118. package/dist/cli/services/ink-incremental.d.ts +19 -0
  119. package/dist/cli/services/ink-incremental.js +59 -0
  120. package/dist/cli/services/ink-incremental.js.map +1 -0
  121. package/dist/cli/services/ink-resize-fix.js +54 -44
  122. package/dist/cli/services/ink-resize-fix.js.map +1 -0
  123. package/dist/cli/services/ink-sync-output.d.ts +12 -0
  124. package/dist/cli/services/ink-sync-output.js +16 -0
  125. package/dist/cli/services/ink-sync-output.js.map +1 -0
  126. package/dist/cli/services/interactive-tools.js +268 -212
  127. package/dist/cli/services/interactive-tools.js.map +1 -0
  128. package/dist/cli/services/keybinding-manager.d.ts +11 -1
  129. package/dist/cli/services/keybinding-manager.js +126 -63
  130. package/dist/cli/services/keybinding-manager.js.map +1 -0
  131. package/dist/cli/services/local-tools.d.ts +1 -1
  132. package/dist/cli/services/local-tools.js +939 -656
  133. package/dist/cli/services/local-tools.js.map +1 -0
  134. package/dist/cli/services/lsp-manager.js +757 -594
  135. package/dist/cli/services/lsp-manager.js.map +1 -0
  136. package/dist/cli/services/mcp-client.d.ts +1 -1
  137. package/dist/cli/services/mcp-client.js +173 -134
  138. package/dist/cli/services/mcp-client.js.map +1 -0
  139. package/dist/cli/services/memory-manager.js +53 -40
  140. package/dist/cli/services/memory-manager.js.map +1 -0
  141. package/dist/cli/services/model-manager.js +55 -40
  142. package/dist/cli/services/model-manager.js.map +1 -0
  143. package/dist/cli/services/model-router.js +115 -85
  144. package/dist/cli/services/model-router.js.map +1 -0
  145. package/dist/cli/services/paths.d.ts +30 -0
  146. package/dist/cli/services/paths.js +81 -0
  147. package/dist/cli/services/paths.js.map +1 -0
  148. package/dist/cli/services/permission-modes.js +32 -25
  149. package/dist/cli/services/permission-modes.js.map +1 -0
  150. package/dist/cli/services/rewind.js +182 -168
  151. package/dist/cli/services/rewind.js.map +1 -0
  152. package/dist/cli/services/ripgrep.js +115 -115
  153. package/dist/cli/services/ripgrep.js.map +1 -0
  154. package/dist/cli/services/sandbox.d.ts +1 -1
  155. package/dist/cli/services/sandbox.js +58 -37
  156. package/dist/cli/services/sandbox.js.map +1 -0
  157. package/dist/cli/services/server-tools.js +738 -565
  158. package/dist/cli/services/server-tools.js.map +1 -0
  159. package/dist/cli/services/session-persistence.js +69 -74
  160. package/dist/cli/services/session-persistence.js.map +1 -0
  161. package/dist/cli/services/subagent-worker.js +42 -27
  162. package/dist/cli/services/subagent-worker.js.map +1 -0
  163. package/dist/cli/services/subagent.d.ts +2 -0
  164. package/dist/cli/services/subagent.js +605 -433
  165. package/dist/cli/services/subagent.js.map +1 -0
  166. package/dist/cli/services/system-prompt.js +86 -78
  167. package/dist/cli/services/system-prompt.js.map +1 -0
  168. package/dist/cli/services/task-decomposer.d.ts +1 -1
  169. package/dist/cli/services/task-decomposer.js +172 -139
  170. package/dist/cli/services/task-decomposer.js.map +1 -0
  171. package/dist/cli/services/team-lead.d.ts +2 -2
  172. package/dist/cli/services/team-lead.js +727 -529
  173. package/dist/cli/services/team-lead.js.map +1 -0
  174. package/dist/cli/services/team-state.js +319 -319
  175. package/dist/cli/services/team-state.js.map +1 -0
  176. package/dist/cli/services/teammate.d.ts +8 -2
  177. package/dist/cli/services/teammate.js +857 -569
  178. package/dist/cli/services/teammate.js.map +1 -0
  179. package/dist/cli/services/telemetry.d.ts +6 -1
  180. package/dist/cli/services/telemetry.js +180 -157
  181. package/dist/cli/services/telemetry.js.map +1 -0
  182. package/dist/cli/services/tools/agent-tools.d.ts +3 -3
  183. package/dist/cli/services/tools/agent-tools.js +480 -322
  184. package/dist/cli/services/tools/agent-tools.js.map +1 -0
  185. package/dist/cli/services/tools/file-ops.js +563 -450
  186. package/dist/cli/services/tools/file-ops.js.map +1 -0
  187. package/dist/cli/services/tools/search-tools.js +231 -162
  188. package/dist/cli/services/tools/search-tools.js.map +1 -0
  189. package/dist/cli/services/tools/shell-exec.js +197 -151
  190. package/dist/cli/services/tools/shell-exec.js.map +1 -0
  191. package/dist/cli/services/tools/task-manager.js +206 -173
  192. package/dist/cli/services/tools/task-manager.js.map +1 -0
  193. package/dist/cli/services/tools/web-tools.js +388 -341
  194. package/dist/cli/services/tools/web-tools.js.map +1 -0
  195. package/dist/cli/setup/SetupApp.d.ts +2 -2
  196. package/dist/cli/setup/SetupApp.js +608 -160
  197. package/dist/cli/setup/SetupApp.js.map +1 -0
  198. package/dist/cli/shared/ErrorBoundary.d.ts +22 -0
  199. package/dist/cli/shared/ErrorBoundary.js +73 -0
  200. package/dist/cli/shared/ErrorBoundary.js.map +1 -0
  201. package/dist/cli/shared/MatrixIntro.js +66 -69
  202. package/dist/cli/shared/MatrixIntro.js.map +1 -0
  203. package/dist/cli/shared/SpinnerSlot.d.ts +14 -0
  204. package/dist/cli/shared/SpinnerSlot.js +63 -0
  205. package/dist/cli/shared/SpinnerSlot.js.map +1 -0
  206. package/dist/cli/shared/Theme.d.ts +1 -1
  207. package/dist/cli/shared/Theme.js +136 -92
  208. package/dist/cli/shared/Theme.js.map +1 -0
  209. package/dist/cli/shared/WhaleBanner.js +99 -11
  210. package/dist/cli/shared/WhaleBanner.js.map +1 -0
  211. package/dist/cli/shared/markdown.d.ts +3 -1
  212. package/dist/cli/shared/markdown.js +736 -674
  213. package/dist/cli/shared/markdown.js.map +1 -0
  214. package/dist/cli/shared/marked-terminal.d.js +2 -0
  215. package/dist/cli/shared/marked-terminal.d.js.map +1 -0
  216. package/dist/cli/shared/theme-manager.js +99 -90
  217. package/dist/cli/shared/theme-manager.js.map +1 -0
  218. package/dist/cli/shared/theme-presets.js +256 -254
  219. package/dist/cli/shared/theme-presets.js.map +1 -0
  220. package/dist/cli/status/StatusApp.js +235 -86
  221. package/dist/cli/status/StatusApp.js.map +1 -0
  222. package/dist/cli/stores/StoreApp.js +275 -65
  223. package/dist/cli/stores/StoreApp.js.map +1 -0
  224. package/dist/index.d.ts +2 -2
  225. package/dist/index.js +509 -396
  226. package/dist/index.js.map +1 -0
  227. package/dist/local-agent/connection.d.ts +2 -2
  228. package/dist/local-agent/connection.js +352 -293
  229. package/dist/local-agent/connection.js.map +1 -0
  230. package/dist/local-agent/discovery.js +259 -122
  231. package/dist/local-agent/discovery.js.map +1 -0
  232. package/dist/local-agent/executor.js +216 -193
  233. package/dist/local-agent/executor.js.map +1 -0
  234. package/dist/local-agent/index.d.ts +2 -2
  235. package/dist/local-agent/index.js +156 -156
  236. package/dist/local-agent/index.js.map +1 -0
  237. package/dist/node/adapters/base.js +18 -8
  238. package/dist/node/adapters/base.js.map +1 -0
  239. package/dist/node/adapters/discord.js +286 -275
  240. package/dist/node/adapters/discord.js.map +1 -0
  241. package/dist/node/adapters/email.js +189 -202
  242. package/dist/node/adapters/email.js.map +1 -0
  243. package/dist/node/adapters/imessage.js +145 -142
  244. package/dist/node/adapters/imessage.js.map +1 -0
  245. package/dist/node/adapters/slack.js +237 -236
  246. package/dist/node/adapters/slack.js.map +1 -0
  247. package/dist/node/adapters/sms.js +149 -151
  248. package/dist/node/adapters/sms.js.map +1 -0
  249. package/dist/node/adapters/telegram.js +88 -92
  250. package/dist/node/adapters/telegram.js.map +1 -0
  251. package/dist/node/adapters/webchat.js +160 -136
  252. package/dist/node/adapters/webchat.js.map +1 -0
  253. package/dist/node/adapters/whatsapp.js +212 -215
  254. package/dist/node/adapters/whatsapp.js.map +1 -0
  255. package/dist/node/cli.js +884 -653
  256. package/dist/node/cli.js.map +1 -0
  257. package/dist/node/config.js +20 -18
  258. package/dist/node/config.js.map +1 -0
  259. package/dist/node/gateway-client.js +191 -181
  260. package/dist/node/gateway-client.js.map +1 -0
  261. package/dist/node/portal/clipboard.js +161 -130
  262. package/dist/node/portal/clipboard.js.map +1 -0
  263. package/dist/node/portal/discovery.js +51 -45
  264. package/dist/node/portal/discovery.js.map +1 -0
  265. package/dist/node/portal/forward.js +64 -58
  266. package/dist/node/portal/forward.js.map +1 -0
  267. package/dist/node/portal/index.js +246 -221
  268. package/dist/node/portal/index.js.map +1 -0
  269. package/dist/node/portal/multiplexer.js +192 -182
  270. package/dist/node/portal/multiplexer.js.map +1 -0
  271. package/dist/node/portal/permissions.js +102 -70
  272. package/dist/node/portal/permissions.js.map +1 -0
  273. package/dist/node/portal/protocol.js +153 -116
  274. package/dist/node/portal/protocol.js.map +1 -0
  275. package/dist/node/portal/screen.js +80 -69
  276. package/dist/node/portal/screen.js.map +1 -0
  277. package/dist/node/portal/session.js +124 -117
  278. package/dist/node/portal/session.js.map +1 -0
  279. package/dist/node/portal/shell.js +140 -113
  280. package/dist/node/portal/shell.js.map +1 -0
  281. package/dist/node/portal/stream.js +77 -75
  282. package/dist/node/portal/stream.js.map +1 -0
  283. package/dist/node/portal/transfer.js +190 -167
  284. package/dist/node/portal/transfer.js.map +1 -0
  285. package/dist/node/portal/ui.js +124 -99
  286. package/dist/node/portal/ui.js.map +1 -0
  287. package/dist/node/remote-desktop/compile-helper.js +50 -45
  288. package/dist/node/remote-desktop/compile-helper.js.map +1 -0
  289. package/dist/node/remote-desktop/index.js +215 -187
  290. package/dist/node/remote-desktop/index.js.map +1 -0
  291. package/dist/node/remote-desktop/protocol.js +45 -29
  292. package/dist/node/remote-desktop/protocol.js.map +1 -0
  293. package/dist/node/runtime.js +493 -410
  294. package/dist/node/runtime.js.map +1 -0
  295. package/dist/server/handlers/__test-utils__/test-db.js +39 -89
  296. package/dist/server/handlers/__test-utils__/test-db.js.map +1 -0
  297. package/dist/server/handlers/analytics.js +467 -261
  298. package/dist/server/handlers/analytics.js.map +1 -0
  299. package/dist/server/handlers/api-docs.js +1030 -895
  300. package/dist/server/handlers/api-docs.js.map +1 -0
  301. package/dist/server/handlers/api-keys.js +291 -242
  302. package/dist/server/handlers/api-keys.js.map +1 -0
  303. package/dist/server/handlers/billing.js +330 -239
  304. package/dist/server/handlers/billing.js.map +1 -0
  305. package/dist/server/handlers/browser.js +468 -395
  306. package/dist/server/handlers/browser.js.map +1 -0
  307. package/dist/server/handlers/catalog.js +1377 -978
  308. package/dist/server/handlers/catalog.js.map +1 -0
  309. package/dist/server/handlers/clickhouse.js +157 -109
  310. package/dist/server/handlers/clickhouse.js.map +1 -0
  311. package/dist/server/handlers/comms.js +1439 -984
  312. package/dist/server/handlers/comms.js.map +1 -0
  313. package/dist/server/handlers/creations.js +461 -394
  314. package/dist/server/handlers/creations.js.map +1 -0
  315. package/dist/server/handlers/crm.js +1082 -791
  316. package/dist/server/handlers/crm.js.map +1 -0
  317. package/dist/server/handlers/discovery.js +251 -232
  318. package/dist/server/handlers/discovery.js.map +1 -0
  319. package/dist/server/handlers/embeddings.js +241 -164
  320. package/dist/server/handlers/embeddings.js.map +1 -0
  321. package/dist/server/handlers/enrichment.js +887 -718
  322. package/dist/server/handlers/enrichment.js.map +1 -0
  323. package/dist/server/handlers/image-gen.js +467 -376
  324. package/dist/server/handlers/image-gen.js.map +1 -0
  325. package/dist/server/handlers/inventory.js +797 -424
  326. package/dist/server/handlers/inventory.js.map +1 -0
  327. package/dist/server/handlers/kali.js +272 -230
  328. package/dist/server/handlers/kali.js.map +1 -0
  329. package/dist/server/handlers/llm-providers.js +803 -580
  330. package/dist/server/handlers/llm-providers.js.map +1 -0
  331. package/dist/server/handlers/local-agent.js +133 -105
  332. package/dist/server/handlers/local-agent.js.map +1 -0
  333. package/dist/server/handlers/media.js +1179 -857
  334. package/dist/server/handlers/media.js.map +1 -0
  335. package/dist/server/handlers/meta-ads.js +2669 -2093
  336. package/dist/server/handlers/meta-ads.js.map +1 -0
  337. package/dist/server/handlers/nodes.js +1321 -913
  338. package/dist/server/handlers/nodes.js.map +1 -0
  339. package/dist/server/handlers/operations.js +183 -157
  340. package/dist/server/handlers/operations.js.map +1 -0
  341. package/dist/server/handlers/platform.js +346 -210
  342. package/dist/server/handlers/platform.js.map +1 -0
  343. package/dist/server/handlers/remove-bg.js +118 -86
  344. package/dist/server/handlers/remove-bg.js.map +1 -0
  345. package/dist/server/handlers/storefront.js +586 -446
  346. package/dist/server/handlers/storefront.js.map +1 -0
  347. package/dist/server/handlers/supply-chain.js +546 -326
  348. package/dist/server/handlers/supply-chain.js.map +1 -0
  349. package/dist/server/handlers/transcription.js +106 -97
  350. package/dist/server/handlers/transcription.js.map +1 -0
  351. package/dist/server/handlers/video-gen.js +593 -424
  352. package/dist/server/handlers/video-gen.js.map +1 -0
  353. package/dist/server/handlers/voice.js +1458 -1039
  354. package/dist/server/handlers/voice.js.map +1 -0
  355. package/dist/server/handlers/workflow-steps.js +2837 -2116
  356. package/dist/server/handlers/workflow-steps.js.map +1 -0
  357. package/dist/server/handlers/workflows.js +1630 -933
  358. package/dist/server/handlers/workflows.js.map +1 -0
  359. package/dist/server/index.js +3167 -2422
  360. package/dist/server/index.js.map +1 -0
  361. package/dist/server/lib/batch-client.js +471 -409
  362. package/dist/server/lib/batch-client.js.map +1 -0
  363. package/dist/server/lib/clickhouse-buffer.js +118 -104
  364. package/dist/server/lib/clickhouse-buffer.js.map +1 -0
  365. package/dist/server/lib/clickhouse-client.js +107 -107
  366. package/dist/server/lib/clickhouse-client.js.map +1 -0
  367. package/dist/server/lib/coa-renderer.js +1786 -356
  368. package/dist/server/lib/coa-renderer.js.map +1 -0
  369. package/dist/server/lib/code-worker-pool.js +227 -177
  370. package/dist/server/lib/code-worker-pool.js.map +1 -0
  371. package/dist/server/lib/code-worker.js +174 -164
  372. package/dist/server/lib/code-worker.js.map +1 -0
  373. package/dist/server/lib/compaction-service.d.ts +2 -12
  374. package/dist/server/lib/compaction-service.js +74 -184
  375. package/dist/server/lib/compaction-service.js.map +1 -0
  376. package/dist/server/lib/logger.js +36 -24
  377. package/dist/server/lib/logger.js.map +1 -0
  378. package/dist/server/lib/otel.js +101 -80
  379. package/dist/server/lib/otel.js.map +1 -0
  380. package/dist/server/lib/pdf-renderer.js +952 -788
  381. package/dist/server/lib/pdf-renderer.js.map +1 -0
  382. package/dist/server/lib/prompt-sanitizer.js +188 -108
  383. package/dist/server/lib/prompt-sanitizer.js.map +1 -0
  384. package/dist/server/lib/provider-capabilities.js +136 -138
  385. package/dist/server/lib/provider-capabilities.js.map +1 -0
  386. package/dist/server/lib/provider-failover.js +190 -168
  387. package/dist/server/lib/provider-failover.js.map +1 -0
  388. package/dist/server/lib/rate-limiter.js +186 -117
  389. package/dist/server/lib/rate-limiter.js.map +1 -0
  390. package/dist/server/lib/react-pdf-layout.js +551 -382
  391. package/dist/server/lib/react-pdf-layout.js.map +1 -0
  392. package/dist/server/lib/server-agent-loop.d.ts +4 -1
  393. package/dist/server/lib/server-agent-loop.js +906 -634
  394. package/dist/server/lib/server-agent-loop.js.map +1 -0
  395. package/dist/server/lib/server-subagent.js +260 -164
  396. package/dist/server/lib/server-subagent.js.map +1 -0
  397. package/dist/server/lib/session-checkpoint.js +105 -96
  398. package/dist/server/lib/session-checkpoint.js.map +1 -0
  399. package/dist/server/lib/ssrf-guard.js +193 -184
  400. package/dist/server/lib/ssrf-guard.js.map +1 -0
  401. package/dist/server/lib/supabase-client.js +94 -82
  402. package/dist/server/lib/supabase-client.js.map +1 -0
  403. package/dist/server/lib/template-resolver.js +154 -176
  404. package/dist/server/lib/template-resolver.js.map +1 -0
  405. package/dist/server/lib/utils.js +242 -133
  406. package/dist/server/lib/utils.js.map +1 -0
  407. package/dist/server/local-agent-gateway.d.ts +2 -2
  408. package/dist/server/local-agent-gateway.js +785 -627
  409. package/dist/server/local-agent-gateway.js.map +1 -0
  410. package/dist/server/providers/anthropic.js +250 -172
  411. package/dist/server/providers/anthropic.js.map +1 -0
  412. package/dist/server/providers/bedrock.js +217 -158
  413. package/dist/server/providers/bedrock.js.map +1 -0
  414. package/dist/server/providers/gemini.js +548 -418
  415. package/dist/server/providers/gemini.js.map +1 -0
  416. package/dist/server/providers/openai.js +571 -437
  417. package/dist/server/providers/openai.js.map +1 -0
  418. package/dist/server/providers/registry.js +23 -18
  419. package/dist/server/providers/registry.js.map +1 -0
  420. package/dist/server/providers/shared.js +123 -95
  421. package/dist/server/providers/shared.js.map +1 -0
  422. package/dist/server/providers/types.js +1 -11
  423. package/dist/server/providers/types.js.map +1 -0
  424. package/dist/server/proxy-handlers.js +209 -165
  425. package/dist/server/proxy-handlers.js.map +1 -0
  426. package/dist/server/tool-router.js +959 -599
  427. package/dist/server/tool-router.js.map +1 -0
  428. package/dist/server/validation.js +248 -188
  429. package/dist/server/validation.js.map +1 -0
  430. package/dist/server/worker.js +202 -133
  431. package/dist/server/worker.js.map +1 -0
  432. package/dist/setup.d.ts +2 -2
  433. package/dist/setup.js +151 -147
  434. package/dist/setup.js.map +1 -0
  435. package/dist/shared/agent-core.d.ts +115 -26
  436. package/dist/shared/agent-core.js +956 -522
  437. package/dist/shared/agent-core.js.map +1 -0
  438. package/dist/shared/anthropic-types.js +1 -6
  439. package/dist/shared/anthropic-types.js.map +1 -0
  440. package/dist/shared/api-client.d.ts +16 -9
  441. package/dist/shared/api-client.js +419 -327
  442. package/dist/shared/api-client.js.map +1 -0
  443. package/dist/shared/compaction.d.ts +36 -0
  444. package/dist/shared/compaction.js +138 -0
  445. package/dist/shared/compaction.js.map +1 -0
  446. package/dist/shared/constants.js +67 -64
  447. package/dist/shared/constants.js.map +1 -0
  448. package/dist/shared/sse-parser.js +221 -219
  449. package/dist/shared/sse-parser.js.map +1 -0
  450. package/dist/shared/tool-dispatch.d.ts +4 -0
  451. package/dist/shared/tool-dispatch.js +226 -165
  452. package/dist/shared/tool-dispatch.js.map +1 -0
  453. package/dist/shared/types.js +1 -6
  454. package/dist/shared/types.js.map +1 -0
  455. package/dist/types/cli-highlight.d.js +2 -0
  456. package/dist/types/cli-highlight.d.js.map +1 -0
  457. package/dist/types/diff.d.js +2 -0
  458. package/dist/types/diff.d.js.map +1 -0
  459. package/dist/types/pdf-parse.d.js +2 -0
  460. package/dist/types/pdf-parse.d.js.map +1 -0
  461. package/dist/updater.d.ts +1 -1
  462. package/dist/updater.js +118 -92
  463. package/dist/updater.js.map +1 -0
  464. package/dist/webchat/widget.js +227 -380
  465. package/dist/webchat/widget.js.map +1 -0
  466. package/package.json +22 -10
  467. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  468. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  469. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  470. package/vendor/ink/build/apply-styles.js +175 -0
  471. package/vendor/ink/build/build-layout.js +77 -0
  472. package/vendor/ink/build/calculate-wrapped-text.js +53 -0
  473. package/vendor/ink/build/colorize.d.ts +3 -0
  474. package/vendor/ink/build/colorize.js +48 -0
  475. package/vendor/ink/build/colorize.js.map +1 -0
  476. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  477. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  478. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  479. package/vendor/ink/build/components/App.d.ts +18 -0
  480. package/vendor/ink/build/components/App.js +351 -0
  481. package/vendor/ink/build/components/App.js.map +1 -0
  482. package/vendor/ink/build/components/AppContext.d.ts +15 -0
  483. package/vendor/ink/build/components/AppContext.js +11 -0
  484. package/vendor/ink/build/components/AppContext.js.map +1 -0
  485. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  486. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  487. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  488. package/vendor/ink/build/components/Box.d.ts +117 -0
  489. package/vendor/ink/build/components/Box.js +34 -0
  490. package/vendor/ink/build/components/Box.js.map +1 -0
  491. package/vendor/ink/build/components/Color.js +62 -0
  492. package/vendor/ink/build/components/Cursor.d.ts +83 -0
  493. package/vendor/ink/build/components/Cursor.js +53 -0
  494. package/vendor/ink/build/components/Cursor.js.map +1 -0
  495. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  496. package/vendor/ink/build/components/CursorContext.js +8 -0
  497. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  498. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  499. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  500. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  501. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  502. package/vendor/ink/build/components/ErrorOverview.js +84 -0
  503. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  504. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  505. package/vendor/ink/build/components/FocusContext.js +17 -0
  506. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  507. package/vendor/ink/build/components/Newline.d.ts +13 -0
  508. package/vendor/ink/build/components/Newline.js +8 -0
  509. package/vendor/ink/build/components/Newline.js.map +1 -0
  510. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  511. package/vendor/ink/build/components/Spacer.js +11 -0
  512. package/vendor/ink/build/components/Spacer.js.map +1 -0
  513. package/vendor/ink/build/components/Static.d.ts +24 -0
  514. package/vendor/ink/build/components/Static.js +28 -0
  515. package/vendor/ink/build/components/Static.js.map +1 -0
  516. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  517. package/vendor/ink/build/components/StderrContext.js +13 -0
  518. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  519. package/vendor/ink/build/components/StdinContext.d.ts +22 -0
  520. package/vendor/ink/build/components/StdinContext.js +19 -0
  521. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  522. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  523. package/vendor/ink/build/components/StdoutContext.js +13 -0
  524. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  525. package/vendor/ink/build/components/Text.d.ts +55 -0
  526. package/vendor/ink/build/components/Text.js +50 -0
  527. package/vendor/ink/build/components/Text.js.map +1 -0
  528. package/vendor/ink/build/components/Transform.d.ts +16 -0
  529. package/vendor/ink/build/components/Transform.js +15 -0
  530. package/vendor/ink/build/components/Transform.js.map +1 -0
  531. package/vendor/ink/build/cursor-helpers.d.ts +38 -0
  532. package/vendor/ink/build/cursor-helpers.js +56 -0
  533. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  534. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  535. package/vendor/ink/build/devtools-window-polyfill.js +65 -0
  536. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  537. package/vendor/ink/build/devtools.d.ts +1 -0
  538. package/vendor/ink/build/devtools.js +11 -0
  539. package/vendor/ink/build/devtools.js.map +1 -0
  540. package/vendor/ink/build/dom.d.ts +56 -0
  541. package/vendor/ink/build/dom.js +124 -0
  542. package/vendor/ink/build/dom.js.map +1 -0
  543. package/vendor/ink/build/experimental/apply-style.js +140 -0
  544. package/vendor/ink/build/experimental/dom.js +123 -0
  545. package/vendor/ink/build/experimental/output.js +91 -0
  546. package/vendor/ink/build/experimental/reconciler.js +141 -0
  547. package/vendor/ink/build/experimental/renderer.js +81 -0
  548. package/vendor/ink/build/get-max-width.d.ts +3 -0
  549. package/vendor/ink/build/get-max-width.js +10 -0
  550. package/vendor/ink/build/get-max-width.js.map +1 -0
  551. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  552. package/vendor/ink/build/hooks/use-app.js +8 -0
  553. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  554. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  555. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  556. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  557. package/vendor/ink/build/hooks/use-focus-manager.d.ts +28 -0
  558. package/vendor/ink/build/hooks/use-focus-manager.js +17 -0
  559. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  560. package/vendor/ink/build/hooks/use-focus.d.ts +29 -0
  561. package/vendor/ink/build/hooks/use-focus.js +42 -0
  562. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  563. package/vendor/ink/build/hooks/use-input.d.ts +131 -0
  564. package/vendor/ink/build/hooks/use-input.js +124 -0
  565. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  566. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +5 -0
  567. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +11 -0
  568. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  569. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  570. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  571. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  572. package/vendor/ink/build/hooks/use-stdin.d.ts +5 -0
  573. package/vendor/ink/build/hooks/use-stdin.js +8 -0
  574. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  575. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  576. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  577. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  578. package/vendor/ink/build/hooks/useInput.js +38 -0
  579. package/vendor/ink/build/index.d.ts +34 -0
  580. package/vendor/ink/build/index.js +20 -0
  581. package/vendor/ink/build/index.js.map +1 -0
  582. package/vendor/ink/build/ink.d.ts +90 -0
  583. package/vendor/ink/build/ink.js +654 -0
  584. package/vendor/ink/build/ink.js.map +1 -0
  585. package/vendor/ink/build/input-parser.d.ts +7 -0
  586. package/vendor/ink/build/input-parser.js +154 -0
  587. package/vendor/ink/build/input-parser.js.map +1 -0
  588. package/vendor/ink/build/instance.js +205 -0
  589. package/vendor/ink/build/instances.d.ts +3 -0
  590. package/vendor/ink/build/instances.js +8 -0
  591. package/vendor/ink/build/instances.js.map +1 -0
  592. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  593. package/vendor/ink/build/kitty-keyboard.js +32 -0
  594. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  595. package/vendor/ink/build/layout.d.ts +7 -0
  596. package/vendor/ink/build/layout.js +33 -0
  597. package/vendor/ink/build/layout.js.map +1 -0
  598. package/vendor/ink/build/log-update.d.ts +19 -0
  599. package/vendor/ink/build/log-update.js +243 -0
  600. package/vendor/ink/build/log-update.js.map +1 -0
  601. package/vendor/ink/build/measure-element.d.ts +16 -0
  602. package/vendor/ink/build/measure-element.js +9 -0
  603. package/vendor/ink/build/measure-element.js.map +1 -0
  604. package/vendor/ink/build/measure-text.d.ts +6 -0
  605. package/vendor/ink/build/measure-text.js +21 -0
  606. package/vendor/ink/build/measure-text.js.map +1 -0
  607. package/vendor/ink/build/options.d.ts +52 -0
  608. package/vendor/ink/build/options.js +2 -0
  609. package/vendor/ink/build/options.js.map +1 -0
  610. package/vendor/ink/build/output.d.ts +35 -0
  611. package/vendor/ink/build/output.js +183 -0
  612. package/vendor/ink/build/output.js.map +1 -0
  613. package/vendor/ink/build/parse-keypress.d.ts +22 -0
  614. package/vendor/ink/build/parse-keypress.js +493 -0
  615. package/vendor/ink/build/parse-keypress.js.map +1 -0
  616. package/vendor/ink/build/reconciler.d.ts +4 -0
  617. package/vendor/ink/build/reconciler.js +274 -0
  618. package/vendor/ink/build/reconciler.js.map +1 -0
  619. package/vendor/ink/build/render-background.d.ts +4 -0
  620. package/vendor/ink/build/render-background.js +25 -0
  621. package/vendor/ink/build/render-background.js.map +1 -0
  622. package/vendor/ink/build/render-border.d.ts +4 -0
  623. package/vendor/ink/build/render-border.js +73 -0
  624. package/vendor/ink/build/render-border.js.map +1 -0
  625. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  626. package/vendor/ink/build/render-node-to-output.js +147 -0
  627. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  628. package/vendor/ink/build/render-to-string.d.ts +38 -0
  629. package/vendor/ink/build/render-to-string.js +115 -0
  630. package/vendor/ink/build/render-to-string.js.map +1 -0
  631. package/vendor/ink/build/render.d.ts +121 -0
  632. package/vendor/ink/build/render.js +55 -0
  633. package/vendor/ink/build/render.js.map +1 -0
  634. package/vendor/ink/build/renderer.d.ts +8 -0
  635. package/vendor/ink/build/renderer.js +55 -0
  636. package/vendor/ink/build/renderer.js.map +1 -0
  637. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  638. package/vendor/ink/build/sanitize-ansi.js +27 -0
  639. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  640. package/vendor/ink/build/screen-reader-update.d.ts +13 -0
  641. package/vendor/ink/build/screen-reader-update.js +38 -0
  642. package/vendor/ink/build/screen-reader-update.js.map +1 -0
  643. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  644. package/vendor/ink/build/squash-text-nodes.js +36 -0
  645. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  646. package/vendor/ink/build/styles.d.ts +240 -0
  647. package/vendor/ink/build/styles.js +232 -0
  648. package/vendor/ink/build/styles.js.map +1 -0
  649. package/vendor/ink/build/utils.d.ts +2 -0
  650. package/vendor/ink/build/utils.js +4 -0
  651. package/vendor/ink/build/utils.js.map +1 -0
  652. package/vendor/ink/build/wrap-text.d.ts +3 -0
  653. package/vendor/ink/build/wrap-text.js +31 -0
  654. package/vendor/ink/build/wrap-text.js.map +1 -0
  655. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  656. package/vendor/ink/build/write-synchronized.js +7 -0
  657. package/vendor/ink/build/write-synchronized.js.map +1 -0
  658. package/vendor/ink/license +10 -0
  659. package/vendor/ink/node_modules/@types/node/LICENSE +21 -0
  660. package/vendor/ink/node_modules/@types/node/README.md +15 -0
  661. package/vendor/ink/node_modules/@types/node/assert/strict.d.ts +105 -0
  662. package/vendor/ink/node_modules/@types/node/assert.d.ts +955 -0
  663. package/vendor/ink/node_modules/@types/node/async_hooks.d.ts +623 -0
  664. package/vendor/ink/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  665. package/vendor/ink/node_modules/@types/node/buffer.d.ts +1810 -0
  666. package/vendor/ink/node_modules/@types/node/child_process.d.ts +1428 -0
  667. package/vendor/ink/node_modules/@types/node/cluster.d.ts +486 -0
  668. package/vendor/ink/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  669. package/vendor/ink/node_modules/@types/node/console.d.ts +151 -0
  670. package/vendor/ink/node_modules/@types/node/constants.d.ts +20 -0
  671. package/vendor/ink/node_modules/@types/node/crypto.d.ts +4065 -0
  672. package/vendor/ink/node_modules/@types/node/dgram.d.ts +564 -0
  673. package/vendor/ink/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
  674. package/vendor/ink/node_modules/@types/node/dns/promises.d.ts +503 -0
  675. package/vendor/ink/node_modules/@types/node/dns.d.ts +922 -0
  676. package/vendor/ink/node_modules/@types/node/domain.d.ts +166 -0
  677. package/vendor/ink/node_modules/@types/node/events.d.ts +1054 -0
  678. package/vendor/ink/node_modules/@types/node/fs/promises.d.ts +1329 -0
  679. package/vendor/ink/node_modules/@types/node/fs.d.ts +4676 -0
  680. package/vendor/ink/node_modules/@types/node/globals.d.ts +150 -0
  681. package/vendor/ink/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  682. package/vendor/ink/node_modules/@types/node/http.d.ts +2167 -0
  683. package/vendor/ink/node_modules/@types/node/http2.d.ts +2480 -0
  684. package/vendor/ink/node_modules/@types/node/https.d.ts +405 -0
  685. package/vendor/ink/node_modules/@types/node/index.d.ts +115 -0
  686. package/vendor/ink/node_modules/@types/node/inspector/promises.d.ts +41 -0
  687. package/vendor/ink/node_modules/@types/node/inspector.d.ts +224 -0
  688. package/vendor/ink/node_modules/@types/node/inspector.generated.d.ts +4226 -0
  689. package/vendor/ink/node_modules/@types/node/module.d.ts +819 -0
  690. package/vendor/ink/node_modules/@types/node/net.d.ts +933 -0
  691. package/vendor/ink/node_modules/@types/node/os.d.ts +507 -0
  692. package/vendor/ink/node_modules/@types/node/package.json +155 -0
  693. package/vendor/ink/node_modules/@types/node/path/posix.d.ts +8 -0
  694. package/vendor/ink/node_modules/@types/node/path/win32.d.ts +8 -0
  695. package/vendor/ink/node_modules/@types/node/path.d.ts +187 -0
  696. package/vendor/ink/node_modules/@types/node/perf_hooks.d.ts +643 -0
  697. package/vendor/ink/node_modules/@types/node/process.d.ts +2156 -0
  698. package/vendor/ink/node_modules/@types/node/punycode.d.ts +117 -0
  699. package/vendor/ink/node_modules/@types/node/querystring.d.ts +152 -0
  700. package/vendor/ink/node_modules/@types/node/quic.d.ts +910 -0
  701. package/vendor/ink/node_modules/@types/node/readline/promises.d.ts +161 -0
  702. package/vendor/ink/node_modules/@types/node/readline.d.ts +541 -0
  703. package/vendor/ink/node_modules/@types/node/repl.d.ts +415 -0
  704. package/vendor/ink/node_modules/@types/node/sea.d.ts +162 -0
  705. package/vendor/ink/node_modules/@types/node/sqlite.d.ts +955 -0
  706. package/vendor/ink/node_modules/@types/node/stream/consumers.d.ts +38 -0
  707. package/vendor/ink/node_modules/@types/node/stream/promises.d.ts +211 -0
  708. package/vendor/ink/node_modules/@types/node/stream/web.d.ts +296 -0
  709. package/vendor/ink/node_modules/@types/node/stream.d.ts +1760 -0
  710. package/vendor/ink/node_modules/@types/node/string_decoder.d.ts +67 -0
  711. package/vendor/ink/node_modules/@types/node/test/reporters.d.ts +96 -0
  712. package/vendor/ink/node_modules/@types/node/test.d.ts +2240 -0
  713. package/vendor/ink/node_modules/@types/node/timers/promises.d.ts +108 -0
  714. package/vendor/ink/node_modules/@types/node/timers.d.ts +159 -0
  715. package/vendor/ink/node_modules/@types/node/tls.d.ts +1198 -0
  716. package/vendor/ink/node_modules/@types/node/trace_events.d.ts +197 -0
  717. package/vendor/ink/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  718. package/vendor/ink/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  719. package/vendor/ink/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  720. package/vendor/ink/node_modules/@types/node/ts5.6/index.d.ts +117 -0
  721. package/vendor/ink/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  722. package/vendor/ink/node_modules/@types/node/ts5.7/index.d.ts +117 -0
  723. package/vendor/ink/node_modules/@types/node/tty.d.ts +250 -0
  724. package/vendor/ink/node_modules/@types/node/url.d.ts +519 -0
  725. package/vendor/ink/node_modules/@types/node/util/types.d.ts +558 -0
  726. package/vendor/ink/node_modules/@types/node/util.d.ts +1662 -0
  727. package/vendor/ink/node_modules/@types/node/v8.d.ts +983 -0
  728. package/vendor/ink/node_modules/@types/node/vm.d.ts +1208 -0
  729. package/vendor/ink/node_modules/@types/node/wasi.d.ts +202 -0
  730. package/vendor/ink/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  731. package/vendor/ink/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  732. package/vendor/ink/node_modules/@types/node/web-globals/console.d.ts +9 -0
  733. package/vendor/ink/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  734. package/vendor/ink/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  735. package/vendor/ink/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  736. package/vendor/ink/node_modules/@types/node/web-globals/events.d.ts +106 -0
  737. package/vendor/ink/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
  738. package/vendor/ink/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  739. package/vendor/ink/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  740. package/vendor/ink/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  741. package/vendor/ink/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  742. package/vendor/ink/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  743. package/vendor/ink/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  744. package/vendor/ink/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  745. package/vendor/ink/node_modules/@types/node/web-globals/url.d.ts +24 -0
  746. package/vendor/ink/node_modules/@types/node/worker_threads.d.ts +717 -0
  747. package/vendor/ink/node_modules/@types/node/zlib.d.ts +618 -0
  748. package/vendor/ink/node_modules/node-pty/LICENSE +69 -0
  749. package/vendor/ink/node_modules/node-pty/README.md +164 -0
  750. package/vendor/ink/node_modules/node-pty/binding.gyp +150 -0
  751. package/vendor/ink/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
  752. package/vendor/ink/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  753. package/vendor/ink/node_modules/node-pty/lib/index.js +52 -0
  754. package/vendor/ink/node_modules/node-pty/lib/interfaces.js +7 -0
  755. package/vendor/ink/node_modules/node-pty/lib/shared/conout.js +11 -0
  756. package/vendor/ink/node_modules/node-pty/lib/terminal.js +190 -0
  757. package/vendor/ink/node_modules/node-pty/lib/types.js +7 -0
  758. package/vendor/ink/node_modules/node-pty/lib/unixTerminal.js +349 -0
  759. package/vendor/ink/node_modules/node-pty/lib/utils.js +39 -0
  760. package/vendor/ink/node_modules/node-pty/lib/windowsConoutConnection.js +125 -0
  761. package/vendor/ink/node_modules/node-pty/lib/windowsPtyAgent.js +287 -0
  762. package/vendor/ink/node_modules/node-pty/lib/windowsTerminal.js +201 -0
  763. package/vendor/ink/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  764. package/vendor/ink/node_modules/node-pty/package.json +65 -0
  765. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  766. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  767. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  768. package/vendor/ink/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  769. package/vendor/ink/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
  770. package/vendor/ink/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
  771. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  772. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  773. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  774. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty.pdb +0 -0
  775. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  776. package/vendor/ink/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  777. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  778. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  779. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  780. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty.pdb +0 -0
  781. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  782. package/vendor/ink/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  783. package/vendor/ink/node_modules/node-pty/scripts/post-install.js +76 -0
  784. package/vendor/ink/node_modules/node-pty/scripts/prebuild.js +34 -0
  785. package/vendor/ink/node_modules/node-pty/src/unix/pty.cc +875 -0
  786. package/vendor/ink/node_modules/node-pty/src/unix/spawn-helper.cc +23 -0
  787. package/vendor/ink/node_modules/node-pty/src/win/conpty.cc +582 -0
  788. package/vendor/ink/node_modules/node-pty/src/win/conpty.h +41 -0
  789. package/vendor/ink/node_modules/node-pty/src/win/conpty_console_list.cc +44 -0
  790. package/vendor/ink/node_modules/node-pty/src/win/path_util.cc +95 -0
  791. package/vendor/ink/node_modules/node-pty/src/win/path_util.h +26 -0
  792. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  793. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  794. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  795. package/vendor/ink/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  796. package/vendor/ink/node_modules/node-pty/typings/node-pty.d.ts +215 -0
  797. package/vendor/ink/node_modules/undici-types/LICENSE +21 -0
  798. package/vendor/ink/node_modules/undici-types/README.md +6 -0
  799. package/vendor/ink/node_modules/undici-types/agent.d.ts +32 -0
  800. package/vendor/ink/node_modules/undici-types/api.d.ts +43 -0
  801. package/vendor/ink/node_modules/undici-types/balanced-pool.d.ts +30 -0
  802. package/vendor/ink/node_modules/undici-types/cache-interceptor.d.ts +173 -0
  803. package/vendor/ink/node_modules/undici-types/cache.d.ts +36 -0
  804. package/vendor/ink/node_modules/undici-types/client-stats.d.ts +15 -0
  805. package/vendor/ink/node_modules/undici-types/client.d.ts +108 -0
  806. package/vendor/ink/node_modules/undici-types/connector.d.ts +34 -0
  807. package/vendor/ink/node_modules/undici-types/content-type.d.ts +21 -0
  808. package/vendor/ink/node_modules/undici-types/cookies.d.ts +30 -0
  809. package/vendor/ink/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  810. package/vendor/ink/node_modules/undici-types/dispatcher.d.ts +276 -0
  811. package/vendor/ink/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  812. package/vendor/ink/node_modules/undici-types/errors.d.ts +161 -0
  813. package/vendor/ink/node_modules/undici-types/eventsource.d.ts +66 -0
  814. package/vendor/ink/node_modules/undici-types/fetch.d.ts +211 -0
  815. package/vendor/ink/node_modules/undici-types/formdata.d.ts +108 -0
  816. package/vendor/ink/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  817. package/vendor/ink/node_modules/undici-types/global-origin.d.ts +7 -0
  818. package/vendor/ink/node_modules/undici-types/h2c-client.d.ts +73 -0
  819. package/vendor/ink/node_modules/undici-types/handlers.d.ts +15 -0
  820. package/vendor/ink/node_modules/undici-types/header.d.ts +160 -0
  821. package/vendor/ink/node_modules/undici-types/index.d.ts +88 -0
  822. package/vendor/ink/node_modules/undici-types/interceptors.d.ts +73 -0
  823. package/vendor/ink/node_modules/undici-types/mock-agent.d.ts +68 -0
  824. package/vendor/ink/node_modules/undici-types/mock-call-history.d.ts +111 -0
  825. package/vendor/ink/node_modules/undici-types/mock-client.d.ts +27 -0
  826. package/vendor/ink/node_modules/undici-types/mock-errors.d.ts +12 -0
  827. package/vendor/ink/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  828. package/vendor/ink/node_modules/undici-types/mock-pool.d.ts +27 -0
  829. package/vendor/ink/node_modules/undici-types/package.json +55 -0
  830. package/vendor/ink/node_modules/undici-types/patch.d.ts +29 -0
  831. package/vendor/ink/node_modules/undici-types/pool-stats.d.ts +19 -0
  832. package/vendor/ink/node_modules/undici-types/pool.d.ts +41 -0
  833. package/vendor/ink/node_modules/undici-types/proxy-agent.d.ts +29 -0
  834. package/vendor/ink/node_modules/undici-types/readable.d.ts +68 -0
  835. package/vendor/ink/node_modules/undici-types/retry-agent.d.ts +8 -0
  836. package/vendor/ink/node_modules/undici-types/retry-handler.d.ts +125 -0
  837. package/vendor/ink/node_modules/undici-types/round-robin-pool.d.ts +41 -0
  838. package/vendor/ink/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  839. package/vendor/ink/node_modules/undici-types/util.d.ts +18 -0
  840. package/vendor/ink/node_modules/undici-types/utility.d.ts +7 -0
  841. package/vendor/ink/node_modules/undici-types/webidl.d.ts +341 -0
  842. package/vendor/ink/node_modules/undici-types/websocket.d.ts +186 -0
  843. package/vendor/ink/package.json +201 -0
  844. package/vendor/ink/readme.md +2636 -0
  845. package/bin/swag-agent.js +0 -9
  846. package/dist/server/lib/pg-rate-limiter.d.ts +0 -21
  847. package/dist/server/lib/pg-rate-limiter.js +0 -86
@@ -16,14 +16,15 @@
16
16
  * - session-persistence.ts (SessionMeta, save/load/list/find sessions)
17
17
  * - system-prompt.ts (buildSystemPrompt)
18
18
  */
19
- import { LOCAL_TOOL_DEFINITIONS, executeLocalTool, isLocalTool, } from "./local-tools.js";
20
- import { INTERACTIVE_TOOL_DEFINITIONS, executeInteractiveTool, waitForPlanApproval, } from "./interactive-tools.js";
19
+
20
+ import { LOCAL_TOOL_DEFINITIONS, executeLocalTool, isLocalTool } from "./local-tools.js";
21
+ import { INTERACTIVE_TOOL_DEFINITIONS, executeInteractiveTool, waitForPlanApproval } from "./interactive-tools.js";
21
22
  import { loadConfig, resolveConfig, getProxyUrl } from "./config-store.js";
22
23
  import { getValidToken, refreshSession } from "./auth-service.js";
23
- import { isServerTool, loadServerToolDefinitions, executeServerTool, getServerStatus, setServerToolContext, } from "./server-tools.js";
24
- import { nextTurn, createTurnContext, logSpan, generateSpanId, flushCliSpans, } from "./telemetry.js";
24
+ import { isServerTool, loadServerToolDefinitions, executeServerTool, getServerStatus, setServerToolContext } from "./server-tools.js";
25
+ import { nextTurn, createTurnContext, logSpan, generateSpanId, flushCliSpans } from "./telemetry.js";
25
26
  import { captureError, addBreadcrumb } from "./error-logger.js";
26
- import { setGlobalEmitter, clearGlobalEmitter, } from "./agent-events.js";
27
+ import { setGlobalEmitter, clearGlobalEmitter } from "./agent-events.js";
27
28
  import { mcpClientManager } from "./mcp-client.js";
28
29
  import { loadHooks, runBeforeToolHook, runAfterToolHook, runSessionHook } from "./hooks.js";
29
30
  import { LoopDetector, COMPACTION_TOTAL_BUDGET, getCompactionConfig, getContextManagement, DEFAULT_SESSION_COST_BUDGET_USD, emitCostWarningIfNeeded, resolveAgentLoopConfig, AGENT_DEFAULTS } from "../../shared/agent-core.js";
@@ -41,39 +42,60 @@ import { setPermissionMode, getPermissionMode, isToolAllowedByPermission } from
41
42
  import { setModel, setModelById, getModel, getModelShortName, estimateCostUsd } from "./model-manager.js";
42
43
  import { saveSession, loadSession, listSessions, findLatestSessionForCwd } from "./session-persistence.js";
43
44
  import { buildSystemPrompt } from "./system-prompt.js";
45
+
44
46
  // ============================================================================
45
47
  // RE-EXPORTS — all consumers keep importing from agent-loop.ts
46
48
  // ============================================================================
49
+
47
50
  // Memory
48
51
  export { loadMemory, addMemory, removeMemory, listMemories };
52
+
49
53
  // Git context
50
54
  export { refreshGitContext };
55
+
51
56
  // CLAUDE.md
52
57
  export { loadClaudeMd, reloadClaudeMd };
58
+
53
59
  // Permission modes
54
60
  export { setPermissionMode, getPermissionMode, isToolAllowedByPermission };
61
+
55
62
  // Model management
56
63
  export { setModel, getModel, getModelShortName, estimateCostUsd };
64
+
57
65
  // Session persistence
58
66
  export { saveSession, loadSession, listSessions, findLatestSessionForCwd };
67
+
59
68
  // Server status (pass-through)
60
69
  export { getServerStatus };
70
+
61
71
  // MCP client manager
62
72
  export { mcpClientManager };
73
+
63
74
  // Re-export background process listing for /tasks command
64
75
  export { listProcesses, listBackgroundAgents } from "./background-processes.js";
76
+
65
77
  // Re-export event emitter for ChatApp
66
78
  export { AgentEventEmitter } from "./agent-events.js";
79
+
80
+ // ============================================================================
81
+ // TYPES
82
+ // ============================================================================
83
+
67
84
  // ============================================================================
68
85
  // SESSION STATE
69
86
  // ============================================================================
87
+
70
88
  // CLI-only: Session-wide token tracking (actual counts from API responses).
71
89
  // Reset via resetSessionState() when starting a new conversation.
72
90
  let sessionInputTokens = 0;
73
91
  let sessionOutputTokens = 0;
74
92
  export function getSessionTokens() {
75
- return { input: sessionInputTokens, output: sessionOutputTokens };
93
+ return {
94
+ input: sessionInputTokens,
95
+ output: sessionOutputTokens
96
+ };
76
97
  }
98
+
77
99
  /**
78
100
  * Reset all CLI-only session state. Call when starting a new conversation
79
101
  * (e.g., /clear command, new print-mode run) to prevent stale token counts,
@@ -83,758 +105,1024 @@ export function getSessionTokens() {
83
105
  * that persist intentionally until explicitly changed.
84
106
  */
85
107
  export function resetSessionState() {
86
- sessionInputTokens = 0;
87
- sessionOutputTokens = 0;
88
- sessionLoopDetector = null;
89
- resetGitContext();
90
- resetClaudeMdCache();
91
- clearReadCache();
92
- resetAgentConfigCache();
108
+ sessionInputTokens = 0;
109
+ sessionOutputTokens = 0;
110
+ sessionLoopDetector = null;
111
+ resetGitContext();
112
+ resetClaudeMdCache();
113
+ clearReadCache();
114
+ resetAgentConfigCache();
93
115
  }
116
+
94
117
  /** CLI-only: loop detector — persists session error state across turns (reset by resetSessionState) */
95
118
  let sessionLoopDetector = null;
96
- // CLI max turns — high ceiling since user controls via Ctrl+C.
97
- // DB agent config maxTurns is used when available, this is the absolute safety cap.
98
- const CLI_MAX_TURNS_CAP = 200;
119
+
120
+ // No artificial turn cap agent self-regulates via end_turn (Claude Code parity).
121
+ // Safety: LoopDetector catches loops, cost budget catches runaway spending, user has Ctrl+C.
122
+ const CLI_NO_LIMIT = Number.MAX_SAFE_INTEGER;
123
+
99
124
  // ============================================================================
100
125
  // SHELL OUTPUT SUMMARIZATION
101
126
  // ============================================================================
127
+
102
128
  const SHELL_SUMMARIZE_LINE_THRESHOLD = 800;
103
129
  const SHELL_SUMMARIZE_SIZE_THRESHOLD = 200_000; // 200KB — only summarize truly huge outputs
104
130
  const SHELL_SUMMARIZE_MAX_INPUT = 300_000; // 300KB max to summarizer
105
131
  const SHELL_SUMMARIZE_ORIGINAL_PREVIEW_LINES = 20;
132
+
106
133
  /**
107
134
  * Check if shell output should be summarized based on line count or size.
108
135
  */
109
136
  function shouldSummarizeShellOutput(output) {
110
- if (output.length > SHELL_SUMMARIZE_SIZE_THRESHOLD)
111
- return true;
112
- const lineCount = output.split("\n").length;
113
- return lineCount > SHELL_SUMMARIZE_LINE_THRESHOLD;
137
+ if (output.length > SHELL_SUMMARIZE_SIZE_THRESHOLD) return true;
138
+ const lineCount = output.split("\n").length;
139
+ return lineCount > SHELL_SUMMARIZE_LINE_THRESHOLD;
114
140
  }
141
+
115
142
  /**
116
143
  * Summarize long shell output using Haiku via server proxy.
117
144
  * Returns summarized output or original if summarization fails.
118
145
  */
119
146
  async function summarizeShellOutput(output, proxyUrl, token, storeId) {
120
- const lineCount = output.split("\n").length;
121
- const truncatedForSummary = output.length > SHELL_SUMMARIZE_MAX_INPUT
122
- ? output.slice(0, SHELL_SUMMARIZE_MAX_INPUT) + "\n... (truncated for summarization)"
123
- : output;
124
- try {
125
- const summaryConfig = buildAPIRequest({
126
- model: MODELS.HAIKU,
127
- contextProfile: "subagent",
128
- });
129
- const stream = await callServerProxy({
130
- proxyUrl,
131
- token,
132
- model: MODELS.HAIKU,
133
- system: [{ type: "text", text: "You are a concise technical summarizer. Summarize shell/command output preserving key information, errors, warnings, file paths, and actionable items. Be brief but thorough." }],
134
- messages: [{ role: "user", content: `Summarize this shell output concisely, preserving key information, errors, and actionable items:\n\n${truncatedForSummary}` }],
135
- tools: [],
136
- apiConfig: summaryConfig,
137
- storeId,
138
- timeoutMs: 15_000,
139
- });
140
- const result = await collectStreamResult(parseSSEStream(stream));
141
- const summary = result.text.trim();
142
- if (!summary)
143
- return output; // Summarization failed, return original
144
- // Build first N lines preview
145
- const originalLines = output.split("\n");
146
- const preview = originalLines.slice(0, SHELL_SUMMARIZE_ORIGINAL_PREVIEW_LINES).join("\n");
147
- return `[Summarized from ${lineCount} lines]\n\n${summary}\n\n[First ${SHELL_SUMMARIZE_ORIGINAL_PREVIEW_LINES} lines of original output]\n${preview}`;
148
- }
149
- catch {
150
- // Summarization failed silently — return original output
151
- return output;
152
- }
147
+ const lineCount = output.split("\n").length;
148
+ const truncatedForSummary = output.length > SHELL_SUMMARIZE_MAX_INPUT ? output.slice(0, SHELL_SUMMARIZE_MAX_INPUT) + "\n... (truncated for summarization)" : output;
149
+ try {
150
+ const summaryConfig = buildAPIRequest({
151
+ model: MODELS.HAIKU,
152
+ contextProfile: "subagent"
153
+ });
154
+ const stream = await callServerProxy({
155
+ proxyUrl,
156
+ token,
157
+ model: MODELS.HAIKU,
158
+ system: [{
159
+ type: "text",
160
+ text: "You are a concise technical summarizer. Summarize shell/command output preserving key information, errors, warnings, file paths, and actionable items. Be brief but thorough."
161
+ }],
162
+ messages: [{
163
+ role: "user",
164
+ content: `Summarize this shell output concisely, preserving key information, errors, and actionable items:\n\n${truncatedForSummary}`
165
+ }],
166
+ tools: [],
167
+ apiConfig: summaryConfig,
168
+ storeId,
169
+ timeoutMs: 15_000
170
+ });
171
+ const result = await collectStreamResult(parseSSEStream(stream));
172
+ const summary = result.text.trim();
173
+ if (!summary) return output; // Summarization failed, return original
174
+
175
+ // Build first N lines preview
176
+ const originalLines = output.split("\n");
177
+ const preview = originalLines.slice(0, SHELL_SUMMARIZE_ORIGINAL_PREVIEW_LINES).join("\n");
178
+ return `[Summarized from ${lineCount} lines]\n\n${summary}\n\n[First ${SHELL_SUMMARIZE_ORIGINAL_PREVIEW_LINES} lines of original output]\n${preview}`;
179
+ } catch {
180
+ // Summarization failed silently — return original output
181
+ return output;
182
+ }
153
183
  }
184
+
154
185
  /**
155
186
  * Post-process tool results to summarize long bash output.
156
187
  * Only affects bash tool results that exceed size/line thresholds.
157
188
  */
158
189
  async function summarizeLongToolResults(toolResults, toolNames, proxyUrl, token, shellSummarization, storeId) {
159
- if (!shellSummarization)
160
- return toolResults;
161
- const tasks = toolResults.map(async (result) => {
162
- // Only summarize bash tool string results
163
- const toolName = toolNames.get(result.tool_use_id);
164
- if (toolName !== "bash" || typeof result.content !== "string")
165
- return result;
166
- // Check thresholds
167
- if (!shouldSummarizeShellOutput(result.content))
168
- return result;
169
- const summarized = await summarizeShellOutput(result.content, proxyUrl, token, storeId);
170
- return { ...result, content: summarized };
171
- });
172
- return Promise.all(tasks);
190
+ if (!shellSummarization) return toolResults;
191
+ const tasks = toolResults.map(async result => {
192
+ // Only summarize bash tool string results
193
+ const toolName = toolNames.get(result.tool_use_id);
194
+ if (toolName !== "bash" || typeof result.content !== "string") return result;
195
+
196
+ // Check thresholds
197
+ if (!shouldSummarizeShellOutput(result.content)) return result;
198
+ const summarized = await summarizeShellOutput(result.content, proxyUrl, token, storeId);
199
+ return {
200
+ ...result,
201
+ content: summarized
202
+ };
203
+ });
204
+ return Promise.all(tasks);
173
205
  }
206
+
174
207
  // ============================================================================
175
208
  // TOOL DEFINITIONS
176
209
  // ============================================================================
210
+
177
211
  async function getTools(allowedTools, disallowedTools) {
178
- const localTools = LOCAL_TOOL_DEFINITIONS.map((t) => ({
179
- name: t.name,
180
- description: t.description,
181
- input_schema: t.input_schema,
182
- }));
183
- // Add interactive tools (ask_user_question, enter_plan_mode, exit_plan_mode)
184
- const interactiveTools = INTERACTIVE_TOOL_DEFINITIONS.map((t) => ({
185
- name: t.name,
186
- description: t.description,
187
- input_schema: t.input_schema,
188
- }));
189
- localTools.push(...interactiveTools);
190
- let serverTools = [];
191
- try {
192
- serverTools = await loadServerToolDefinitions();
193
- }
194
- catch {
195
- // Server tools silently unavailable
196
- }
197
- // Deduplicate: local tools take priority over server tools with the same name
198
- const localNames = new Set(localTools.map(t => t.name));
199
- const uniqueServerTools = serverTools.filter(t => !localNames.has(t.name));
200
- // MCP tools from external servers
201
- const mcpTools = mcpClientManager.getTools();
202
- let allTools = [...localTools, ...uniqueServerTools, ...mcpTools];
203
- // Apply tool filtering
204
- if (allowedTools && allowedTools.length > 0) {
205
- const allowed = new Set(allowedTools);
206
- allTools = allTools.filter(t => allowed.has(t.name));
207
- }
208
- if (disallowedTools && disallowedTools.length > 0) {
209
- const disallowed = new Set(disallowedTools);
210
- allTools = allTools.filter(t => !disallowed.has(t.name));
211
- }
212
- return {
213
- tools: allTools,
214
- serverToolCount: uniqueServerTools.length,
215
- };
212
+ const localTools = LOCAL_TOOL_DEFINITIONS.map(t => ({
213
+ name: t.name,
214
+ description: t.description,
215
+ input_schema: t.input_schema
216
+ }));
217
+
218
+ // Add interactive tools (ask_user_question, enter_plan_mode, exit_plan_mode)
219
+ const interactiveTools = INTERACTIVE_TOOL_DEFINITIONS.map(t => ({
220
+ name: t.name,
221
+ description: t.description,
222
+ input_schema: t.input_schema
223
+ }));
224
+ localTools.push(...interactiveTools);
225
+ let serverTools = [];
226
+ try {
227
+ serverTools = await loadServerToolDefinitions();
228
+ } catch {
229
+ // Server tools silently unavailable
230
+ }
231
+
232
+ // Deduplicate: local tools take priority over server tools with the same name
233
+ const localNames = new Set(localTools.map(t => t.name));
234
+ const uniqueServerTools = serverTools.filter(t => !localNames.has(t.name));
235
+
236
+ // MCP tools from external servers
237
+ const mcpTools = mcpClientManager.getTools();
238
+ let allTools = [...localTools, ...uniqueServerTools, ...mcpTools];
239
+
240
+ // Apply tool filtering
241
+ if (allowedTools && allowedTools.length > 0) {
242
+ const allowed = new Set(allowedTools);
243
+ allTools = allTools.filter(t => allowed.has(t.name));
244
+ }
245
+ if (disallowedTools && disallowedTools.length > 0) {
246
+ const disallowed = new Set(disallowedTools);
247
+ allTools = allTools.filter(t => !disallowed.has(t.name));
248
+ }
249
+ return {
250
+ tools: allTools,
251
+ serverToolCount: uniqueServerTools.length
252
+ };
216
253
  }
254
+
217
255
  /** Exposed for /status command */
218
256
  export async function getServerToolCount() {
219
- try {
220
- const defs = await loadServerToolDefinitions();
221
- return defs.length;
222
- }
223
- catch {
224
- return 0;
225
- }
257
+ try {
258
+ const defs = await loadServerToolDefinitions();
259
+ return defs.length;
260
+ } catch {
261
+ return 0;
262
+ }
226
263
  }
264
+
227
265
  // ============================================================================
228
266
  // MAIN LOOP
229
267
  // ============================================================================
268
+
230
269
  export async function runAgentLoop(opts) {
231
- const { message, conversationHistory, callbacks, abortSignal, emitter } = opts;
232
- if (opts.model)
233
- setModel(opts.model);
234
- // Set global emitter for subagents to use
235
- if (emitter) {
236
- setGlobalEmitter(emitter);
237
- }
238
- // effectiveMaxTurns resolved after DB config fetch below
239
- let effectiveMaxTurns = opts.maxTurns || CLI_MAX_TURNS_CAP;
240
- // Load hooks from project and user config
241
- const hooksCwd = opts.cwd || process.cwd();
242
- const hooks = loadHooks(hooksCwd);
243
- // Fire SessionStart hook (non-blocking)
244
- if (hooks.length > 0) {
245
- runSessionHook(hooks, "SessionStart", { session_id: `turn-${Date.now()}` }).catch(() => { });
246
- }
247
- // Shell summarization config (default: true)
248
- const shellSummarization = opts.shellSummarization !== false;
249
- const { tools, serverToolCount } = await getTools(opts.allowedTools, opts.disallowedTools);
250
- const systemPrompt = await buildSystemPrompt(serverToolCount > 0, opts.effort);
251
- // Build user content — text-only string or content blocks array with images
252
- let userContent;
253
- if (opts.images && opts.images.length > 0) {
254
- const blocks = [];
255
- for (const img of opts.images) {
256
- blocks.push({
257
- type: "image",
258
- source: {
259
- type: "base64",
260
- media_type: img.mediaType,
261
- data: img.base64,
262
- },
263
- });
270
+ const {
271
+ message,
272
+ conversationHistory,
273
+ callbacks,
274
+ abortSignal,
275
+ emitter
276
+ } = opts;
277
+ if (opts.model) setModel(opts.model);
278
+
279
+ // Set global emitter for subagents to use
280
+ if (emitter) {
281
+ setGlobalEmitter(emitter);
282
+ }
283
+
284
+ // effectiveMaxTurns resolved after DB config fetch below
285
+ // 0 or unset = no limit (Claude Code parity — agent self-regulates via end_turn)
286
+ let effectiveMaxTurns = opts.maxTurns || CLI_NO_LIMIT;
287
+
288
+ // Load hooks from project and user config
289
+ const hooksCwd = opts.cwd || process.cwd();
290
+ const hooks = loadHooks(hooksCwd);
291
+
292
+ // Fire SessionStart hook (non-blocking)
293
+ if (hooks.length > 0) {
294
+ runSessionHook(hooks, "SessionStart", {
295
+ session_id: `turn-${Date.now()}`
296
+ }).catch(() => {});
297
+ }
298
+
299
+ // Shell summarization config (default: true)
300
+ const shellSummarization = opts.shellSummarization !== false;
301
+ const {
302
+ tools,
303
+ serverToolCount
304
+ } = await getTools(opts.allowedTools, opts.disallowedTools);
305
+ const systemPrompt = await buildSystemPrompt(serverToolCount > 0, opts.effort);
306
+
307
+ // Build user content — text-only string or content blocks array with images
308
+ let userContent;
309
+ if (opts.images && opts.images.length > 0) {
310
+ const blocks = [];
311
+ for (const img of opts.images) {
312
+ blocks.push({
313
+ type: "image",
314
+ source: {
315
+ type: "base64",
316
+ media_type: img.mediaType,
317
+ data: img.base64
264
318
  }
265
- blocks.push({ type: "text", text: message || "(see attached images)" });
266
- userContent = blocks;
319
+ });
267
320
  }
268
- else {
269
- userContent = message;
321
+ blocks.push({
322
+ type: "text",
323
+ text: message || "(see attached images)"
324
+ });
325
+ userContent = blocks;
326
+ } else {
327
+ userContent = message;
328
+ }
329
+ const messages = [...conversationHistory, {
330
+ role: "user",
331
+ content: userContent
332
+ }];
333
+ let totalIn = 0;
334
+ let totalOut = 0;
335
+ let totalCacheCreation = 0;
336
+ let totalCacheRead = 0;
337
+ let totalThinking = 0;
338
+ let allAssistantText = [];
339
+ let prevIterationInputTokens = 0; // Tracks actual context size from last API call
340
+
341
+ // Telemetry: one turn per user message (not per API call)
342
+ const sessionStart = Date.now();
343
+ const {
344
+ storeId
345
+ } = resolveConfig();
346
+ const turnNum = nextTurn(); // ONCE per user message
347
+ const turnCtx = createTurnContext({
348
+ model: getModel(),
349
+ turnNumber: turnNum
350
+ });
351
+ addBreadcrumb("agent", `Turn ${turnNum}: ${message.slice(0, 100)}`, "info");
352
+
353
+ // Set server tool context so tool calls carry trace/user identity to Fly.io server
354
+ setServerToolContext({
355
+ traceId: turnCtx.traceId,
356
+ conversationId: turnCtx.conversationId,
357
+ userId: turnCtx.userId,
358
+ userEmail: turnCtx.userEmail,
359
+ source: "whale-code"
360
+ });
361
+ logSpan({
362
+ action: "chat.user_message",
363
+ durationMs: 0,
364
+ context: turnCtx,
365
+ storeId: storeId || undefined,
366
+ details: {
367
+ message: message,
368
+ conversation_history_length: conversationHistory.length
270
369
  }
271
- const messages = [
272
- ...conversationHistory,
273
- { role: "user", content: userContent },
274
- ];
275
- // Session-level loop detector: persists failed strategies across turns.
276
- // Created once per conversation, reset only when user starts a new conversation.
277
- if (!sessionLoopDetector || conversationHistory.length === 0) {
278
- sessionLoopDetector = new LoopDetector();
370
+ });
371
+ let sessionCostUsd = 0;
372
+ let compactionCount = 0;
373
+ let consecutiveCompactions = 0; // Circuit breaker: consecutive compactions without progress
374
+ let maxTokensContinuations = 0; // Cap continuation loops to prevent infinite looping
375
+ const costWarningsEmitted = new Set();
376
+ const activeModel = getModel();
377
+
378
+ // Fetch agent config from DB — single source of truth, nothing hardcoded.
379
+ // Falls back to AGENT_DEFAULTS only if DB is unreachable (never blocks).
380
+ const dbAgent = await loadCLIAgentConfig();
381
+ const resolved = dbAgent ? resolveAgentLoopConfig(dbAgent, "sse") : null;
382
+
383
+ // Session-level loop detector: persists failed strategies across turns.
384
+ // Created once per conversation, reset only when user starts a new conversation.
385
+ // Must be created AFTER config resolution so DB thresholds apply.
386
+ if (!sessionLoopDetector || conversationHistory.length === 0) {
387
+ sessionLoopDetector = resolved ? LoopDetector.fromResolved(resolved) : new LoopDetector();
388
+ }
389
+ const loopDetector = sessionLoopDetector;
390
+ loopDetector.resetTurn();
391
+ const effectiveBudget = opts.maxBudgetUsd ?? resolved?.contextOverrides.session_cost_budget_usd ?? DEFAULT_SESSION_COST_BUDGET_USD;
392
+
393
+ // Compaction settings from DB via getContextManagement — pass DB overrides so
394
+ // the trigger/budget values match what the Anthropic API actually uses.
395
+ const ctxOverrides = resolved?.contextOverrides;
396
+ const ctxMgmt = getContextManagement(activeModel, ctxOverrides);
397
+ const compactEdit = ctxMgmt.config.edits.find(e => e.type === "compact_20260112");
398
+ const effectiveCompactionTrigger = compactEdit?.trigger?.value ?? AGENT_DEFAULTS.compactionTriggerTokens;
399
+ const effectiveCompactionBudget = ctxOverrides?.compaction_total_budget ?? COMPACTION_TOTAL_BUDGET;
400
+
401
+ // Override maxTurns from DB config (if not explicitly set by caller)
402
+ // 0 = no limit (Claude Code parity)
403
+ if (!opts.maxTurns && resolved && resolved.maxTurns > 0) {
404
+ effectiveMaxTurns = resolved.maxTurns;
405
+ }
406
+
407
+ // Max continuations: resolved from DB config → AGENT_DEFAULTS fallback
408
+ const maxContinuations = resolved?.maxContinuations ?? AGENT_DEFAULTS.maxContinuations;
409
+
410
+ // Tool executor — routes to interactive, local, server, or MCP tools.
411
+ // Wraps execution with before/after hooks when hooks are loaded.
412
+ const INTERACTIVE_TOOL_NAMES = new Set(INTERACTIVE_TOOL_DEFINITIONS.map(t => t.name));
413
+ const toolExecutor = async (name, input) => {
414
+ if (!name) {
415
+ return {
416
+ success: false,
417
+ output: "Tool call missing name — skipping."
418
+ };
279
419
  }
280
- const loopDetector = sessionLoopDetector;
281
- loopDetector.resetTurn();
282
- let totalIn = 0;
283
- let totalOut = 0;
284
- let totalCacheCreation = 0;
285
- let totalCacheRead = 0;
286
- let totalThinking = 0;
287
- let allAssistantText = [];
288
- let prevIterationInputTokens = 0; // Tracks actual context size from last API call
289
- // Telemetry: one turn per user message (not per API call)
290
- const sessionStart = Date.now();
291
- const { storeId } = resolveConfig();
292
- const turnNum = nextTurn(); // ONCE per user message
293
- const turnCtx = createTurnContext({ model: getModel(), turnNumber: turnNum });
294
- addBreadcrumb("agent", `Turn ${turnNum}: ${message.slice(0, 100)}`, "info");
295
- // Set server tool context so tool calls carry trace/user identity to Fly.io server
296
- setServerToolContext({
297
- traceId: turnCtx.traceId,
298
- conversationId: turnCtx.conversationId,
299
- userId: turnCtx.userId,
300
- userEmail: turnCtx.userEmail,
301
- source: "whale-code",
302
- });
303
- logSpan({
304
- action: "chat.user_message",
305
- durationMs: 0,
306
- context: turnCtx,
307
- storeId: storeId || undefined,
308
- details: {
309
- message: message,
310
- conversation_history_length: conversationHistory.length,
311
- },
312
- });
313
- let sessionCostUsd = 0;
314
- let compactionCount = 0;
315
- const costWarningsEmitted = new Set();
316
- const activeModel = getModel();
317
- // Fetch agent config from DB — single source of truth, nothing hardcoded.
318
- // Falls back to AGENT_DEFAULTS only if DB is unreachable (never blocks).
319
- const dbAgent = await loadCLIAgentConfig();
320
- const resolved = dbAgent
321
- ? resolveAgentLoopConfig(dbAgent, "sse")
322
- : null;
323
- const effectiveBudget = opts.maxBudgetUsd
324
- ?? resolved?.contextOverrides.session_cost_budget_usd
325
- ?? DEFAULT_SESSION_COST_BUDGET_USD;
326
- // Compaction settings from DB via getContextManagement — pass DB overrides so
327
- // the trigger/budget values match what the Anthropic API actually uses.
328
- const ctxOverrides = resolved?.contextOverrides;
329
- const ctxMgmt = getContextManagement(activeModel, ctxOverrides);
330
- const compactEdit = ctxMgmt.config.edits.find((e) => e.type === "compact_20260112");
331
- const effectiveCompactionTrigger = compactEdit?.trigger?.value ?? AGENT_DEFAULTS.compactionTriggerTokens;
332
- const effectiveCompactionBudget = ctxOverrides?.compaction_total_budget ?? COMPACTION_TOTAL_BUDGET;
333
- // Override maxTurns from DB config (if not explicitly set by caller)
334
- if (!opts.maxTurns && resolved) {
335
- effectiveMaxTurns = resolved.maxTurns;
420
+
421
+ // Permission mode enforcement
422
+ if (!isToolAllowedByPermission(name)) {
423
+ return {
424
+ success: false,
425
+ output: `Tool "${name}" blocked by ${getPermissionMode()} mode. Switch modes with /mode.`
426
+ };
336
427
  }
337
- // Tool executor — routes to interactive, local, server, or MCP tools.
338
- // Wraps execution with before/after hooks when hooks are loaded.
339
- const INTERACTIVE_TOOL_NAMES = new Set(INTERACTIVE_TOOL_DEFINITIONS.map(t => t.name));
340
- const toolExecutor = async (name, input) => {
341
- if (!name) {
342
- return { success: false, output: "Tool call missing name — skipping." };
428
+
429
+ // BeforeTool hook may block or modify input
430
+ let effectiveInput = input;
431
+ if (hooks.length > 0) {
432
+ const hookResult = await runBeforeToolHook(hooks, name, input);
433
+ if (!hookResult.allow) {
434
+ return {
435
+ success: false,
436
+ output: hookResult.message || "Blocked by hook"
437
+ };
438
+ }
439
+ if (hookResult.modifiedInput) {
440
+ effectiveInput = hookResult.modifiedInput;
441
+ }
442
+ }
443
+ let result;
444
+ if (INTERACTIVE_TOOL_NAMES.has(name)) {
445
+ result = await executeInteractiveTool(name, effectiveInput);
446
+
447
+ // For exit_plan_mode: wait for UI approval, then map decision to result
448
+ if (name === "exit_plan_mode" && result.success) {
449
+ const decision = await waitForPlanApproval();
450
+ switch (decision.action) {
451
+ case "execute":
452
+ result = {
453
+ success: true,
454
+ output: `__PLAN_APPROVED_CLEAN__\n${result.output}`
455
+ };
456
+ break;
457
+ case "edit":
458
+ result = {
459
+ success: true,
460
+ output: "Plan returned for revision. Make changes and use ExitPlanMode again when ready."
461
+ };
462
+ break;
463
+ case "feedback":
464
+ result = {
465
+ success: true,
466
+ output: `User feedback on plan:\n\n${decision.feedback || "(no feedback provided)"}\n\nRevise the plan based on this feedback, then use ExitPlanMode again.`
467
+ };
468
+ break;
469
+ case "cancel":
470
+ result = {
471
+ success: true,
472
+ output: "Plan cancelled by user. Do not proceed with implementation."
473
+ };
474
+ break;
343
475
  }
344
- // Permission mode enforcement
345
- if (!isToolAllowedByPermission(name)) {
346
- return { success: false, output: `Tool "${name}" blocked by ${getPermissionMode()} mode. Switch modes with /mode.` };
476
+ }
477
+ } else if (isLocalTool(name)) {
478
+ result = await executeLocalTool(name, effectiveInput, abortSignal);
479
+ } else if (isServerTool(name)) {
480
+ result = await executeServerTool(name, effectiveInput, emitter);
481
+ } else if (mcpClientManager.isMcpTool(name)) {
482
+ result = await mcpClientManager.callTool(name, effectiveInput);
483
+ } else {
484
+ result = {
485
+ success: false,
486
+ output: `Unknown tool: ${name}`
487
+ };
488
+ }
489
+
490
+ // AfterTool hook — may modify output
491
+ if (hooks.length > 0) {
492
+ const afterResult = await runAfterToolHook(hooks, name, result.output, result.success);
493
+ if (afterResult.modifiedOutput !== undefined) {
494
+ result = {
495
+ ...result,
496
+ output: afterResult.modifiedOutput
497
+ };
498
+ }
499
+ }
500
+ return result;
501
+ };
502
+ try {
503
+ for (let iteration = 0; iteration < effectiveMaxTurns; iteration++) {
504
+ if (abortSignal?.aborted) {
505
+ logSpan({
506
+ action: "chat.cancelled",
507
+ durationMs: Date.now() - sessionStart,
508
+ context: turnCtx,
509
+ storeId: storeId || undefined,
510
+ details: {
511
+ iteration,
512
+ reason: "user_abort"
513
+ }
514
+ });
515
+ callbacks.onError("Cancelled", messages);
516
+ return;
517
+ }
518
+
519
+ // Budget enforcement — always enforced (defaults to DEFAULT_SESSION_COST_BUDGET_USD)
520
+ if (sessionCostUsd >= effectiveBudget) {
521
+ logSpan({
522
+ action: "chat.budget_exceeded",
523
+ durationMs: Date.now() - sessionStart,
524
+ context: turnCtx,
525
+ storeId: storeId || undefined,
526
+ severity: "warn",
527
+ details: {
528
+ session_cost_usd: sessionCostUsd,
529
+ max_budget_usd: effectiveBudget,
530
+ iteration
531
+ }
532
+ });
533
+ callbacks.onError(`Budget exceeded: $${sessionCostUsd.toFixed(4)} >= $${effectiveBudget}`, messages);
534
+ return;
535
+ }
536
+ const apiStart = Date.now();
537
+ const apiSpanId = generateSpanId();
538
+ const apiRowId = crypto.randomUUID(); // UUID for this span's row — children reference via parent_id
539
+ const costContext = `Session cost: $${sessionCostUsd.toFixed(2)} | Budget remaining: $${(effectiveBudget - sessionCostUsd).toFixed(2)}`;
540
+
541
+ // Build API request config
542
+ const currentModel = getModel();
543
+ const apiConfig = buildAPIRequest({
544
+ model: currentModel,
545
+ contextProfile: "main",
546
+ thinkingEnabled: opts.thinking,
547
+ contextOverrides: ctxOverrides
548
+ });
549
+
550
+ // Prepare with prompt caching
551
+ let {
552
+ tools: cachedTools,
553
+ messages: cachedMessages
554
+ } = prepareWithCaching(tools, messages);
555
+ const system = buildSystemBlocks(systemPrompt, costContext);
556
+
557
+ // Client-side context trimming for non-Anthropic providers.
558
+ // Uses prevIterationInputTokens (actual context size from last API call) — NOT
559
+ // cumulative sessionInputTokens, which grows quadratically and would trigger too early.
560
+ const provider = getProvider(currentModel);
561
+ if (provider === "gemini" || provider === "openai") {
562
+ const preTrimMessages = cachedMessages;
563
+ if (provider === "gemini") {
564
+ // Emergency fallback only — compaction fires at 700K first; trim at 950K catches failures
565
+ cachedMessages = trimGeminiContext(cachedMessages, prevIterationInputTokens);
566
+ } else {
567
+ // Emergency fallback only — compaction fires at 120K first; trim at 190K catches failures
568
+ // GPT-4o: 128K context → 96K threshold (no compaction headroom)
569
+ const threshold = currentModel === "gpt-4o" ? 96_000 : 190_000;
570
+ cachedMessages = trimOpenAIContext(cachedMessages, prevIterationInputTokens, threshold);
347
571
  }
348
- // BeforeTool hook may block or modify input
349
- let effectiveInput = input;
350
- if (hooks.length > 0) {
351
- const hookResult = await runBeforeToolHook(hooks, name, input);
352
- if (!hookResult.allow) {
353
- return { success: false, output: hookResult.message || "Blocked by hook" };
354
- }
355
- if (hookResult.modifiedInput) {
356
- effectiveInput = hookResult.modifiedInput;
357
- }
572
+ // Notify UI when trimming actually occurred (trim returns same ref if no-op)
573
+ if (cachedMessages !== preTrimMessages) {
574
+ // Count tool results before/after to report meaningful numbers
575
+ const countToolResults = msgs => msgs.reduce((sum, m) => sum + (Array.isArray(m.content) ? m.content.filter(b => b.type === "tool_result" && b.content !== "[trimmed]").length : 0), 0);
576
+ const activeBefore = countToolResults(preTrimMessages);
577
+ const activeAfter = countToolResults(cachedMessages);
578
+ const estimatedSaved = Math.round(prevIterationInputTokens * ((activeBefore - activeAfter) / Math.max(activeBefore, 1)));
579
+ callbacks.onAutoCompact?.(activeBefore, activeAfter, estimatedSaved);
580
+ emitter?.emitCompact(activeBefore, activeAfter, estimatedSaved);
358
581
  }
359
- let result;
360
- if (INTERACTIVE_TOOL_NAMES.has(name)) {
361
- result = await executeInteractiveTool(name, effectiveInput);
362
- // For exit_plan_mode: wait for UI approval, then map decision to result
363
- if (name === "exit_plan_mode" && result.success) {
364
- const decision = await waitForPlanApproval();
365
- switch (decision.action) {
366
- case "execute":
367
- result = { success: true, output: `__PLAN_APPROVED_CLEAN__\n${result.output}` };
368
- break;
369
- case "edit":
370
- result = { success: true, output: "Plan returned for revision. Make changes and use ExitPlanMode again when ready." };
371
- break;
372
- case "feedback":
373
- result = { success: true, output: `User feedback on plan:\n\n${decision.feedback || "(no feedback provided)"}\n\nRevise the plan based on this feedback, then use ExitPlanMode again.` };
374
- break;
375
- case "cancel":
376
- result = { success: true, output: "Plan cancelled by user. Do not proceed with implementation." };
377
- break;
378
- }
582
+ }
583
+
584
+ // Get auth token
585
+ const token = await getValidToken();
586
+ if (!token) {
587
+ throw new Error("Not logged in. Run: whale login");
588
+ }
589
+
590
+ // Call server proxy with built-in retry
591
+ const originalModel = currentModel;
592
+ const stream = await callServerProxy({
593
+ proxyUrl: getProxyUrl(),
594
+ token,
595
+ model: currentModel,
596
+ system,
597
+ messages: cachedMessages,
598
+ tools: cachedTools,
599
+ apiConfig,
600
+ signal: abortSignal,
601
+ fallbackModel: opts.fallbackModel,
602
+ storeId: storeId || undefined,
603
+ onFallback: (from, to) => {
604
+ setModel(to);
605
+ logSpan({
606
+ action: "claude_api_fallback",
607
+ durationMs: 0,
608
+ context: {
609
+ ...turnCtx,
610
+ spanId: apiSpanId
611
+ },
612
+ storeId: storeId || undefined,
613
+ details: {
614
+ from_model: from,
615
+ to_model: to
379
616
  }
617
+ });
618
+ },
619
+ onRetry: (attempt, max, err) => {
620
+ const msg = `\n\x1b[33m\u21BB Retrying (${attempt}/${max})... ${err.slice(0, 80)}\x1b[0m\n`;
621
+ if (emitter) {
622
+ emitter.emitText(msg);
623
+ } else {
624
+ callbacks.onText(msg);
625
+ }
626
+ },
627
+ onTokenRefresh: async () => {
628
+ const result = await refreshSession();
629
+ return result.success ? result.config.access_token : null;
380
630
  }
381
- else if (isLocalTool(name)) {
382
- result = await executeLocalTool(name, effectiveInput);
631
+ });
632
+
633
+ // Process stream events with UI callbacks
634
+ let thinkingChunks = 0;
635
+ const result = await processStreamWithCallbacks(parseSSEStream(stream, abortSignal), {
636
+ onText: text => {
637
+ if (emitter) {
638
+ emitter.emitText(text);
639
+ } else {
640
+ callbacks.onText(text);
641
+ }
642
+ },
643
+ onThinking: () => {
644
+ thinkingChunks++;
645
+ emitter?.emitThinking(thinkingChunks);
646
+ },
647
+ onToolStart: (name, input) => {
648
+ // NOTE: Do NOT call callbacks.onToolStart here — dispatchTools.onStart
649
+ // fires it once per tool at execution time. Calling it here too would
650
+ // send duplicate tool_start events (stale spinners in WhaleChat).
651
+ if (input) {
652
+ // Tool block complete — emit structured start for CLI TUI only
653
+ emitter?.emitToolStart("", name);
654
+ }
383
655
  }
384
- else if (isServerTool(name)) {
385
- result = await executeServerTool(name, effectiveInput, emitter);
656
+ }, abortSignal);
657
+
658
+ // Flush buffered text
659
+ emitter?.flushText();
660
+
661
+ // Restore original model after transient fallback
662
+ if (getModel() !== originalModel && opts.fallbackModel) {
663
+ setModelById(originalModel);
664
+ }
665
+
666
+ // Update session token tracking
667
+ sessionInputTokens += result.usage.inputTokens;
668
+ sessionOutputTokens += result.usage.outputTokens;
669
+ prevIterationInputTokens = result.usage.inputTokens; // Actual context size for next trim check
670
+
671
+ // Emit usage with model + cost context for all providers
672
+ if (emitter && (result.usage.inputTokens > 0 || result.usage.outputTokens > 0)) {
673
+ const iterCost = estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
674
+ emitter.emitUsage(result.usage.inputTokens, result.usage.outputTokens, currentModel, iterCost, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
675
+ }
676
+ totalIn += result.usage.inputTokens;
677
+ totalOut += result.usage.outputTokens;
678
+ totalCacheCreation += result.usage.cacheCreationTokens;
679
+ totalCacheRead += result.usage.cacheReadTokens;
680
+ totalThinking += result.thinkingTokens;
681
+ sessionCostUsd += estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
682
+
683
+ // Graduated cost warnings
684
+ emitCostWarningIfNeeded(sessionCostUsd, effectiveBudget, costWarningsEmitted, text => {
685
+ if (emitter) {
686
+ emitter.emitText(text);
687
+ } else {
688
+ callbacks.onText(text);
386
689
  }
387
- else if (mcpClientManager.isMcpTool(name)) {
388
- result = await mcpClientManager.callTool(name, effectiveInput);
690
+ });
691
+
692
+ // Server-side context management notification
693
+ if (result.contextManagementApplied) {
694
+ callbacks.onAutoCompact?.(messages.length, messages.length, 0);
695
+ emitter?.emitCompact(messages.length, messages.length, 0);
696
+ logSpan({
697
+ action: "chat.api_compaction",
698
+ durationMs: Date.now() - apiStart,
699
+ context: turnCtx,
700
+ storeId: storeId || undefined,
701
+ details: {
702
+ type: "server_side",
703
+ has_compaction_content: result.compactionContent !== null,
704
+ iteration
705
+ }
706
+ });
707
+ }
708
+ if (result.text) allAssistantText.push(result.text);
709
+
710
+ // Telemetry: API call span
711
+ const iterCostUsd = estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
712
+ logSpan({
713
+ action: "claude_api_request",
714
+ durationMs: Date.now() - apiStart,
715
+ context: {
716
+ ...turnCtx,
717
+ spanId: apiSpanId,
718
+ rowId: apiRowId,
719
+ inputTokens: result.usage.inputTokens,
720
+ outputTokens: result.usage.outputTokens,
721
+ totalCost: iterCostUsd,
722
+ model: currentModel
723
+ },
724
+ storeId: storeId || undefined,
725
+ details: {
726
+ "gen_ai.request.model": currentModel,
727
+ "gen_ai.usage.input_tokens": result.usage.inputTokens,
728
+ "gen_ai.usage.output_tokens": result.usage.outputTokens,
729
+ "gen_ai.usage.cache_creation_tokens": result.usage.cacheCreationTokens,
730
+ "gen_ai.usage.cache_read_tokens": result.usage.cacheReadTokens,
731
+ "gen_ai.usage.cost": iterCostUsd,
732
+ stop_reason: result.stopReason === "compaction" ? "compaction" : result.toolUseBlocks.length > 0 ? "tool_use" : "end_turn",
733
+ iteration,
734
+ tool_count: result.toolUseBlocks.length,
735
+ tool_names: result.toolUseBlocks.map(t => t.name)
389
736
  }
390
- else {
391
- result = { success: false, output: `Unknown tool: ${name}` };
737
+ });
738
+
739
+ // Compaction handling — API paused after generating summary.
740
+ // Per Anthropic docs: the compaction block replaces all prior context.
741
+ // Do NOT preserve old messages — they can exceed the trigger threshold
742
+ // and cause an infinite compaction loop. The summary captures all context.
743
+ if (result.stopReason === "compaction" && result.compactionContent) {
744
+ compactionCount++;
745
+ consecutiveCompactions++;
746
+ logSpan({
747
+ action: "chat.compaction_pause",
748
+ durationMs: Date.now() - apiStart,
749
+ context: turnCtx,
750
+ storeId: storeId || undefined,
751
+ details: {
752
+ compaction_count: compactionCount,
753
+ consecutive: consecutiveCompactions,
754
+ messages_before: messages.length
755
+ }
756
+ });
757
+
758
+ // Circuit breaker: if compaction fires repeatedly without progress,
759
+ // the summary + system prompt alone may exceed the trigger. Force wrap-up.
760
+ const MAX_CONSECUTIVE_COMPACTIONS = 3;
761
+ const shouldWrapUp = consecutiveCompactions > MAX_CONSECUTIVE_COMPACTIONS || compactionCount * effectiveCompactionTrigger >= effectiveCompactionBudget;
762
+ const compactedMessages = [{
763
+ role: "assistant",
764
+ content: [{
765
+ type: "compaction",
766
+ content: result.compactionContent
767
+ }]
768
+ }, {
769
+ role: "user",
770
+ content: [{
771
+ type: "text",
772
+ text: shouldWrapUp ? "You have reached the context budget. Please wrap up your current work and provide a final summary of what was accomplished and what remains." : "Continue with your task."
773
+ }]
774
+ }];
775
+ messages.length = 0;
776
+ messages.push(...compactedMessages);
777
+ if (shouldWrapUp) {
778
+ const budgetMsg = consecutiveCompactions > MAX_CONSECUTIVE_COMPACTIONS ? "\n[Compaction loop detected — wrapping up.]" : "\n[Context budget reached — wrapping up.]";
779
+ if (emitter) {
780
+ emitter.emitText(budgetMsg);
781
+ } else {
782
+ callbacks.onText(budgetMsg);
783
+ }
392
784
  }
393
- // AfterTool hook may modify output
394
- if (hooks.length > 0) {
395
- const afterResult = await runAfterToolHook(hooks, name, result.output, result.success);
396
- if (afterResult.modifiedOutput !== undefined) {
397
- result = { ...result, output: afterResult.modifiedOutput };
398
- }
785
+ iteration--; // Don't count compaction as an iteration
786
+ continue;
787
+ }
788
+
789
+ // No tool calls check if we should continue or stop
790
+ if (result.toolUseBlocks.length === 0) {
791
+ // If model hit max_tokens, it was truncated mid-response — continue so it can finish.
792
+ // Cap at MAX_CONTINUATIONS to prevent infinite loops on very large outputs (e.g. HTML reports).
793
+ if (result.stopReason === "max_tokens" && maxTokensContinuations < maxContinuations) {
794
+ maxTokensContinuations++;
795
+ const truncatedText = result.text || "";
796
+ const assistantContent = buildAssistantContent({
797
+ text: truncatedText,
798
+ toolUseBlocks: [],
799
+ thinkingBlocks: result.thinkingBlocks,
800
+ compactionContent: result.compactionContent
801
+ });
802
+ messages.push({
803
+ role: "assistant",
804
+ content: assistantContent
805
+ });
806
+
807
+ // Escalating continuation prompts — if the model keeps hitting max_tokens,
808
+ // it's probably trying to write a large file. Guide it toward smaller chunks.
809
+ const continuationText = maxTokensContinuations === 1 ? "[Your response was truncated due to the output token limit. If you were writing a large file, split it into multiple smaller write_file calls (~200 lines each) or use run_command with a heredoc. Continue concisely.]" : `[Your response was truncated again (attempt ${maxTokensContinuations}/${maxContinuations}). You MUST reduce output size. For large files: use multiple write_file calls of ~100-200 lines each, or write via run_command. Do NOT attempt to write the entire file in one call.]`;
810
+ messages.push({
811
+ role: "user",
812
+ content: [{
813
+ type: "text",
814
+ text: continuationText
815
+ }]
816
+ });
817
+ continue;
399
818
  }
400
- return result;
401
- };
402
- try {
403
- for (let iteration = 0; iteration < effectiveMaxTurns; iteration++) {
404
- if (abortSignal?.aborted) {
405
- logSpan({ action: "chat.cancelled", durationMs: Date.now() - sessionStart, context: turnCtx, storeId: storeId || undefined, details: { iteration, reason: "user_abort" } });
406
- callbacks.onError("Cancelled", messages);
407
- return;
408
- }
409
- // Budget enforcement — always enforced (defaults to DEFAULT_SESSION_COST_BUDGET_USD)
410
- if (sessionCostUsd >= effectiveBudget) {
411
- logSpan({ action: "chat.budget_exceeded", durationMs: Date.now() - sessionStart, context: turnCtx, storeId: storeId || undefined, severity: "warn", details: { session_cost_usd: sessionCostUsd, max_budget_usd: effectiveBudget, iteration } });
412
- callbacks.onError(`Budget exceeded: $${sessionCostUsd.toFixed(4)} >= $${effectiveBudget}`, messages);
413
- return;
414
- }
415
- const apiStart = Date.now();
416
- const apiSpanId = generateSpanId();
417
- const apiRowId = crypto.randomUUID(); // UUID for this span's row — children reference via parent_id
418
- const costContext = `Session cost: $${sessionCostUsd.toFixed(2)} | Budget remaining: $${(effectiveBudget - sessionCostUsd).toFixed(2)}`;
419
- // Build API request config
420
- const currentModel = getModel();
421
- const apiConfig = buildAPIRequest({
422
- model: currentModel,
423
- contextProfile: "main",
424
- thinkingEnabled: opts.thinking,
425
- contextOverrides: ctxOverrides,
426
- });
427
- // Prepare with prompt caching
428
- let { tools: cachedTools, messages: cachedMessages } = prepareWithCaching(tools, messages);
429
- const system = buildSystemBlocks(systemPrompt, costContext);
430
- // Client-side context trimming for non-Anthropic providers.
431
- // Uses prevIterationInputTokens (actual context size from last API call) — NOT
432
- // cumulative sessionInputTokens, which grows quadratically and would trigger too early.
433
- const provider = getProvider(currentModel);
434
- if (provider === "gemini" || provider === "openai") {
435
- const preTrimMessages = cachedMessages;
436
- if (provider === "gemini") {
437
- // Emergency fallback only — compaction fires at 700K first; trim at 950K catches failures
438
- cachedMessages = trimGeminiContext(cachedMessages, prevIterationInputTokens);
439
- }
440
- else {
441
- // Emergency fallback only — compaction fires at 120K first; trim at 190K catches failures
442
- // GPT-4o: 128K context → 96K threshold (no compaction headroom)
443
- const threshold = currentModel === "gpt-4o" ? 96_000 : 190_000;
444
- cachedMessages = trimOpenAIContext(cachedMessages, prevIterationInputTokens, threshold);
445
- }
446
- // Notify UI when trimming actually occurred (trim returns same ref if no-op)
447
- if (cachedMessages !== preTrimMessages) {
448
- // Count tool results before/after to report meaningful numbers
449
- const countToolResults = (msgs) => msgs.reduce((sum, m) => sum + (Array.isArray(m.content)
450
- ? m.content.filter(b => b.type === "tool_result" && b.content !== "[trimmed]").length
451
- : 0), 0);
452
- const activeBefore = countToolResults(preTrimMessages);
453
- const activeAfter = countToolResults(cachedMessages);
454
- const estimatedSaved = Math.round(prevIterationInputTokens * ((activeBefore - activeAfter) / Math.max(activeBefore, 1)));
455
- callbacks.onAutoCompact?.(activeBefore, activeAfter, estimatedSaved);
456
- emitter?.emitCompact(activeBefore, activeAfter, estimatedSaved);
457
- }
458
- }
459
- // Get auth token
460
- const token = await getValidToken();
461
- if (!token) {
462
- throw new Error("Not logged in. Run: whale login");
463
- }
464
- // Call server proxy with built-in retry
465
- const originalModel = currentModel;
466
- const stream = await callServerProxy({
467
- proxyUrl: getProxyUrl(),
468
- token,
469
- model: currentModel,
470
- system,
471
- messages: cachedMessages,
472
- tools: cachedTools,
473
- apiConfig,
474
- signal: abortSignal,
475
- fallbackModel: opts.fallbackModel,
476
- storeId: storeId || undefined,
477
- onFallback: (from, to) => {
478
- setModel(to);
479
- logSpan({ action: "claude_api_fallback", durationMs: 0, context: { ...turnCtx, spanId: apiSpanId }, storeId: storeId || undefined, details: { from_model: from, to_model: to } });
480
- },
481
- onRetry: (attempt, max, err) => {
482
- const msg = `\n\x1b[33m\u21BB Retrying (${attempt}/${max})... ${err.slice(0, 80)}\x1b[0m\n`;
483
- if (emitter) {
484
- emitter.emitText(msg);
485
- }
486
- else {
487
- callbacks.onText(msg);
488
- }
489
- },
490
- onTokenRefresh: async () => {
491
- const result = await refreshSession();
492
- return result.success ? result.config.access_token : null;
493
- },
494
- });
495
- // Process stream events with UI callbacks
496
- let thinkingChunks = 0;
497
- const result = await processStreamWithCallbacks(parseSSEStream(stream, abortSignal), {
498
- onText: (text) => {
499
- if (emitter) {
500
- emitter.emitText(text);
501
- }
502
- else {
503
- callbacks.onText(text);
504
- }
505
- },
506
- onThinking: () => {
507
- thinkingChunks++;
508
- emitter?.emitThinking(thinkingChunks);
509
- },
510
- onToolStart: (name, input) => {
511
- // NOTE: Do NOT call callbacks.onToolStart here — dispatchTools.onStart
512
- // fires it once per tool at execution time. Calling it here too would
513
- // send duplicate tool_start events (stale spinners in WhaleChat).
514
- if (input) {
515
- // Tool block complete — emit structured start for CLI TUI only
516
- emitter?.emitToolStart("", name);
517
- }
518
- },
519
- }, abortSignal);
520
- // Flush buffered text
521
- emitter?.flushText();
522
- // Restore original model after transient fallback
523
- if (getModel() !== originalModel && opts.fallbackModel) {
524
- setModelById(originalModel);
525
- }
526
- // Update session token tracking
527
- sessionInputTokens += result.usage.inputTokens;
528
- sessionOutputTokens += result.usage.outputTokens;
529
- prevIterationInputTokens = result.usage.inputTokens; // Actual context size for next trim check
530
- // Emit usage with model + cost context for all providers
531
- if (emitter && (result.usage.inputTokens > 0 || result.usage.outputTokens > 0)) {
532
- const iterCost = estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
533
- emitter.emitUsage(result.usage.inputTokens, result.usage.outputTokens, currentModel, iterCost, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
534
- }
535
- totalIn += result.usage.inputTokens;
536
- totalOut += result.usage.outputTokens;
537
- totalCacheCreation += result.usage.cacheCreationTokens;
538
- totalCacheRead += result.usage.cacheReadTokens;
539
- totalThinking += result.thinkingTokens;
540
- sessionCostUsd += estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
541
- // Graduated cost warnings
542
- emitCostWarningIfNeeded(sessionCostUsd, effectiveBudget, costWarningsEmitted, (text) => { if (emitter) {
543
- emitter.emitText(text);
544
- }
545
- else {
546
- callbacks.onText(text);
547
- } });
548
- // Server-side context management notification
549
- if (result.contextManagementApplied) {
550
- callbacks.onAutoCompact?.(messages.length, messages.length, 0);
551
- emitter?.emitCompact(messages.length, messages.length, 0);
552
- logSpan({ action: "chat.api_compaction", durationMs: Date.now() - apiStart, context: turnCtx, storeId: storeId || undefined, details: { type: "server_side", has_compaction_content: result.compactionContent !== null, iteration } });
553
- }
554
- if (result.text)
555
- allAssistantText.push(result.text);
556
- // Telemetry: API call span
557
- const iterCostUsd = estimateCostUsd(result.usage.inputTokens, result.usage.outputTokens, currentModel, result.thinkingTokens, result.usage.cacheReadTokens, result.usage.cacheCreationTokens);
558
- logSpan({
559
- action: "claude_api_request",
560
- durationMs: Date.now() - apiStart,
561
- context: { ...turnCtx, spanId: apiSpanId, rowId: apiRowId, inputTokens: result.usage.inputTokens, outputTokens: result.usage.outputTokens, totalCost: iterCostUsd, model: currentModel },
562
- storeId: storeId || undefined,
563
- details: {
564
- "gen_ai.request.model": currentModel,
565
- "gen_ai.usage.input_tokens": result.usage.inputTokens,
566
- "gen_ai.usage.output_tokens": result.usage.outputTokens,
567
- "gen_ai.usage.cache_creation_tokens": result.usage.cacheCreationTokens,
568
- "gen_ai.usage.cache_read_tokens": result.usage.cacheReadTokens,
569
- "gen_ai.usage.cost": iterCostUsd,
570
- stop_reason: result.stopReason === "compaction" ? "compaction" : result.toolUseBlocks.length > 0 ? "tool_use" : "end_turn",
571
- iteration,
572
- tool_count: result.toolUseBlocks.length,
573
- tool_names: result.toolUseBlocks.map(t => t.name),
574
- },
575
- });
576
- // Compaction handling — API paused after generating summary.
577
- // Preserve last 2 messages for continuity, then resume.
578
- if (result.stopReason === "compaction" && result.compactionContent) {
579
- compactionCount++;
580
- logSpan({ action: "chat.compaction_pause", durationMs: Date.now() - apiStart, context: turnCtx, storeId: storeId || undefined, details: { compaction_count: compactionCount, messages_before: messages.length } });
581
- // Budget enforcement
582
- if (compactionCount * effectiveCompactionTrigger >= effectiveCompactionBudget) {
583
- const budgetMsg = "\n[Context budget reached — wrapping up.]";
584
- if (emitter) {
585
- emitter.emitText(budgetMsg);
586
- }
587
- else {
588
- callbacks.onText(budgetMsg);
589
- }
590
- const compactedMessages = [
591
- { role: "assistant", content: [{ type: "compaction", content: result.compactionContent }] },
592
- { role: "user", content: [{ type: "text", text: "You have reached the context budget. Please wrap up your current work and provide a final summary of what was accomplished and what remains." }] },
593
- ];
594
- messages.length = 0;
595
- messages.push(...compactedMessages);
596
- continue;
597
- }
598
- // Normal compaction: preserve last 2 messages for continuity
599
- const preserved = messages.slice(-2);
600
- const compactedMessages = [
601
- { role: "assistant", content: [{ type: "compaction", content: result.compactionContent }] },
602
- ...preserved,
603
- ];
604
- messages.length = 0;
605
- messages.push(...compactedMessages);
606
- iteration--; // Don't count compaction as an iteration
607
- continue;
608
- }
609
- // No tool calls — check if we should continue or stop
610
- if (result.toolUseBlocks.length === 0) {
611
- // If model hit max_tokens, it was truncated mid-response — continue so it can finish
612
- if (result.stopReason === "max_tokens") {
613
- const truncatedText = result.text || "";
614
- const assistantContent = buildAssistantContent({
615
- text: truncatedText,
616
- toolUseBlocks: [],
617
- thinkingBlocks: result.thinkingBlocks,
618
- compactionContent: result.compactionContent,
619
- });
620
- messages.push({ role: "assistant", content: assistantContent });
621
- messages.push({ role: "user", content: [{ type: "text", text: "[Your response was truncated due to length. Please continue where you left off.]" }] });
622
- continue;
623
- }
624
- break;
625
- }
626
- // Execute tools via shared dispatch
627
- const { results: toolResults, bailOut, bailMessage } = await dispatchTools(result.toolUseBlocks, toolExecutor, {
628
- loopDetector,
629
- maxConcurrent: 7,
630
- onStart: (name, input) => {
631
- callbacks.onToolStart(name, input);
632
- },
633
- onResult: (name, success, output, durationMs) => {
634
- callbacks.onToolResult(name, success, output, undefined, durationMs);
635
- logSpan({
636
- action: `tool.${name}`,
637
- durationMs,
638
- context: { ...turnCtx, spanId: generateSpanId(), parentSpanId: apiSpanId, parentId: apiRowId },
639
- storeId: storeId || undefined,
640
- error: success ? undefined : output,
641
- details: {
642
- tool_input: {},
643
- tool_result: truncateResult(output, 2000),
644
- error_type: success ? undefined : classifyToolError(output),
645
- iteration,
646
- },
647
- });
648
- },
649
- signal: abortSignal,
650
- transcribeAudio: storeId
651
- ? async (base64, mediaType) => callTranscribe({
652
- proxyUrl: getProxyUrl(),
653
- token,
654
- storeId: storeId,
655
- audioBase64: base64,
656
- mediaType,
657
- })
658
- : undefined,
659
- });
660
- if (bailOut) {
661
- logSpan({ action: "chat.bail_out", durationMs: Date.now() - sessionStart, context: turnCtx, storeId: storeId || undefined, severity: "warn", details: { ...loopDetector.getSessionStats(), message: bailMessage, iteration } });
662
- }
663
- // Shell output summarization — summarize long bash output to save context window
664
- const toolNameMap = new Map(result.toolUseBlocks.map(t => [t.id, t.name]));
665
- const finalToolResults = await summarizeLongToolResults(toolResults, toolNameMap, getProxyUrl(), token, shellSummarization, storeId || undefined);
666
- // Build assistant content and append to conversation
667
- const assistantContent = buildAssistantContent({
668
- text: result.text,
669
- toolUseBlocks: result.toolUseBlocks,
670
- thinkingBlocks: result.thinkingBlocks,
671
- compactionContent: result.compactionContent,
672
- });
673
- messages.push({ role: "assistant", content: assistantContent });
674
- // Check for __PLAN_APPROVED_CLEAN__ marker — clear context and start fresh with just the plan
675
- const planCleanMarker = "__PLAN_APPROVED_CLEAN__\n";
676
- const hasCleanPlanApproval = finalToolResults.some((tr) => {
677
- const content = typeof tr.content === "string" ? tr.content : "";
678
- return content.startsWith(planCleanMarker);
679
- });
680
- if (hasCleanPlanApproval) {
681
- // Extract plan content from the marker
682
- const planResult = finalToolResults.find((tr) => {
683
- const content = typeof tr.content === "string" ? tr.content : "";
684
- return content.startsWith(planCleanMarker);
685
- });
686
- const planText = planResult.content.slice(planCleanMarker.length);
687
- const beforeCount = messages.length;
688
- messages.length = 0;
689
- messages.push({ role: "user", content: [{ type: "text", text: `Implement this plan:\n\n${planText}` }] });
690
- callbacks.onAutoCompact?.(beforeCount, 1, 0);
691
- continue;
692
- }
693
- messages.push({ role: "user", content: finalToolResults });
694
- // Non-native compaction for OpenAI/Gemini — fires after tool results appended
695
- const compactionCfg = getCompactionConfig(currentModel);
696
- if (!compactionCfg.isNative && result.usage.inputTokens >= compactionCfg.triggerTokens) {
697
- compactionCount++;
698
- if (compactionCount * compactionCfg.triggerTokens >= compactionCfg.totalBudget) {
699
- // Budget exhaustion — force wrap-up (same as native compaction budget logic)
700
- const budgetMsg = "\n[Context budget reached — wrapping up.]";
701
- if (emitter) {
702
- emitter.emitText(budgetMsg);
703
- }
704
- else {
705
- callbacks.onText(budgetMsg);
706
- }
707
- const summary = await requestProviderCompaction({
708
- proxyUrl: getProxyUrl(),
709
- token,
710
- messages: messages,
711
- systemPrompt,
712
- });
713
- const compactedMessages = [
714
- ...(summary
715
- ? [{ role: "assistant", content: [{ type: "compaction", content: summary }] }]
716
- : []),
717
- { role: "user", content: [{ type: "text", text: "You have reached the context budget. Please wrap up your current work and provide a final summary of what was accomplished and what remains." }] },
718
- ];
719
- messages.length = 0;
720
- messages.push(...compactedMessages);
721
- continue;
722
- }
723
- // Normal compaction — summarize and preserve last 2 messages
724
- const summary = await requestProviderCompaction({
725
- proxyUrl: getProxyUrl(),
726
- token,
727
- messages: messages,
728
- systemPrompt,
729
- });
730
- if (summary) {
731
- const preserved = messages.slice(-2);
732
- const compactedMessages = [
733
- { role: "assistant", content: [{ type: "compaction", content: summary }] },
734
- ...preserved,
735
- ];
736
- messages.length = 0;
737
- messages.push(...compactedMessages);
738
- iteration--; // Don't count compaction as an iteration
739
- callbacks.onAutoCompact?.(messages.length + preserved.length, messages.length, Math.round(result.usage.inputTokens * 0.7));
740
- emitter?.emitCompact(messages.length + preserved.length, messages.length, Math.round(result.usage.inputTokens * 0.7));
741
- logSpan({ action: "chat.provider_compaction", durationMs: 0, context: turnCtx, storeId: storeId || undefined, details: { compaction_count: compactionCount, provider, model: currentModel, input_tokens: result.usage.inputTokens, trigger_tokens: compactionCfg.triggerTokens } });
742
- }
819
+ break;
820
+ }
821
+
822
+ // Reset counters model is taking tool actions, making progress
823
+ maxTokensContinuations = 0;
824
+ consecutiveCompactions = 0;
825
+
826
+ // Execute tools via shared dispatch
827
+ const {
828
+ results: toolResults,
829
+ bailOut,
830
+ bailMessage
831
+ } = await dispatchTools(result.toolUseBlocks, toolExecutor, {
832
+ loopDetector,
833
+ maxConcurrent: resolved?.maxConcurrentTools ?? AGENT_DEFAULTS.maxConcurrentTools,
834
+ batchErrorLimit: resolved?.loopBatchErrorLimit ?? AGENT_DEFAULTS.loopBatchErrorLimit,
835
+ onStart: (name, input) => {
836
+ callbacks.onToolStart(name, input);
837
+ },
838
+ onResult: (name, success, output, durationMs) => {
839
+ callbacks.onToolResult(name, success, output, undefined, durationMs);
840
+ logSpan({
841
+ action: `tool.${name}`,
842
+ durationMs,
843
+ context: {
844
+ ...turnCtx,
845
+ spanId: generateSpanId(),
846
+ parentSpanId: apiSpanId,
847
+ parentId: apiRowId
848
+ },
849
+ storeId: storeId || undefined,
850
+ error: success ? undefined : output,
851
+ details: {
852
+ tool_input: {},
853
+ tool_result: truncateResult(output, 2000),
854
+ error_type: success ? undefined : classifyToolError(output),
855
+ iteration
743
856
  }
744
- }
745
- // Telemetry: session summary
857
+ });
858
+ },
859
+ signal: abortSignal,
860
+ transcribeAudio: storeId ? async (base64, mediaType) => callTranscribe({
861
+ proxyUrl: getProxyUrl(),
862
+ token,
863
+ storeId: storeId,
864
+ audioBase64: base64,
865
+ mediaType
866
+ }) : undefined
867
+ });
868
+ if (bailOut) {
746
869
  logSpan({
747
- action: "chat.session_complete",
748
- durationMs: Date.now() - sessionStart,
749
- context: { ...turnCtx, inputTokens: totalIn, outputTokens: totalOut, model: activeModel },
870
+ action: "chat.bail_out",
871
+ durationMs: Date.now() - sessionStart,
872
+ context: turnCtx,
873
+ storeId: storeId || undefined,
874
+ severity: "warn",
875
+ details: {
876
+ ...loopDetector.getSessionStats(),
877
+ message: bailMessage,
878
+ iteration
879
+ }
880
+ });
881
+ }
882
+
883
+ // Shell output summarization — summarize long bash output to save context window
884
+ const toolNameMap = new Map(result.toolUseBlocks.map(t => [t.id, t.name]));
885
+ const finalToolResults = await summarizeLongToolResults(toolResults, toolNameMap, getProxyUrl(), token, shellSummarization, storeId || undefined);
886
+
887
+ // Build assistant content and append to conversation
888
+ const assistantContent = buildAssistantContent({
889
+ text: result.text,
890
+ toolUseBlocks: result.toolUseBlocks,
891
+ thinkingBlocks: result.thinkingBlocks,
892
+ compactionContent: result.compactionContent
893
+ });
894
+ messages.push({
895
+ role: "assistant",
896
+ content: assistantContent
897
+ });
898
+
899
+ // Check for __PLAN_APPROVED_CLEAN__ marker — clear context and start fresh with just the plan
900
+ const planCleanMarker = "__PLAN_APPROVED_CLEAN__\n";
901
+ const hasCleanPlanApproval = finalToolResults.some(tr => {
902
+ const content = typeof tr.content === "string" ? tr.content : "";
903
+ return content.startsWith(planCleanMarker);
904
+ });
905
+ if (hasCleanPlanApproval) {
906
+ // Extract plan content from the marker
907
+ const planResult = finalToolResults.find(tr => {
908
+ const content = typeof tr.content === "string" ? tr.content : "";
909
+ return content.startsWith(planCleanMarker);
910
+ });
911
+ const planText = planResult.content.slice(planCleanMarker.length);
912
+ const beforeCount = messages.length;
913
+ messages.length = 0;
914
+ messages.push({
915
+ role: "user",
916
+ content: [{
917
+ type: "text",
918
+ text: `Implement this plan:\n\n${planText}`
919
+ }]
920
+ });
921
+ callbacks.onAutoCompact?.(beforeCount, 1, 0);
922
+ continue;
923
+ }
924
+ messages.push({
925
+ role: "user",
926
+ content: finalToolResults
927
+ });
928
+
929
+ // Non-native compaction for OpenAI/Gemini — fires after tool results appended
930
+ const compactionCfg = getCompactionConfig(currentModel);
931
+ if (!compactionCfg.isNative && result.usage.inputTokens >= compactionCfg.triggerTokens) {
932
+ compactionCount++;
933
+ if (compactionCount * compactionCfg.triggerTokens >= compactionCfg.totalBudget) {
934
+ // Budget exhaustion — force wrap-up (same as native compaction budget logic)
935
+ const budgetMsg = "\n[Context budget reached — wrapping up.]";
936
+ if (emitter) {
937
+ emitter.emitText(budgetMsg);
938
+ } else {
939
+ callbacks.onText(budgetMsg);
940
+ }
941
+ const summary = await requestProviderCompaction({
942
+ proxyUrl: getProxyUrl(),
943
+ token,
944
+ messages,
945
+ systemPrompt
946
+ });
947
+ const compactedMessages = [...(summary ? [{
948
+ role: "assistant",
949
+ content: [{
950
+ type: "compaction",
951
+ content: summary
952
+ }]
953
+ }] : []), {
954
+ role: "user",
955
+ content: [{
956
+ type: "text",
957
+ text: "You have reached the context budget. Please wrap up your current work and provide a final summary of what was accomplished and what remains."
958
+ }]
959
+ }];
960
+ messages.length = 0;
961
+ messages.push(...compactedMessages);
962
+ continue;
963
+ }
964
+
965
+ // Normal compaction — summarize, don't preserve old messages (prevents loop)
966
+ const summary = await requestProviderCompaction({
967
+ proxyUrl: getProxyUrl(),
968
+ token,
969
+ messages: messages,
970
+ systemPrompt
971
+ });
972
+ if (summary) {
973
+ const beforeCount = messages.length;
974
+ const compactedMessages = [{
975
+ role: "assistant",
976
+ content: [{
977
+ type: "compaction",
978
+ content: summary
979
+ }]
980
+ }, {
981
+ role: "user",
982
+ content: [{
983
+ type: "text",
984
+ text: "Continue with your task."
985
+ }]
986
+ }];
987
+ messages.length = 0;
988
+ messages.push(...compactedMessages);
989
+ iteration--; // Don't count compaction as an iteration
990
+
991
+ callbacks.onAutoCompact?.(beforeCount, messages.length, Math.round(result.usage.inputTokens * 0.7));
992
+ emitter?.emitCompact(beforeCount, messages.length, Math.round(result.usage.inputTokens * 0.7));
993
+ logSpan({
994
+ action: "chat.provider_compaction",
995
+ durationMs: 0,
996
+ context: turnCtx,
750
997
  storeId: storeId || undefined,
751
998
  details: {
752
- input_tokens: totalIn, output_tokens: totalOut, total_tokens: totalIn + totalOut,
753
- cache_creation_tokens: totalCacheCreation, cache_read_tokens: totalCacheRead,
754
- session_input_tokens: sessionInputTokens, session_output_tokens: sessionOutputTokens,
755
- model: activeModel,
756
- },
757
- });
758
- const turnCostUsd = estimateCostUsd(totalIn, totalOut, activeModel, totalThinking, totalCacheRead, totalCacheCreation);
759
- callbacks.onUsage(totalIn, totalOut, totalThinking, activeModel, turnCostUsd, totalCacheRead, totalCacheCreation);
760
- // Flush telemetry spans to server before session ends
761
- flushCliSpans();
762
- // Fire SessionEnd hook (non-blocking)
763
- if (hooks.length > 0) {
764
- runSessionHook(hooks, "SessionEnd", { session_id: `turn-${sessionStart}` }).catch(() => { });
999
+ compaction_count: compactionCount,
1000
+ provider,
1001
+ model: currentModel,
1002
+ input_tokens: result.usage.inputTokens,
1003
+ trigger_tokens: compactionCfg.triggerTokens
1004
+ }
1005
+ });
765
1006
  }
766
- const finalText = allAssistantText.length > 0 ? allAssistantText[allAssistantText.length - 1] : "";
767
- emitter?.emitDone(finalText, messages);
768
- if (emitter)
769
- clearGlobalEmitter();
770
- callbacks.onDone(messages);
1007
+ }
771
1008
  }
772
- catch (err) {
773
- const errorMsg = abortSignal?.aborted || err?.message === "Cancelled"
774
- ? "Cancelled"
775
- : String(err?.message || err);
776
- logSpan({
777
- action: errorMsg === "Cancelled" ? "chat.cancelled" : "chat.fatal_error",
778
- durationMs: Date.now() - sessionStart,
779
- context: { ...turnCtx, inputTokens: totalIn, outputTokens: totalOut, model: activeModel },
780
- storeId: storeId || undefined,
781
- severity: errorMsg === "Cancelled" ? "info" : "error",
782
- error: errorMsg === "Cancelled" ? undefined : errorMsg,
783
- details: { input_tokens: totalIn, output_tokens: totalOut, session_cost_usd: sessionCostUsd, model: activeModel },
784
- });
785
- // Capture to error_events (not just audit_logs) for non-cancellations
786
- if (errorMsg !== "Cancelled") {
787
- captureError({
788
- error: err instanceof Error ? err : undefined,
789
- errorType: "AgentLoopError",
790
- errorMessage: errorMsg,
791
- severity: "error",
792
- traceId: turnCtx.traceId,
793
- spanId: turnCtx.spanId,
794
- storeId: storeId || undefined,
795
- tags: { model: activeModel, turn: String(turnNum) },
796
- });
1009
+
1010
+ // Telemetry: session summary
1011
+ logSpan({
1012
+ action: "chat.session_complete",
1013
+ durationMs: Date.now() - sessionStart,
1014
+ context: {
1015
+ ...turnCtx,
1016
+ inputTokens: totalIn,
1017
+ outputTokens: totalOut,
1018
+ model: activeModel
1019
+ },
1020
+ storeId: storeId || undefined,
1021
+ details: {
1022
+ input_tokens: totalIn,
1023
+ output_tokens: totalOut,
1024
+ total_tokens: totalIn + totalOut,
1025
+ cache_creation_tokens: totalCacheCreation,
1026
+ cache_read_tokens: totalCacheRead,
1027
+ session_input_tokens: sessionInputTokens,
1028
+ session_output_tokens: sessionOutputTokens,
1029
+ model: activeModel
1030
+ }
1031
+ });
1032
+ const turnCostUsd = estimateCostUsd(totalIn, totalOut, activeModel, totalThinking, totalCacheRead, totalCacheCreation);
1033
+ callbacks.onUsage(totalIn, totalOut, totalThinking, activeModel, turnCostUsd, totalCacheRead, totalCacheCreation);
1034
+
1035
+ // Flush telemetry spans to server before session ends
1036
+ flushCliSpans();
1037
+
1038
+ // Fire SessionEnd hook (non-blocking)
1039
+ if (hooks.length > 0) {
1040
+ runSessionHook(hooks, "SessionEnd", {
1041
+ session_id: `turn-${sessionStart}`
1042
+ }).catch(() => {});
1043
+ }
1044
+ const finalText = allAssistantText.length > 0 ? allAssistantText[allAssistantText.length - 1] : "";
1045
+ emitter?.emitDone(finalText, messages);
1046
+ if (emitter) clearGlobalEmitter();
1047
+ callbacks.onDone(messages);
1048
+ } catch (err) {
1049
+ const errorMsg = abortSignal?.aborted || err?.message === "Cancelled" ? "Cancelled" : String(err?.message || err);
1050
+ logSpan({
1051
+ action: errorMsg === "Cancelled" ? "chat.cancelled" : "chat.fatal_error",
1052
+ durationMs: Date.now() - sessionStart,
1053
+ context: {
1054
+ ...turnCtx,
1055
+ inputTokens: totalIn,
1056
+ outputTokens: totalOut,
1057
+ model: activeModel
1058
+ },
1059
+ storeId: storeId || undefined,
1060
+ severity: errorMsg === "Cancelled" ? "info" : "error",
1061
+ error: errorMsg === "Cancelled" ? undefined : errorMsg,
1062
+ details: {
1063
+ input_tokens: totalIn,
1064
+ output_tokens: totalOut,
1065
+ session_cost_usd: sessionCostUsd,
1066
+ model: activeModel
1067
+ }
1068
+ });
1069
+
1070
+ // Capture to error_events (not just audit_logs) for non-cancellations
1071
+ if (errorMsg !== "Cancelled") {
1072
+ captureError({
1073
+ error: err instanceof Error ? err : undefined,
1074
+ errorType: "AgentLoopError",
1075
+ errorMessage: errorMsg,
1076
+ severity: "error",
1077
+ traceId: turnCtx.traceId,
1078
+ spanId: turnCtx.spanId,
1079
+ storeId: storeId || undefined,
1080
+ tags: {
1081
+ model: activeModel,
1082
+ turn: String(turnNum)
797
1083
  }
798
- // Flush telemetry on error path too
799
- flushCliSpans();
800
- emitter?.emitError(errorMsg);
801
- if (emitter)
802
- clearGlobalEmitter();
803
- callbacks.onError(errorMsg, messages);
1084
+ });
804
1085
  }
1086
+
1087
+ // Flush telemetry on error path too
1088
+ flushCliSpans();
1089
+ emitter?.emitError(errorMsg);
1090
+ if (emitter) clearGlobalEmitter();
1091
+ callbacks.onError(errorMsg, messages);
1092
+ }
805
1093
  }
1094
+
806
1095
  // ============================================================================
807
1096
  // TELEMETRY HELPERS
808
1097
  // ============================================================================
1098
+
809
1099
  export function truncateResult(output, maxLen) {
810
- if (output.length <= maxLen)
811
- return output;
812
- return output.slice(0, maxLen) + `... (${output.length} chars total)`;
1100
+ if (output.length <= maxLen) return output;
1101
+ return output.slice(0, maxLen) + `... (${output.length} chars total)`;
813
1102
  }
814
1103
  export function classifyToolError(output) {
815
- const lower = output.toLowerCase();
816
- if (lower.includes("timed out") || lower.includes("timeout"))
817
- return "timeout";
818
- if (lower.includes("permission denied") || lower.includes("eacces"))
819
- return "permission";
820
- if (lower.includes("not found") || lower.includes("no such file"))
821
- return "not_found";
822
- if (lower.includes("command not found") || lower.includes("exit code 127"))
823
- return "command_not_found";
824
- if (lower.includes("import") && lower.includes("error"))
825
- return "import_error";
826
- if (lower.includes("syntax") || lower.includes("parse"))
827
- return "syntax_error";
828
- if (lower.includes("externally-managed"))
829
- return "env_managed";
830
- return "unknown";
1104
+ const lower = output.toLowerCase();
1105
+ if (lower.includes("timed out") || lower.includes("timeout")) return "timeout";
1106
+ if (lower.includes("permission denied") || lower.includes("eacces")) return "permission";
1107
+ if (lower.includes("not found") || lower.includes("no such file")) return "not_found";
1108
+ if (lower.includes("command not found") || lower.includes("exit code 127")) return "command_not_found";
1109
+ if (lower.includes("import") && lower.includes("error")) return "import_error";
1110
+ if (lower.includes("syntax") || lower.includes("parse")) return "syntax_error";
1111
+ if (lower.includes("externally-managed")) return "env_managed";
1112
+ return "unknown";
831
1113
  }
1114
+
832
1115
  // Convenience: check if user can use the agent (logged in OR has API key)
833
1116
  export function canUseAgent() {
834
- const config = loadConfig();
835
- const hasToken = !!(config.access_token && config.refresh_token);
836
- const hasApiKey = !!(process.env.ANTHROPIC_API_KEY || config.anthropic_api_key);
837
- if (hasToken || hasApiKey)
838
- return { ready: true };
839
- return { ready: false, reason: "Run `whale login` to authenticate." };
1117
+ const config = loadConfig();
1118
+ const hasToken = !!(config.access_token && config.refresh_token);
1119
+ const hasApiKey = !!(process.env.ANTHROPIC_API_KEY || config.anthropic_api_key);
1120
+ if (hasToken || hasApiKey) return {
1121
+ ready: true
1122
+ };
1123
+ return {
1124
+ ready: false,
1125
+ reason: "Run `whale login` to authenticate."
1126
+ };
840
1127
  }
1128
+ //# sourceMappingURL=agent-loop.js.map