whale-code 6.5.11 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (613) hide show
  1. package/dist/cli/chat/ChatApp.js +7 -11
  2. package/dist/cli/chat/ChatApp.js.map +1 -1
  3. package/dist/cli/chat/ChatInput.js +7 -3
  4. package/dist/cli/chat/ChatInput.js.map +1 -1
  5. package/dist/cli/chat/MessageList.js +5 -6
  6. package/dist/cli/chat/MessageList.js.map +1 -1
  7. package/dist/cli/chat/StatusBar.d.ts +2 -2
  8. package/dist/cli/chat/StatusBar.js +90 -160
  9. package/dist/cli/chat/StatusBar.js.map +1 -1
  10. package/dist/cli/chat/components/LiveArea.js +78 -115
  11. package/dist/cli/chat/components/LiveArea.js.map +1 -1
  12. package/dist/cli/chat/components/StaticMessages.js +60 -79
  13. package/dist/cli/chat/components/StaticMessages.js.map +1 -1
  14. package/dist/cli/chat/hooks/useAgentLoop.js +45 -37
  15. package/dist/cli/chat/hooks/useAgentLoop.js.map +1 -1
  16. package/dist/cli/chat/store.d.ts +12 -0
  17. package/dist/cli/chat/store.js +19 -0
  18. package/dist/cli/chat/store.js.map +1 -1
  19. package/dist/cli/services/agent-loop-tools.js +10 -1
  20. package/dist/cli/services/agent-loop-tools.js.map +1 -1
  21. package/dist/cli/services/cli-agent-loop.js +3 -2
  22. package/dist/cli/services/cli-agent-loop.js.map +1 -1
  23. package/dist/cli/services/config-store.js +4 -3
  24. package/dist/cli/services/config-store.js.map +1 -1
  25. package/dist/cli/services/memory-manager.js +2 -2
  26. package/dist/cli/services/memory-manager.js.map +1 -1
  27. package/dist/cli/services/permission-modes.js +14 -10
  28. package/dist/cli/services/permission-modes.js.map +1 -1
  29. package/dist/cli/services/session-client.js +2 -1
  30. package/dist/cli/services/session-client.js.map +1 -1
  31. package/dist/cli/services/session-persistence.js +14 -6
  32. package/dist/cli/services/session-persistence.js.map +1 -1
  33. package/dist/cli/shared/SpinnerSlot.js +4 -1
  34. package/dist/cli/shared/SpinnerSlot.js.map +1 -1
  35. package/dist/server/handlers/browser-lifecycle.js +10 -0
  36. package/dist/server/handlers/browser-lifecycle.js.map +1 -1
  37. package/dist/server/handlers/browser.js +16 -1
  38. package/dist/server/handlers/browser.js.map +1 -1
  39. package/dist/server/handlers/campaigns.js +11 -0
  40. package/dist/server/handlers/campaigns.js.map +1 -1
  41. package/dist/server/handlers/catalog-products.js +19 -5
  42. package/dist/server/handlers/catalog-products.js.map +1 -1
  43. package/dist/server/handlers/catalog.js +42 -8
  44. package/dist/server/handlers/catalog.js.map +1 -1
  45. package/dist/server/handlers/clickhouse.js +4 -4
  46. package/dist/server/handlers/clickhouse.js.map +1 -1
  47. package/dist/server/handlers/comms-email.js +70 -8
  48. package/dist/server/handlers/comms-email.js.map +1 -1
  49. package/dist/server/handlers/comms.js +63 -21
  50. package/dist/server/handlers/comms.js.map +1 -1
  51. package/dist/server/handlers/coupons.js +141 -77
  52. package/dist/server/handlers/coupons.js.map +1 -1
  53. package/dist/server/handlers/google-ads.js +280 -8
  54. package/dist/server/handlers/google-ads.js.map +1 -1
  55. package/dist/server/handlers/remove-bg.d.ts +33 -0
  56. package/dist/server/handlers/remove-bg.js +698 -44
  57. package/dist/server/handlers/remove-bg.js.map +1 -1
  58. package/dist/server/handlers/supply-chain.js +93 -1
  59. package/dist/server/handlers/supply-chain.js.map +1 -1
  60. package/dist/server/handlers/workflow-steps-types.d.ts +1 -1
  61. package/dist/server/handlers/workflow-steps-types.js +7 -1
  62. package/dist/server/handlers/workflow-steps-types.js.map +1 -1
  63. package/dist/server/handlers/workflow-steps.js +1 -1
  64. package/dist/server/handlers/workflow-steps.js.map +1 -1
  65. package/dist/server/index.js +122 -29
  66. package/dist/server/index.js.map +1 -1
  67. package/dist/server/lib/agent-loop-turn.js +33 -3
  68. package/dist/server/lib/agent-loop-turn.js.map +1 -1
  69. package/dist/server/lib/agent-loop-types.d.ts +6 -2
  70. package/dist/server/lib/agent-loop-types.js +14 -2
  71. package/dist/server/lib/agent-loop-types.js.map +1 -1
  72. package/dist/server/lib/clickhouse-client.js +4 -2
  73. package/dist/server/lib/clickhouse-client.js.map +1 -1
  74. package/dist/server/lib/code-worker.js +4 -1
  75. package/dist/server/lib/code-worker.js.map +1 -1
  76. package/dist/server/providers/anthropic.js +103 -33
  77. package/dist/server/providers/anthropic.js.map +1 -1
  78. package/dist/server/server-chat.js +2 -2
  79. package/dist/server/server-chat.js.map +1 -1
  80. package/dist/server/server-helpers.d.ts +8 -1
  81. package/dist/server/server-helpers.js +17 -3
  82. package/dist/server/server-helpers.js.map +1 -1
  83. package/dist/server/server-persist.js +34 -21
  84. package/dist/server/server-persist.js.map +1 -1
  85. package/dist/server/server-rate-limit.d.ts +0 -1
  86. package/dist/server/server-rate-limit.js +5 -5
  87. package/dist/server/server-rate-limit.js.map +1 -1
  88. package/dist/server/server-routes-approvals.js +2 -2
  89. package/dist/server/server-routes-approvals.js.map +1 -1
  90. package/dist/server/server-routes-auth.js +2 -2
  91. package/dist/server/server-routes-auth.js.map +1 -1
  92. package/dist/server/server-routes-events.js +2 -2
  93. package/dist/server/server-routes-events.js.map +1 -1
  94. package/dist/server/server-routes-public.js +4 -4
  95. package/dist/server/server-routes-public.js.map +1 -1
  96. package/dist/server/server-routes-webchat.js +3 -3
  97. package/dist/server/server-routes-webchat.js.map +1 -1
  98. package/dist/server/server-store-circuit-breaker.js +1 -1
  99. package/dist/server/server-store-circuit-breaker.js.map +1 -1
  100. package/dist/server/tool-router.js +7 -4
  101. package/dist/server/tool-router.js.map +1 -1
  102. package/dist/server/validation.js +11 -0
  103. package/dist/server/validation.js.map +1 -1
  104. package/dist/shared/api-client.js +38 -11
  105. package/dist/shared/api-client.js.map +1 -1
  106. package/package.json +12 -10
  107. package/vendor/ink/build/ink.js +68 -24
  108. package/vendor/ink/node_modules/react-devtools-core/README.md +152 -0
  109. package/vendor/ink/node_modules/react-devtools-core/backend.js +1 -0
  110. package/vendor/ink/node_modules/react-devtools-core/dist/648.chunk.js +2 -0
  111. package/vendor/ink/node_modules/react-devtools-core/dist/648.chunk.js.map +1 -0
  112. package/vendor/ink/node_modules/react-devtools-core/dist/backend.js +15691 -0
  113. package/vendor/ink/node_modules/react-devtools-core/dist/backend.js.map +1 -0
  114. package/vendor/ink/node_modules/react-devtools-core/dist/importFile.worker.worker.js +2 -0
  115. package/vendor/ink/node_modules/react-devtools-core/dist/importFile.worker.worker.js.map +1 -0
  116. package/vendor/ink/node_modules/react-devtools-core/dist/parseSourceAndMetadata.worker.worker.js +14 -0
  117. package/vendor/ink/node_modules/react-devtools-core/dist/parseSourceAndMetadata.worker.worker.js.map +1 -0
  118. package/vendor/ink/node_modules/react-devtools-core/dist/standalone.js +2 -0
  119. package/vendor/ink/node_modules/react-devtools-core/dist/standalone.js.map +1 -0
  120. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/LICENSE +21 -0
  121. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/README.md +495 -0
  122. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/browser.js +8 -0
  123. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/index.js +10 -0
  124. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/buffer-util.js +129 -0
  125. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/constants.js +10 -0
  126. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/event-target.js +184 -0
  127. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/extension.js +223 -0
  128. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/limiter.js +55 -0
  129. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/permessage-deflate.js +518 -0
  130. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/receiver.js +607 -0
  131. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/sender.js +409 -0
  132. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/stream.js +180 -0
  133. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/validation.js +104 -0
  134. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/websocket-server.js +449 -0
  135. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/lib/websocket.js +1197 -0
  136. package/vendor/ink/node_modules/react-devtools-core/node_modules/ws/package.json +56 -0
  137. package/vendor/ink/node_modules/react-devtools-core/package.json +38 -0
  138. package/vendor/ink/node_modules/react-devtools-core/standalone.js +1 -0
  139. package/dist/cli/__tests__/print-mode-streaming.test.js +0 -270
  140. package/dist/cli/__tests__/print-mode.basic-output.test.js +0 -230
  141. package/dist/cli/__tests__/print-mode.session-errors.test.js +0 -252
  142. package/dist/cli/__tests__/print-mode.test.js +0 -273
  143. package/dist/cli/__tests__/serve-mode-messages.test.js +0 -338
  144. package/dist/cli/__tests__/serve-mode.messages.part2.test.js +0 -266
  145. package/dist/cli/__tests__/serve-mode.messages.test.js +0 -277
  146. package/dist/cli/__tests__/serve-mode.startup-http.test.js +0 -279
  147. package/dist/cli/__tests__/serve-mode.test.js +0 -345
  148. package/dist/cli/chat/NodeManager.d.ts +0 -30
  149. package/dist/cli/chat/NodeManager.js +0 -66
  150. package/dist/cli/chat/NodeManager.js.map +0 -1
  151. package/dist/cli/chat/chat-input-menu-handler.d.ts +0 -32
  152. package/dist/cli/chat/hooks/slash-imsg-handlers.js +0 -148
  153. package/dist/cli/chat/hooks/slash-imsg-handlers.js.map +0 -1
  154. package/dist/cli/chat/hooks/useStreamingReducer.d.ts +0 -66
  155. package/dist/cli/commands/__tests__/config-cmd.test.js +0 -270
  156. package/dist/cli/commands/__tests__/doctor.test.js +0 -257
  157. package/dist/cli/commands/__tests__/imsg-node-bridge.test.js +0 -99
  158. package/dist/cli/commands/__tests__/imsg-utils.test.js +0 -73
  159. package/dist/cli/commands/__tests__/init.test.js +0 -214
  160. package/dist/cli/commands/__tests__/mcp.test.js +0 -287
  161. package/dist/cli/commands/imsg-watcher-helpers.d.ts +0 -40
  162. package/dist/cli/commands/imsg-watcher-helpers.js +0 -184
  163. package/dist/cli/commands/imsg-watcher-helpers.js.map +0 -1
  164. package/dist/cli/commands/imsg-watcher.d.ts +0 -11
  165. package/dist/cli/commands/imsg-watcher.js +0 -230
  166. package/dist/cli/commands/imsg-watcher.js.map +0 -1
  167. package/dist/cli/services/__tests__/agent-definitions.test.js +0 -153
  168. package/dist/cli/services/__tests__/agent-events-global.test.js +0 -39
  169. package/dist/cli/services/__tests__/agent-events.part2.test.js +0 -113
  170. package/dist/cli/services/__tests__/agent-events.test.js +0 -157
  171. package/dist/cli/services/__tests__/agent-loop-auth.test.js +0 -392
  172. package/dist/cli/services/__tests__/agent-loop-budget.test.js +0 -389
  173. package/dist/cli/services/__tests__/agent-loop-tools-lifecycle.test.js +0 -430
  174. package/dist/cli/services/__tests__/agent-loop-tools-maxturns.test.js +0 -486
  175. package/dist/cli/services/__tests__/agent-loop-utils-execution.test.js +0 -528
  176. package/dist/cli/services/__tests__/agent-loop-utils-helpers.test.js +0 -466
  177. package/dist/cli/services/__tests__/agent-worker-base-execute.test.js +0 -257
  178. package/dist/cli/services/__tests__/agent-worker-base-helpers.test.js +0 -198
  179. package/dist/cli/services/__tests__/agent-worker-base.test.js +0 -278
  180. package/dist/cli/services/__tests__/auth-service-exports.test.js +0 -41
  181. package/dist/cli/services/__tests__/auth-service.part2.test.js +0 -169
  182. package/dist/cli/services/__tests__/auth-service.test.js +0 -242
  183. package/dist/cli/services/__tests__/background-processes.test.js +0 -282
  184. package/dist/cli/services/__tests__/claude-md-loader.test.js +0 -134
  185. package/dist/cli/services/__tests__/config-store.test.js +0 -247
  186. package/dist/cli/services/__tests__/debug-log.test.js +0 -199
  187. package/dist/cli/services/__tests__/edge-cases-caching.test.js +0 -174
  188. package/dist/cli/services/__tests__/edge-cases-compaction-core.test.js +0 -226
  189. package/dist/cli/services/__tests__/edge-cases-compaction-openai.test.js +0 -152
  190. package/dist/cli/services/__tests__/edge-cases-compaction-shapes.test.js +0 -53
  191. package/dist/cli/services/__tests__/edge-cases-compaction-thinking.test.js +0 -226
  192. package/dist/cli/services/__tests__/edge-cases-compaction.test.js +0 -131
  193. package/dist/cli/services/__tests__/edge-cases-paths.test.js +0 -86
  194. package/dist/cli/services/__tests__/error-logger-messages.test.js +0 -81
  195. package/dist/cli/services/__tests__/error-logger-transport.test.js +0 -119
  196. package/dist/cli/services/__tests__/error-logger.test.js +0 -264
  197. package/dist/cli/services/__tests__/file-history.test.js +0 -136
  198. package/dist/cli/services/__tests__/git-context-cache-reset.test.js +0 -223
  199. package/dist/cli/services/__tests__/git-context.test.js +0 -241
  200. package/dist/cli/services/__tests__/interactive-tools-execute.test.js +0 -166
  201. package/dist/cli/services/__tests__/interactive-tools-plan.test.js +0 -197
  202. package/dist/cli/services/__tests__/interactive-tools.part2.test.js +0 -168
  203. package/dist/cli/services/__tests__/interactive-tools.test.js +0 -179
  204. package/dist/cli/services/__tests__/keybinding-manager.test.js +0 -205
  205. package/dist/cli/services/__tests__/local-tools-dispatch.test.js +0 -404
  206. package/dist/cli/services/__tests__/local-tools.test.js +0 -238
  207. package/dist/cli/services/__tests__/lsp-manager.test.js +0 -364
  208. package/dist/cli/services/__tests__/mcp-client-connect-disconnect.test.js +0 -310
  209. package/dist/cli/services/__tests__/mcp-client.test.js +0 -93
  210. package/dist/cli/services/__tests__/memory-manager.test.js +0 -154
  211. package/dist/cli/services/__tests__/model-manager-utils.test.js +0 -154
  212. package/dist/cli/services/__tests__/model-manager.test.js +0 -175
  213. package/dist/cli/services/__tests__/permission-modes.test.js +0 -222
  214. package/dist/cli/services/__tests__/ripgrep.test.js +0 -328
  215. package/dist/cli/services/__tests__/server-tools-execute.test.js +0 -317
  216. package/dist/cli/services/__tests__/server-tools.test.js +0 -272
  217. package/dist/cli/services/__tests__/session-persistence.test.js +0 -245
  218. package/dist/cli/services/__tests__/subagent-basic.test.js +0 -489
  219. package/dist/cli/services/__tests__/subagent-edge.test.js +0 -545
  220. package/dist/cli/services/__tests__/subagent-prompts.test.js +0 -558
  221. package/dist/cli/services/__tests__/subagent-worker-errors.test.js +0 -255
  222. package/dist/cli/services/__tests__/subagent-worker.test.js +0 -242
  223. package/dist/cli/services/__tests__/system-prompt.test.js +0 -210
  224. package/dist/cli/services/__tests__/team-lead-comms-messaging.test.js +0 -250
  225. package/dist/cli/services/__tests__/team-lead-comms-result.test.js +0 -232
  226. package/dist/cli/services/__tests__/team-lead-comms-stop.test.js +0 -344
  227. package/dist/cli/services/__tests__/team-lead-comms.test.js +0 -285
  228. package/dist/cli/services/__tests__/team-lead-create.test.js +0 -327
  229. package/dist/cli/services/__tests__/team-lead-run.test.js +0 -318
  230. package/dist/cli/services/__tests__/team-lead-stop.test.js +0 -199
  231. package/dist/cli/services/__tests__/team-state-comms.test.js +0 -240
  232. package/dist/cli/services/__tests__/team-state-core.test.js +0 -230
  233. package/dist/cli/services/__tests__/team-state-tasks-complete-fail-available.test.js +0 -224
  234. package/dist/cli/services/__tests__/team-state-tasks.test.js +0 -184
  235. package/dist/cli/services/__tests__/telemetry-ai-metadata.test.js +0 -116
  236. package/dist/cli/services/__tests__/telemetry.part2.test.js +0 -195
  237. package/dist/cli/services/__tests__/telemetry.test.js +0 -176
  238. package/dist/cli/services/agent-loop-iteration.d.ts +0 -13
  239. package/dist/cli/services/agent-loop-setup.d.ts +0 -32
  240. package/dist/cli/services/agent-worker-base-api.d.ts +0 -19
  241. package/dist/cli/services/agent-worker-base-helpers.d.ts +0 -27
  242. package/dist/cli/services/agent-worker-base-tools.d.ts +0 -16
  243. package/dist/cli/services/agent-worker-base-types.d.ts +0 -81
  244. package/dist/cli/services/background-agents.d.ts +0 -26
  245. package/dist/cli/services/background-processes-ops.d.ts +0 -24
  246. package/dist/cli/services/background-tool-defs.d.ts +0 -50
  247. package/dist/cli/services/config-modules-model.test.js +0 -133
  248. package/dist/cli/services/config-modules-permission.test.js +0 -85
  249. package/dist/cli/services/config-modules-permissions.test.js +0 -85
  250. package/dist/cli/services/config-modules-session.test.js +0 -297
  251. package/dist/cli/services/format-server-response-columns.test.js +0 -265
  252. package/dist/cli/services/format-server-response-fallback.test.js +0 -65
  253. package/dist/cli/services/format-server-response-primitives-basic.test.js +0 -261
  254. package/dist/cli/services/format-server-response-primitives-nested.test.js +0 -188
  255. package/dist/cli/services/format-server-response-primitives.test.js +0 -300
  256. package/dist/cli/services/format-server-response-realworld.test.js +0 -248
  257. package/dist/cli/services/format-server-response-values.test.js +0 -247
  258. package/dist/cli/services/hooks-runners.test.js +0 -184
  259. package/dist/cli/services/hooks.glob-load.test.js +0 -233
  260. package/dist/cli/services/hooks.run-hooks.test.js +0 -184
  261. package/dist/cli/services/hooks.test.js +0 -233
  262. package/dist/cli/services/ink-incremental.d.ts +0 -19
  263. package/dist/cli/services/ink-incremental.js +0 -59
  264. package/dist/cli/services/ink-incremental.js.map +0 -1
  265. package/dist/cli/services/ink-resize-fix.d.ts +0 -18
  266. package/dist/cli/services/ink-resize-fix.js +0 -76
  267. package/dist/cli/services/ink-resize-fix.js.map +0 -1
  268. package/dist/cli/services/ink-sync-output.d.ts +0 -12
  269. package/dist/cli/services/ink-sync-output.js +0 -16
  270. package/dist/cli/services/ink-sync-output.js.map +0 -1
  271. package/dist/cli/services/interactive-tool-defs.d.ts +0 -80
  272. package/dist/cli/services/local-tools-definitions.d.ts +0 -6
  273. package/dist/cli/services/local-tools-files.test.js +0 -256
  274. package/dist/cli/services/local-tools-read-many.d.ts +0 -6
  275. package/dist/cli/services/model-router.test.js +0 -245
  276. package/dist/cli/services/rewind-rewindTo.test.js +0 -202
  277. package/dist/cli/services/rewind.test.js +0 -175
  278. package/dist/cli/services/sandbox.test.js +0 -198
  279. package/dist/cli/services/subagent-execution.d.ts +0 -12
  280. package/dist/cli/services/team-lead-auto.d.ts +0 -11
  281. package/dist/cli/services/team-lead-execution.d.ts +0 -28
  282. package/dist/cli/services/teammate-loop.js +0 -557
  283. package/dist/cli/services/teammate-loop.js.map +0 -1
  284. package/dist/cli/services/tools/__tests__/agent-tools-tasks-teams.test.js +0 -250
  285. package/dist/cli/services/tools/__tests__/agent-tools-teams.test.js +0 -200
  286. package/dist/cli/services/tools/__tests__/agent-tools.test.js +0 -340
  287. package/dist/cli/services/tools/__tests__/file-ops-cache.test.js +0 -152
  288. package/dist/cli/services/tools/__tests__/file-ops-notebook.test.js +0 -249
  289. package/dist/cli/services/tools/__tests__/file-ops-read.test.js +0 -261
  290. package/dist/cli/services/tools/__tests__/file-ops-write.test.js +0 -292
  291. package/dist/cli/services/tools/__tests__/search-tools-rg.test.js +0 -92
  292. package/dist/cli/services/tools/__tests__/search-tools.part2.test.js +0 -174
  293. package/dist/cli/services/tools/__tests__/search-tools.test.js +0 -227
  294. package/dist/cli/services/tools/__tests__/shell-exec-allowed-core.test.js +0 -163
  295. package/dist/cli/services/tools/__tests__/shell-exec-allowed-extended.test.js +0 -220
  296. package/dist/cli/services/tools/__tests__/shell-exec-allowed.part2.test.js +0 -215
  297. package/dist/cli/services/tools/__tests__/shell-exec-allowed.test.js +0 -154
  298. package/dist/cli/services/tools/__tests__/shell-exec-blocked.test.js +0 -132
  299. package/dist/cli/services/tools/__tests__/shell-exec-execution.test.js +0 -245
  300. package/dist/cli/services/tools/__tests__/task-manager-create.test.js +0 -110
  301. package/dist/cli/services/tools/__tests__/task-manager-crud.test.js +0 -339
  302. package/dist/cli/services/tools/__tests__/task-manager-list-get.test.js +0 -343
  303. package/dist/cli/services/tools/__tests__/task-manager-query.test.js +0 -346
  304. package/dist/cli/services/tools/__tests__/task-manager-routing.test.js +0 -58
  305. package/dist/cli/services/tools/__tests__/task-manager-update.test.js +0 -224
  306. package/dist/cli/services/tools/__tests__/task-manager.test.js +0 -159
  307. package/dist/cli/services/tools/__tests__/web-tools-html-search.test.js +0 -227
  308. package/dist/cli/services/tools/__tests__/web-tools.test.js +0 -285
  309. package/dist/cli/services/tools/shell-exec.test.js +0 -148
  310. package/dist/cli/shared/SharedTick.d.ts +0 -10
  311. package/dist/cli/shared/__tests__/markdown.test.js +0 -188
  312. package/dist/local-agent/__tests__/connection-disconnect.test.js +0 -201
  313. package/dist/local-agent/__tests__/connection-lifecycle.test.js +0 -289
  314. package/dist/local-agent/__tests__/connection-msghandling.test.js +0 -311
  315. package/dist/local-agent/__tests__/connection-reconnect.test.js +0 -230
  316. package/dist/local-agent/__tests__/connection-toolexec.test.js +0 -253
  317. package/dist/local-agent/__tests__/discovery.test.js +0 -328
  318. package/dist/local-agent/__tests__/executor-background.test.js +0 -219
  319. package/dist/local-agent/__tests__/executor-exec.test.js +0 -221
  320. package/dist/local-agent/__tests__/executor-jobs-sessions.test.js +0 -220
  321. package/dist/local-agent/__tests__/executor-system-info.test.js +0 -133
  322. package/dist/local-agent/__tests__/executor-systeminfo.test.js +0 -109
  323. package/dist/local-agent/__tests__/executor.test.js +0 -235
  324. package/dist/local-agent/__tests__/index.test.js +0 -139
  325. package/dist/node/__tests__/cli-channels.test.js +0 -293
  326. package/dist/node/__tests__/cli-config-edge.test.js +0 -154
  327. package/dist/node/__tests__/cli-config.test.js +0 -215
  328. package/dist/node/__tests__/config.test.js +0 -292
  329. package/dist/node/__tests__/runtime-heartbeat.test.js +0 -153
  330. package/dist/node/__tests__/runtime-lifecycle-init.test.js +0 -263
  331. package/dist/node/__tests__/runtime-lifecycle-stats.test.js +0 -180
  332. package/dist/node/__tests__/runtime-lifecycle.test.js +0 -305
  333. package/dist/node/__tests__/runtime-relay.test.js +0 -341
  334. package/dist/node/adapters/__tests__/base.test.js +0 -286
  335. package/dist/node/adapters/__tests__/discord.test.js +0 -284
  336. package/dist/node/adapters/__tests__/email-send.test.js +0 -295
  337. package/dist/node/adapters/__tests__/email.inbound-send.test.js +0 -217
  338. package/dist/node/adapters/__tests__/email.lifecycle.test.js +0 -211
  339. package/dist/node/adapters/__tests__/email.test.js +0 -290
  340. package/dist/node/adapters/__tests__/email.webhook-send.test.js +0 -251
  341. package/dist/node/adapters/__tests__/imessage-filter.test.js +0 -183
  342. package/dist/node/adapters/__tests__/imessage-lifecycle.test.js +0 -215
  343. package/dist/node/adapters/__tests__/imessage-send-restart.test.js +0 -227
  344. package/dist/node/adapters/__tests__/slack.part2.test.js +0 -135
  345. package/dist/node/adapters/__tests__/slack.test.js +0 -241
  346. package/dist/node/adapters/__tests__/sms-extras.test.js +0 -108
  347. package/dist/node/adapters/__tests__/sms-lifecycle.test.js +0 -203
  348. package/dist/node/adapters/__tests__/sms-messaging.test.js +0 -266
  349. package/dist/node/adapters/__tests__/sms.part2.test.js +0 -174
  350. package/dist/node/adapters/__tests__/sms.test.js +0 -253
  351. package/dist/node/adapters/__tests__/telegram-polling.test.js +0 -256
  352. package/dist/node/adapters/__tests__/telegram-send.test.js +0 -166
  353. package/dist/node/adapters/__tests__/webchat-inbound.test.js +0 -188
  354. package/dist/node/adapters/__tests__/webchat-outbound.test.js +0 -178
  355. package/dist/node/adapters/__tests__/whatsapp-inbound.test.js +0 -200
  356. package/dist/node/adapters/__tests__/whatsapp-send.test.js +0 -212
  357. package/dist/node/adapters/__tests__/whatsapp.test.js +0 -280
  358. package/dist/server/__tests__/gateway-fast-fail.test.js +0 -160
  359. package/dist/server/__tests__/local-agent-gateway.test.js +0 -186
  360. package/dist/server/__tests__/proxy-handlers-delegation.test.js +0 -240
  361. package/dist/server/__tests__/proxy-handlers-validation.test.js +0 -211
  362. package/dist/server/__tests__/proxy-handlers.part2.test.js +0 -240
  363. package/dist/server/__tests__/proxy-handlers.test.js +0 -213
  364. package/dist/server/__tests__/strip-base64-e2e.test.js +0 -303
  365. package/dist/server/__tests__/strip-base64.test.js +0 -256
  366. package/dist/server/__tests__/tool-router-agent-tools.test.js +0 -324
  367. package/dist/server/__tests__/tool-router-execute-core.test.js +0 -357
  368. package/dist/server/__tests__/tool-router-execute-permissions.test.js +0 -332
  369. package/dist/server/__tests__/tool-router-execute.test.js +0 -348
  370. package/dist/server/__tests__/tool-router-load.test.js +0 -432
  371. package/dist/server/__tests__/tool-router-permissions.test.js +0 -359
  372. package/dist/server/__tests__/tool-router-registry-cache.test.js +0 -383
  373. package/dist/server/__tests__/tool-router-registry-handlers.test.js +0 -272
  374. package/dist/server/__tests__/tool-router-registry.test.js +0 -331
  375. package/dist/server/__tests__/validation-inventory.test.js +0 -250
  376. package/dist/server/__tests__/validation-misc.test.js +0 -243
  377. package/dist/server/__tests__/validation-supply-chain.test.js +0 -188
  378. package/dist/server/__tests__/worker.test.js +0 -265
  379. package/dist/server/handlers/__tests__/conversation-lock.test.js +0 -117
  380. package/dist/server/handlers/__tests__/e2e/auth-cross-platform-login.e2e.test.js +0 -268
  381. package/dist/server/handlers/__tests__/e2e/auth-cross-platform-tokens.e2e.test.js +0 -264
  382. package/dist/server/handlers/__tests__/e2e/email-pipeline-send.e2e.test.js +0 -214
  383. package/dist/server/handlers/__tests__/e2e/email-pipeline-threads.e2e.test.js +0 -168
  384. package/dist/server/handlers/__tests__/e2e/error-logging-pipeline-dedup.e2e.test.js +0 -229
  385. package/dist/server/handlers/__tests__/e2e/error-logging-pipeline.e2e.test.js +0 -239
  386. package/dist/server/handlers/__tests__/e2e/error-logging-rate-limit.e2e.test.js +0 -150
  387. package/dist/server/handlers/__tests__/e2e/inventory-sync-guards.e2e.test.js +0 -177
  388. package/dist/server/handlers/__tests__/e2e/inventory-sync.e2e.test.js +0 -228
  389. package/dist/server/handlers/__tests__/e2e/inventory-sync.part2.e2e.test.js +0 -188
  390. package/dist/server/handlers/__tests__/e2e/order-lifecycle-fulfillment.e2e.test.js +0 -295
  391. package/dist/server/handlers/__tests__/e2e/order-lifecycle.e2e.test.js +0 -277
  392. package/dist/server/handlers/__tests__/e2e/order-lifecycle.fulfillment.e2e.test.js +0 -307
  393. package/dist/server/handlers/__tests__/e2e/order-lifecycle.setup.e2e.test.js +0 -177
  394. package/dist/server/handlers/__tests__/e2e/storefront-checkout-cart.e2e.test.js +0 -255
  395. package/dist/server/handlers/__tests__/e2e/storefront-checkout-webhook.e2e.test.js +0 -231
  396. package/dist/server/handlers/__tests__/e2e/workflow-execution-failures.e2e.test.js +0 -235
  397. package/dist/server/handlers/__tests__/e2e/workflow-execution.e2e.test.js +0 -294
  398. package/dist/server/handlers/__tests__/e2e/workflow-security.e2e.test.js +0 -311
  399. package/dist/server/handlers/__tests__/e2e/workflow-security.part2.e2e.test.js +0 -267
  400. package/dist/server/handlers/__tests__/workflow-cache.test.js +0 -237
  401. package/dist/server/handlers/analytics-errors-edge.test.js +0 -173
  402. package/dist/server/handlers/analytics.test.js +0 -280
  403. package/dist/server/handlers/api-docs-examples-ext.d.ts +0 -9
  404. package/dist/server/handlers/api-docs-examples-ext.js +0 -278
  405. package/dist/server/handlers/api-docs-examples-ext.js.map +0 -1
  406. package/dist/server/handlers/api-docs-examples.d.ts +0 -8
  407. package/dist/server/handlers/api-docs-examples.js +0 -221
  408. package/dist/server/handlers/api-docs-examples.js.map +0 -1
  409. package/dist/server/handlers/api-docs-sections-ext.d.ts +0 -2
  410. package/dist/server/handlers/api-docs-sections-ext.js +0 -497
  411. package/dist/server/handlers/api-docs-sections-ext.js.map +0 -1
  412. package/dist/server/handlers/api-docs-sections.d.ts +0 -21
  413. package/dist/server/handlers/api-docs-sections.js +0 -293
  414. package/dist/server/handlers/api-docs-sections.js.map +0 -1
  415. package/dist/server/handlers/api-keys.part2.test.js +0 -157
  416. package/dist/server/handlers/api-keys.test.js +0 -161
  417. package/dist/server/handlers/billing-routes.test.js +0 -123
  418. package/dist/server/handlers/billing.test.js +0 -215
  419. package/dist/server/handlers/browser-actions-errors.test.js +0 -94
  420. package/dist/server/handlers/browser-actions.part2.test.js +0 -190
  421. package/dist/server/handlers/browser-actions.test.js +0 -190
  422. package/dist/server/handlers/browser-validation.test.js +0 -257
  423. package/dist/server/handlers/catalog.test.js +0 -297
  424. package/dist/server/handlers/comms.test.js +0 -289
  425. package/dist/server/handlers/creations-advanced-collections.test.js +0 -214
  426. package/dist/server/handlers/creations-advanced-generate.test.js +0 -142
  427. package/dist/server/handlers/creations-advanced.test.js +0 -171
  428. package/dist/server/handlers/creations-collections-preview.test.js +0 -214
  429. package/dist/server/handlers/creations-crud.test.js +0 -260
  430. package/dist/server/handlers/creations-mutations.test.js +0 -197
  431. package/dist/server/handlers/crm.test.js +0 -179
  432. package/dist/server/handlers/discovery-advertise.test.js +0 -185
  433. package/dist/server/handlers/discovery-scan.test.js +0 -233
  434. package/dist/server/handlers/embeddings-embed-search.test.js +0 -196
  435. package/dist/server/handlers/embeddings-index-delete-stats.test.js +0 -140
  436. package/dist/server/handlers/embeddings-search.test.js +0 -221
  437. package/dist/server/handlers/embeddings.test.js +0 -137
  438. package/dist/server/handlers/enrichment-breach.d.ts +0 -8
  439. package/dist/server/handlers/enrichment-breach.js +0 -266
  440. package/dist/server/handlers/enrichment-breach.js.map +0 -1
  441. package/dist/server/handlers/enrichment-data.d.ts +0 -13
  442. package/dist/server/handlers/enrichment-data.js +0 -145
  443. package/dist/server/handlers/enrichment-data.js.map +0 -1
  444. package/dist/server/handlers/enrichment-mutations.test.js +0 -240
  445. package/dist/server/handlers/enrichment-queries.test.js +0 -181
  446. package/dist/server/handlers/enrichment-validation.test.js +0 -177
  447. package/dist/server/handlers/enrichment-writes.d.ts +0 -16
  448. package/dist/server/handlers/enrichment-writes.js +0 -226
  449. package/dist/server/handlers/enrichment-writes.js.map +0 -1
  450. package/dist/server/handlers/image-gen.test.js +0 -205
  451. package/dist/server/handlers/inventory.test.js +0 -380
  452. package/dist/server/handlers/kali-background.test.js +0 -222
  453. package/dist/server/handlers/kali-errors.test.js +0 -92
  454. package/dist/server/handlers/kali-validation.test.js +0 -234
  455. package/dist/server/handlers/llm-providers-actions.test.js +0 -220
  456. package/dist/server/handlers/llm-providers-anthropic.test.js +0 -239
  457. package/dist/server/handlers/llm-providers-failover.test.js +0 -232
  458. package/dist/server/handlers/llm-providers-providers.test.js +0 -300
  459. package/dist/server/handlers/llm-providers-validation.test.js +0 -239
  460. package/dist/server/handlers/local-agent-tools.test.js +0 -224
  461. package/dist/server/handlers/local-agent.test.js +0 -198
  462. package/dist/server/handlers/local-agent.tools-status.test.js +0 -204
  463. package/dist/server/handlers/local-agent.validation-exec.test.js +0 -182
  464. package/dist/server/handlers/meta-ads-audience-rules.test.js +0 -243
  465. package/dist/server/handlers/meta-ads-audience-targeting.test.js +0 -205
  466. package/dist/server/handlers/meta-ads-audiences-targeting.test.js +0 -383
  467. package/dist/server/handlers/meta-ads-crud-ads.test.js +0 -136
  468. package/dist/server/handlers/meta-ads-crud-campaigns.test.js +0 -189
  469. package/dist/server/handlers/meta-ads-crud-create.test.js +0 -303
  470. package/dist/server/handlers/meta-ads-crud-list-update.test.js +0 -259
  471. package/dist/server/handlers/meta-ads-delete-publish-sync.test.js +0 -282
  472. package/dist/server/handlers/meta-ads-insights.test.js +0 -80
  473. package/dist/server/handlers/meta-ads-list-get.test.js +0 -237
  474. package/dist/server/handlers/meta-ads-publish-delete.test.js +0 -254
  475. package/dist/server/handlers/meta-ads-publish-helpers.js +0 -117
  476. package/dist/server/handlers/meta-ads-publish-helpers.js.map +0 -1
  477. package/dist/server/handlers/meta-ads-publish-sync.test.js +0 -205
  478. package/dist/server/handlers/meta-ads-publish.test.js +0 -254
  479. package/dist/server/handlers/meta-ads-sync-insights.test.js +0 -184
  480. package/dist/server/handlers/meta-ads-update.test.js +0 -117
  481. package/dist/server/handlers/nodes-channels.test.js +0 -413
  482. package/dist/server/handlers/nodes-events.test.js +0 -131
  483. package/dist/server/handlers/nodes-list-delete.test.js +0 -171
  484. package/dist/server/handlers/nodes-messages-delivery.test.js +0 -208
  485. package/dist/server/handlers/nodes-messages.test.js +0 -211
  486. package/dist/server/handlers/nodes-register.test.js +0 -277
  487. package/dist/server/handlers/nodes.test.js +0 -353
  488. package/dist/server/handlers/operations.test.js +0 -136
  489. package/dist/server/handlers/platform-telemetry.test.js +0 -200
  490. package/dist/server/handlers/platform-websearch.test.js +0 -160
  491. package/dist/server/handlers/storefront.test.js +0 -329
  492. package/dist/server/handlers/supply-chain.test.js +0 -347
  493. package/dist/server/handlers/transcription.test.js +0 -118
  494. package/dist/server/handlers/video-gen-veo.js +0 -114
  495. package/dist/server/handlers/video-gen-veo.js.map +0 -1
  496. package/dist/server/handlers/video-gen.test.js +0 -146
  497. package/dist/server/handlers/voice.test.js +0 -153
  498. package/dist/server/handlers/workflow-steps.test.js +0 -330
  499. package/dist/server/handlers/workflows-extras.test.js +0 -65
  500. package/dist/server/handlers/workflows.part2.test.js +0 -170
  501. package/dist/server/handlers/workflows.test.js +0 -281
  502. package/dist/server/lib/__tests__/batch-client-conversion-jsonl.test.js +0 -171
  503. package/dist/server/lib/__tests__/batch-client-polling.test.js +0 -292
  504. package/dist/server/lib/__tests__/batch-client-queue.test.js +0 -270
  505. package/dist/server/lib/__tests__/clickhouse-buffer.test.js +0 -236
  506. package/dist/server/lib/__tests__/code-worker-edge-cases.test.js +0 -118
  507. package/dist/server/lib/__tests__/code-worker-pool-execute.test.js +0 -193
  508. package/dist/server/lib/__tests__/code-worker-pool-execution.test.js +0 -165
  509. package/dist/server/lib/__tests__/code-worker-pool-init.test.js +0 -131
  510. package/dist/server/lib/__tests__/code-worker-pool.test.js +0 -194
  511. package/dist/server/lib/__tests__/code-worker-sandbox-ops.test.js +0 -123
  512. package/dist/server/lib/__tests__/code-worker-sandbox.test.js +0 -217
  513. package/dist/server/lib/__tests__/code-worker.test.js +0 -179
  514. package/dist/server/lib/__tests__/compaction-service-generate.test.js +0 -229
  515. package/dist/server/lib/__tests__/compaction-service.test.js +0 -319
  516. package/dist/server/lib/__tests__/otel.test.js +0 -146
  517. package/dist/server/lib/__tests__/prompt-sanitizer-validation.test.js +0 -165
  518. package/dist/server/lib/__tests__/prompt-sanitizer.sanitize.test.js +0 -343
  519. package/dist/server/lib/__tests__/prompt-sanitizer.test.js +0 -328
  520. package/dist/server/lib/__tests__/prompt-sanitizer.validate-tool.test.js +0 -145
  521. package/dist/server/lib/__tests__/provider-capabilities.test.js +0 -263
  522. package/dist/server/lib/__tests__/provider-failover-routing.test.js +0 -145
  523. package/dist/server/lib/__tests__/provider-failover-state.test.js +0 -131
  524. package/dist/server/lib/__tests__/rate-limiter-budgets.test.js +0 -216
  525. package/dist/server/lib/__tests__/rate-limiter.budgets-tools.test.js +0 -113
  526. package/dist/server/lib/__tests__/rate-limiter.check-request.test.js +0 -141
  527. package/dist/server/lib/__tests__/rate-limiter.stats-lifecycle.test.js +0 -135
  528. package/dist/server/lib/__tests__/rate-limiter.test.js +0 -207
  529. package/dist/server/lib/__tests__/server-agent-loop-abort-conditions.test.js +0 -544
  530. package/dist/server/lib/__tests__/server-agent-loop-abort.part2.test.js +0 -504
  531. package/dist/server/lib/__tests__/server-agent-loop-abort.test.js +0 -396
  532. package/dist/server/lib/__tests__/server-agent-loop-compaction.test.js +0 -397
  533. package/dist/server/lib/__tests__/server-agent-loop-failover.test.js +0 -356
  534. package/dist/server/lib/__tests__/server-agent-loop-features-caching.test.js +0 -519
  535. package/dist/server/lib/__tests__/server-agent-loop-features-edges.test.js +0 -512
  536. package/dist/server/lib/__tests__/server-subagent-bailout.test.js +0 -194
  537. package/dist/server/lib/__tests__/server-subagent-basics.test.js +0 -348
  538. package/dist/server/lib/__tests__/server-subagent-errors-abort.test.js +0 -319
  539. package/dist/server/lib/__tests__/server-subagent-errors-progress.test.js +0 -253
  540. package/dist/server/lib/__tests__/server-subagent-errors.part2.test.js +0 -253
  541. package/dist/server/lib/__tests__/server-subagent-errors.test.js +0 -319
  542. package/dist/server/lib/__tests__/session-checkpoint-load.test.js +0 -275
  543. package/dist/server/lib/__tests__/session-checkpoint-save.test.js +0 -159
  544. package/dist/server/lib/__tests__/ssrf-guard.test.js +0 -93
  545. package/dist/server/lib/__tests__/supabase-client.test.js +0 -111
  546. package/dist/server/lib/__tests__/template-resolver.test.js +0 -317
  547. package/dist/server/lib/__tests__/utils-timeout.test.js +0 -49
  548. package/dist/server/lib/__tests__/utils.test.js +0 -322
  549. package/dist/server/providers/__tests__/anthropic-adapter.test.js +0 -228
  550. package/dist/server/providers/__tests__/anthropic-betas-toolchoice.test.js +0 -257
  551. package/dist/server/providers/__tests__/anthropic-errors.test.js +0 -262
  552. package/dist/server/providers/__tests__/anthropic-stream-core.test.js +0 -275
  553. package/dist/server/providers/__tests__/anthropic-streaming-betas.test.js +0 -247
  554. package/dist/server/providers/__tests__/anthropic-streaming-core.test.js +0 -275
  555. package/dist/server/providers/__tests__/bedrock-config.test.js +0 -177
  556. package/dist/server/providers/__tests__/bedrock-stream-behavior-streaming.test.js +0 -272
  557. package/dist/server/providers/__tests__/bedrock-stream-behavior-toolchoice.test.js +0 -214
  558. package/dist/server/providers/__tests__/bedrock-stream-behavior.part2.test.js +0 -165
  559. package/dist/server/providers/__tests__/bedrock-stream-behavior.test.js +0 -309
  560. package/dist/server/providers/__tests__/bedrock-stream-body-credentials.test.js +0 -170
  561. package/dist/server/providers/__tests__/bedrock-stream-body-extras.test.js +0 -183
  562. package/dist/server/providers/__tests__/bedrock-stream-body-request.test.js +0 -305
  563. package/dist/server/providers/__tests__/bedrock-stream-body.part2.test.js +0 -305
  564. package/dist/server/providers/__tests__/bedrock-stream-body.test.js +0 -175
  565. package/dist/server/providers/__tests__/bedrock-stream-errors.test.js +0 -165
  566. package/dist/server/providers/__tests__/gemini-config-methods.test.js +0 -182
  567. package/dist/server/providers/__tests__/gemini-config-streaming.test.js +0 -257
  568. package/dist/server/providers/__tests__/gemini-conversion-messages.test.js +0 -247
  569. package/dist/server/providers/__tests__/gemini-conversion-schema.test.js +0 -365
  570. package/dist/server/providers/__tests__/gemini-tools-choice.test.js +0 -221
  571. package/dist/server/providers/__tests__/gemini-tools-fn.test.js +0 -252
  572. package/dist/server/providers/__tests__/openai-config.test.js +0 -194
  573. package/dist/server/providers/__tests__/openai-conversion.test.js +0 -276
  574. package/dist/server/providers/__tests__/openai-messages.test.js +0 -261
  575. package/dist/server/providers/__tests__/openai-streaming.test.js +0 -394
  576. package/dist/server/providers/__tests__/openai-tools-cache.test.js +0 -227
  577. package/dist/server/providers/__tests__/registry.test.js +0 -183
  578. package/dist/server/providers/__tests__/shared.test.js +0 -297
  579. package/dist/shared/agent-core-config.test.js +0 -132
  580. package/dist/shared/agent-core-context-thinking.test.js +0 -293
  581. package/dist/shared/agent-core-loop-calls.test.js +0 -174
  582. package/dist/shared/agent-core-loop-detector-bail.test.js +0 -201
  583. package/dist/shared/agent-core-loop-detector.test.js +0 -195
  584. package/dist/shared/agent-core-loop-errors.test.js +0 -258
  585. package/dist/shared/agent-core-pricing.test.js +0 -191
  586. package/dist/shared/agent-core-sanitize-retry.test.js +0 -129
  587. package/dist/shared/api-client-build-request.test.js +0 -228
  588. package/dist/shared/api-client-build-system-caching.test.js +0 -107
  589. package/dist/shared/api-client-build.test.js +0 -223
  590. package/dist/shared/api-client-config.d.ts +0 -21
  591. package/dist/shared/api-client-helpers.d.ts +0 -57
  592. package/dist/shared/api-client-helpers.test.js +0 -261
  593. package/dist/shared/api-client-proxy-happy.test.js +0 -255
  594. package/dist/shared/api-client-proxy-retry.test.js +0 -307
  595. package/dist/shared/api-client-proxy.d.ts +0 -26
  596. package/dist/shared/api-client-proxy.test.js +0 -255
  597. package/dist/shared/api-client-retry.test.js +0 -307
  598. package/dist/shared/api-client-system-trimming.test.js +0 -261
  599. package/dist/shared/api-client-trimming.d.ts +0 -36
  600. package/dist/shared/api-client.test.js +0 -228
  601. package/dist/shared/compaction-thinking.test.js +0 -315
  602. package/dist/shared/compaction-trimming.test.js +0 -223
  603. package/dist/shared/sse-parser-callbacks.test.js +0 -422
  604. package/dist/shared/sse-parser-collect.test.js +0 -252
  605. package/dist/shared/sse-parser-e2e.test.js +0 -558
  606. package/dist/shared/sse-parser-parse.test.js +0 -253
  607. package/dist/shared/tool-dispatch-advanced-batch-build.test.js +0 -405
  608. package/dist/shared/tool-dispatch-advanced.test.js +0 -320
  609. package/dist/shared/tool-dispatch-basic.test.js +0 -278
  610. package/dist/shared/tool-dispatch-content.d.ts +0 -14
  611. package/dist/shared/tool-dispatch-parallel.test.js +0 -378
  612. package/dist/webchat/__tests__/widget-messaging.test.js +0 -323
  613. package/dist/webchat/__tests__/widget.test.js +0 -273
@@ -2,11 +2,11 @@
2
2
  //
3
3
  // Unified path for streaming and non-streaming API calls.
4
4
 
5
- import { isRetryableError, sanitizeError } from "../../shared/agent-core.js";
5
+ import { categorizeError, isRetryableError, sanitizeError } from "../../shared/agent-core.js";
6
6
  import { processStreamWithCallbacks } from "../../shared/sse-parser.js";
7
7
  import { providerFailover } from "./provider-failover.js";
8
8
  import { createLogger } from "./logger.js";
9
- import { MAX_RETRIES, RETRY_BASE_DELAY_MS } from "./agent-loop-types.js";
9
+ import { MAX_RETRIES, RETRY_BASE_DELAY_MS, RETRY_MAX_DELAY_MS, RETRY_OVERLOADED_MULTIPLIER, MODEL_FALLBACK_AFTER_ATTEMPT, MODEL_FALLBACK_MAP } from "./agent-loop-types.js";
10
10
  const log = createLogger("agent-loop");
11
11
 
12
12
  // ============================================================================
@@ -84,11 +84,41 @@ export async function executeTurn(cfg, callbacks, streaming) {
84
84
  } catch (err) {
85
85
  providerFailover.recordFailure(cfg.activeProvider);
86
86
  if (attempt < MAX_RETRIES && isRetryableError(err)) {
87
- const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
87
+ const {
88
+ category
89
+ } = categorizeError(err);
90
+ let delay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt), RETRY_MAX_DELAY_MS);
91
+
92
+ // Overloaded/rate-limited errors need much longer backoff
93
+ if (category === "PROVIDER_DOWN" || category === "RATE_LIMIT") {
94
+ delay = Math.min(delay * RETRY_OVERLOADED_MULTIPLIER, RETRY_MAX_DELAY_MS);
95
+
96
+ // Model fallback: after N failed attempts on an overloaded model,
97
+ // downgrade to a lower-tier model (e.g. Opus → Sonnet) for remaining retries.
98
+ // Sonnet has much more capacity and is almost never overloaded.
99
+ if (attempt >= MODEL_FALLBACK_AFTER_ATTEMPT) {
100
+ const fallbackModel = MODEL_FALLBACK_MAP[baseParams.model];
101
+ if (fallbackModel && baseParams.model !== fallbackModel) {
102
+ log.warn({
103
+ from: baseParams.model,
104
+ to: fallbackModel,
105
+ attempt
106
+ }, "model fallback: overloaded, downgrading");
107
+ baseParams.model = fallbackModel;
108
+ // Reset delay — fallback model is likely available immediately
109
+ delay = Math.min(RETRY_BASE_DELAY_MS, delay);
110
+ }
111
+ }
112
+ }
113
+
114
+ // Add jitter (±25%) to prevent thundering herd
115
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
116
+ delay = Math.round(delay + jitter);
88
117
  log.warn({
89
118
  attempt: attempt + 1,
90
119
  maxRetries: MAX_RETRIES,
91
120
  delayMs: delay,
121
+ category,
92
122
  err: sanitizeError(err)
93
123
  }, "retrying API call");
94
124
  await new Promise(resolve => setTimeout(resolve, delay));
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop-turn.js","names":["isRetryableError","sanitizeError","processStreamWithCallbacks","providerFailover","createLogger","MAX_RETRIES","RETRY_BASE_DELAY_MS","log","executeTurn","cfg","callbacks","streaming","betas","ctxMgmt","thinkingCfg","beta","push","documents","length","includes","baseParams","model","activeModel","max_tokens","maxTokens","temperature","thinking","type","system","omitTools","tools","finalToolDefs","anthropicToolChoice","tool_choice","messages","finalMessages","context_management","config","attempt","result","stream","anthropic","create","streamResult","text","toolUseBlocks","compactionContent","stopReason","citations","usage","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","response","extractNonStreamingResult","recordSuccess","activeProvider","err","recordFailure","delay","Math","pow","warn","maxRetries","delayMs","Promise","resolve","setTimeout","Error","executeStreamingTurn","executeNonStreamingTurn","block","content","onText","id","name","input","onToolStart","citeBlock","citation","cited_text","document_index","start_char_index","end_char_index","document_title","onCitation","stop_reason","input_tokens","output_tokens","cache_read_input_tokens","cache_creation_input_tokens"],"sources":["../../../src/server/lib/agent-loop-turn.ts"],"sourcesContent":["// agent-loop-turn.ts — Per-turn API call + response processing for the server agent loop.\n//\n// Unified path for streaming and non-streaming API calls.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n BetaTextBlockParam,\n BetaMessageParam,\n BetaToolUnion,\n BetaMessage,\n MessageCreateParamsStreaming,\n MessageCreateParamsNonStreaming,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages\";\n\nimport { isRetryableError, sanitizeError } from \"../../shared/agent-core.js\";\nimport { processStreamWithCallbacks } from \"../../shared/sse-parser.js\";\nimport type { BetaStreamEvent } from \"../../shared/anthropic-types.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport { providerFailover } from \"./provider-failover.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { MAX_RETRIES, RETRY_BASE_DELAY_MS, type TurnMetrics } from \"./agent-loop-types.js\";\n\nconst log = createLogger(\"agent-loop\");\n\n// ============================================================================\n// Shared turn-level types\n// ============================================================================\n\nexport interface TurnConfig {\n anthropic: Anthropic;\n activeModel: string;\n activeProvider: string;\n maxTokens: number;\n temperature: number;\n system: BetaTextBlockParam[];\n finalToolDefs: object[];\n finalMessages: Anthropic.MessageParam[];\n anthropicToolChoice?: Record<string, unknown>;\n omitTools: boolean;\n ctxMgmt: { betas: string[]; config: unknown };\n thinkingCfg: { thinking: Record<string, unknown>; beta?: string };\n documents?: any[];\n}\n\nexport interface TurnResult {\n text: string;\n toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }>;\n compactionContent: string | null;\n stopReason: string;\n citations: CitationBlock[];\n usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n };\n}\n\nexport interface TurnCallbacks {\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onCitation?: (citation: CitationBlock) => void;\n}\n\n// ============================================================================\n// UNIFIED TURN EXECUTION — streaming or non-streaming via single path\n// ============================================================================\n\n/**\n * Execute a single API turn with retry, provider failover, and response normalization.\n * Handles both streaming and non-streaming modes through a unified code path.\n */\nexport async function executeTurn(\n cfg: TurnConfig,\n callbacks: TurnCallbacks,\n streaming: boolean,\n): Promise<TurnResult> {\n // Build betas list (shared between both modes)\n const betas = [...cfg.ctxMgmt.betas];\n if (cfg.thinkingCfg.beta) betas.push(cfg.thinkingCfg.beta);\n if (cfg.documents?.length && !betas.includes(\"citations-2025-04-15\")) {\n betas.push(\"citations-2025-04-15\");\n }\n\n // Build shared API params\n const baseParams = {\n model: cfg.activeModel,\n max_tokens: cfg.maxTokens,\n temperature: cfg.thinkingCfg.thinking.type !== \"disabled\" ? 1 : cfg.temperature,\n system: cfg.system,\n ...(cfg.omitTools ? {} : { tools: cfg.finalToolDefs as unknown as BetaToolUnion[] }),\n ...(cfg.anthropicToolChoice && !cfg.omitTools ? { tool_choice: cfg.anthropicToolChoice } : {}),\n messages: cfg.finalMessages as unknown as BetaMessageParam[],\n thinking: cfg.thinkingCfg.thinking,\n betas,\n context_management: cfg.ctxMgmt.config,\n ...(cfg.documents?.length ? { documents: cfg.documents } : {}),\n };\n\n // Retry loop (shared between both modes)\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n let result: TurnResult;\n\n if (streaming) {\n const stream = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n stream: true,\n } as unknown as MessageCreateParamsStreaming);\n\n const streamResult = await processStreamWithCallbacks(\n stream as unknown as AsyncIterable<BetaStreamEvent>,\n callbacks,\n );\n\n result = {\n text: streamResult.text,\n toolUseBlocks: streamResult.toolUseBlocks,\n compactionContent: streamResult.compactionContent,\n stopReason: streamResult.stopReason || \"end_turn\",\n citations: streamResult.citations,\n usage: {\n inputTokens: streamResult.usage.inputTokens,\n outputTokens: streamResult.usage.outputTokens,\n cacheReadTokens: streamResult.usage.cacheReadTokens,\n cacheCreationTokens: streamResult.usage.cacheCreationTokens,\n },\n };\n } else {\n const response = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n } as unknown as MessageCreateParamsNonStreaming) as BetaMessage;\n\n result = extractNonStreamingResult(response, callbacks);\n }\n\n providerFailover.recordSuccess(cfg.activeProvider);\n return result;\n } catch (err) {\n providerFailover.recordFailure(cfg.activeProvider);\n if (attempt < MAX_RETRIES && isRetryableError(err)) {\n const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);\n log.warn({ attempt: attempt + 1, maxRetries: MAX_RETRIES, delayMs: delay, err: sanitizeError(err) }, \"retrying API call\");\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n throw err;\n }\n }\n\n throw new Error(\"Failed to get response after retries\");\n}\n\n// ============================================================================\n// Backward-compatible wrappers (used by existing callers)\n// ============================================================================\n\nexport async function executeStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, true);\n}\n\nexport async function executeNonStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, false);\n}\n\n// ============================================================================\n// Non-streaming response extraction (private helper)\n// ============================================================================\n\nfunction extractNonStreamingResult(response: BetaMessage, callbacks: TurnCallbacks): TurnResult {\n let text = \"\";\n let compactionContent: string | null = null;\n const toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }> = [];\n const citations: CitationBlock[] = [];\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n text += block.text;\n callbacks.onText?.(block.text);\n } else if (block.type === \"tool_use\") {\n toolUseBlocks.push({\n id: block.id,\n name: block.name,\n input: block.input as Record<string, unknown>,\n });\n callbacks.onToolStart?.(block.name, block.input as Record<string, unknown>);\n } else if ((block as any).type === \"cite\") {\n const citeBlock = block as any;\n const citation: CitationBlock = {\n type: \"cite\",\n cited_text: citeBlock.cited_text ?? \"\",\n document_index: citeBlock.document_index ?? 0,\n start_char_index: citeBlock.start_char_index ?? 0,\n end_char_index: citeBlock.end_char_index ?? 0,\n ...(citeBlock.document_title ? { document_title: citeBlock.document_title } : {}),\n };\n citations.push(citation);\n callbacks.onCitation?.(citation);\n } else if ((block as any).type === \"compaction\") {\n compactionContent = (block as any).content || \"\";\n }\n }\n\n return {\n text,\n toolUseBlocks,\n compactionContent,\n stopReason: response.stop_reason || \"end_turn\",\n citations,\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n cacheReadTokens: response.usage?.cache_read_input_tokens ?? 0,\n cacheCreationTokens: response.usage?.cache_creation_input_tokens ?? 0,\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"agent-loop-turn.js","names":["categorizeError","isRetryableError","sanitizeError","processStreamWithCallbacks","providerFailover","createLogger","MAX_RETRIES","RETRY_BASE_DELAY_MS","RETRY_MAX_DELAY_MS","RETRY_OVERLOADED_MULTIPLIER","MODEL_FALLBACK_AFTER_ATTEMPT","MODEL_FALLBACK_MAP","log","executeTurn","cfg","callbacks","streaming","betas","ctxMgmt","thinkingCfg","beta","push","documents","length","includes","baseParams","model","activeModel","max_tokens","maxTokens","temperature","thinking","type","system","omitTools","tools","finalToolDefs","anthropicToolChoice","tool_choice","messages","finalMessages","context_management","config","attempt","result","stream","anthropic","create","streamResult","text","toolUseBlocks","compactionContent","stopReason","citations","usage","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","response","extractNonStreamingResult","recordSuccess","activeProvider","err","recordFailure","category","delay","Math","min","pow","fallbackModel","warn","from","to","jitter","random","round","maxRetries","delayMs","Promise","resolve","setTimeout","Error","executeStreamingTurn","executeNonStreamingTurn","block","content","onText","id","name","input","onToolStart","citeBlock","citation","cited_text","document_index","start_char_index","end_char_index","document_title","onCitation","stop_reason","input_tokens","output_tokens","cache_read_input_tokens","cache_creation_input_tokens"],"sources":["../../../src/server/lib/agent-loop-turn.ts"],"sourcesContent":["// agent-loop-turn.ts — Per-turn API call + response processing for the server agent loop.\n//\n// Unified path for streaming and non-streaming API calls.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type {\n BetaTextBlockParam,\n BetaMessageParam,\n BetaToolUnion,\n BetaMessage,\n MessageCreateParamsStreaming,\n MessageCreateParamsNonStreaming,\n} from \"@anthropic-ai/sdk/resources/beta/messages/messages\";\n\nimport { categorizeError, isRetryableError, sanitizeError } from \"../../shared/agent-core.js\";\nimport { processStreamWithCallbacks } from \"../../shared/sse-parser.js\";\nimport type { BetaStreamEvent } from \"../../shared/anthropic-types.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport { providerFailover } from \"./provider-failover.js\";\nimport { createLogger } from \"./logger.js\";\n\nimport { MAX_RETRIES, RETRY_BASE_DELAY_MS, RETRY_MAX_DELAY_MS, RETRY_OVERLOADED_MULTIPLIER, MODEL_FALLBACK_AFTER_ATTEMPT, MODEL_FALLBACK_MAP, type TurnMetrics } from \"./agent-loop-types.js\";\n\nconst log = createLogger(\"agent-loop\");\n\n// ============================================================================\n// Shared turn-level types\n// ============================================================================\n\nexport interface TurnConfig {\n anthropic: Anthropic;\n activeModel: string;\n activeProvider: string;\n maxTokens: number;\n temperature: number;\n system: BetaTextBlockParam[];\n finalToolDefs: object[];\n finalMessages: Anthropic.MessageParam[];\n anthropicToolChoice?: Record<string, unknown>;\n omitTools: boolean;\n ctxMgmt: { betas: string[]; config: unknown };\n thinkingCfg: { thinking: Record<string, unknown>; beta?: string };\n documents?: any[];\n}\n\nexport interface TurnResult {\n text: string;\n toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }>;\n compactionContent: string | null;\n stopReason: string;\n citations: CitationBlock[];\n usage: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreationTokens: number;\n };\n}\n\nexport interface TurnCallbacks {\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onCitation?: (citation: CitationBlock) => void;\n}\n\n// ============================================================================\n// UNIFIED TURN EXECUTION — streaming or non-streaming via single path\n// ============================================================================\n\n/**\n * Execute a single API turn with retry, provider failover, and response normalization.\n * Handles both streaming and non-streaming modes through a unified code path.\n */\nexport async function executeTurn(\n cfg: TurnConfig,\n callbacks: TurnCallbacks,\n streaming: boolean,\n): Promise<TurnResult> {\n // Build betas list (shared between both modes)\n const betas = [...cfg.ctxMgmt.betas];\n if (cfg.thinkingCfg.beta) betas.push(cfg.thinkingCfg.beta);\n if (cfg.documents?.length && !betas.includes(\"citations-2025-04-15\")) {\n betas.push(\"citations-2025-04-15\");\n }\n\n // Build shared API params\n const baseParams = {\n model: cfg.activeModel,\n max_tokens: cfg.maxTokens,\n temperature: cfg.thinkingCfg.thinking.type !== \"disabled\" ? 1 : cfg.temperature,\n system: cfg.system,\n ...(cfg.omitTools ? {} : { tools: cfg.finalToolDefs as unknown as BetaToolUnion[] }),\n ...(cfg.anthropicToolChoice && !cfg.omitTools ? { tool_choice: cfg.anthropicToolChoice } : {}),\n messages: cfg.finalMessages as unknown as BetaMessageParam[],\n thinking: cfg.thinkingCfg.thinking,\n betas,\n context_management: cfg.ctxMgmt.config,\n ...(cfg.documents?.length ? { documents: cfg.documents } : {}),\n };\n\n // Retry loop (shared between both modes)\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n let result: TurnResult;\n\n if (streaming) {\n const stream = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n stream: true,\n } as unknown as MessageCreateParamsStreaming);\n\n const streamResult = await processStreamWithCallbacks(\n stream as unknown as AsyncIterable<BetaStreamEvent>,\n callbacks,\n );\n\n result = {\n text: streamResult.text,\n toolUseBlocks: streamResult.toolUseBlocks,\n compactionContent: streamResult.compactionContent,\n stopReason: streamResult.stopReason || \"end_turn\",\n citations: streamResult.citations,\n usage: {\n inputTokens: streamResult.usage.inputTokens,\n outputTokens: streamResult.usage.outputTokens,\n cacheReadTokens: streamResult.usage.cacheReadTokens,\n cacheCreationTokens: streamResult.usage.cacheCreationTokens,\n },\n };\n } else {\n const response = await cfg.anthropic.beta.messages.create({\n ...baseParams,\n } as unknown as MessageCreateParamsNonStreaming) as BetaMessage;\n\n result = extractNonStreamingResult(response, callbacks);\n }\n\n providerFailover.recordSuccess(cfg.activeProvider);\n return result;\n } catch (err) {\n providerFailover.recordFailure(cfg.activeProvider);\n if (attempt < MAX_RETRIES && isRetryableError(err)) {\n const { category } = categorizeError(err);\n let delay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt), RETRY_MAX_DELAY_MS);\n\n // Overloaded/rate-limited errors need much longer backoff\n if (category === \"PROVIDER_DOWN\" || category === \"RATE_LIMIT\") {\n delay = Math.min(delay * RETRY_OVERLOADED_MULTIPLIER, RETRY_MAX_DELAY_MS);\n\n // Model fallback: after N failed attempts on an overloaded model,\n // downgrade to a lower-tier model (e.g. Opus → Sonnet) for remaining retries.\n // Sonnet has much more capacity and is almost never overloaded.\n if (attempt >= MODEL_FALLBACK_AFTER_ATTEMPT) {\n const fallbackModel = MODEL_FALLBACK_MAP[baseParams.model];\n if (fallbackModel && baseParams.model !== fallbackModel) {\n log.warn({ from: baseParams.model, to: fallbackModel, attempt }, \"model fallback: overloaded, downgrading\");\n baseParams.model = fallbackModel;\n // Reset delay — fallback model is likely available immediately\n delay = Math.min(RETRY_BASE_DELAY_MS, delay);\n }\n }\n }\n\n // Add jitter (±25%) to prevent thundering herd\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n delay = Math.round(delay + jitter);\n\n log.warn({ attempt: attempt + 1, maxRetries: MAX_RETRIES, delayMs: delay, category, err: sanitizeError(err) }, \"retrying API call\");\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n throw err;\n }\n }\n\n throw new Error(\"Failed to get response after retries\");\n}\n\n// ============================================================================\n// Backward-compatible wrappers (used by existing callers)\n// ============================================================================\n\nexport async function executeStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, true);\n}\n\nexport async function executeNonStreamingTurn(cfg: TurnConfig, callbacks: TurnCallbacks): Promise<TurnResult> {\n return executeTurn(cfg, callbacks, false);\n}\n\n// ============================================================================\n// Non-streaming response extraction (private helper)\n// ============================================================================\n\nfunction extractNonStreamingResult(response: BetaMessage, callbacks: TurnCallbacks): TurnResult {\n let text = \"\";\n let compactionContent: string | null = null;\n const toolUseBlocks: Array<{ id: string; name: string; input: Record<string, unknown> }> = [];\n const citations: CitationBlock[] = [];\n\n for (const block of response.content) {\n if (block.type === \"text\") {\n text += block.text;\n callbacks.onText?.(block.text);\n } else if (block.type === \"tool_use\") {\n toolUseBlocks.push({\n id: block.id,\n name: block.name,\n input: block.input as Record<string, unknown>,\n });\n callbacks.onToolStart?.(block.name, block.input as Record<string, unknown>);\n } else if ((block as any).type === \"cite\") {\n const citeBlock = block as any;\n const citation: CitationBlock = {\n type: \"cite\",\n cited_text: citeBlock.cited_text ?? \"\",\n document_index: citeBlock.document_index ?? 0,\n start_char_index: citeBlock.start_char_index ?? 0,\n end_char_index: citeBlock.end_char_index ?? 0,\n ...(citeBlock.document_title ? { document_title: citeBlock.document_title } : {}),\n };\n citations.push(citation);\n callbacks.onCitation?.(citation);\n } else if ((block as any).type === \"compaction\") {\n compactionContent = (block as any).content || \"\";\n }\n }\n\n return {\n text,\n toolUseBlocks,\n compactionContent,\n stopReason: response.stop_reason || \"end_turn\",\n citations,\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n cacheReadTokens: response.usage?.cache_read_input_tokens ?? 0,\n cacheCreationTokens: response.usage?.cache_creation_input_tokens ?? 0,\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -105,9 +105,13 @@ export interface ServerAgentLoopResult {
105
105
  }
106
106
  export type { SubagentProgressCallback, SubagentProgressEvent };
107
107
  export type { ToolChoice };
108
- export declare const MAX_RETRIES = 3;
109
- export declare const RETRY_BASE_DELAY_MS = 1000;
108
+ export declare const MAX_RETRIES = 5;
109
+ export declare const RETRY_BASE_DELAY_MS = 2000;
110
+ export declare const RETRY_MAX_DELAY_MS = 60000;
111
+ export declare const RETRY_OVERLOADED_MULTIPLIER = 3;
112
+ export declare const MODEL_FALLBACK_AFTER_ATTEMPT = 2;
110
113
  export declare const DEFAULT_MAX_CONCURRENT_TOOLS = 7;
114
+ export declare const MODEL_FALLBACK_MAP: Record<string, string>;
111
115
  /**
112
116
  * Map ToolChoice to Anthropic API `tool_choice` format.
113
117
  * Returns undefined if tools should be omitted entirely.
@@ -10,10 +10,22 @@
10
10
  // CONSTANTS
11
11
  // ============================================================================
12
12
 
13
- export const MAX_RETRIES = 3;
14
- export const RETRY_BASE_DELAY_MS = 1000;
13
+ export const MAX_RETRIES = 5;
14
+ export const RETRY_BASE_DELAY_MS = 2000;
15
+ export const RETRY_MAX_DELAY_MS = 60000;
16
+ export const RETRY_OVERLOADED_MULTIPLIER = 3;
17
+ export const MODEL_FALLBACK_AFTER_ATTEMPT = 2;
15
18
  export const DEFAULT_MAX_CONCURRENT_TOOLS = 7;
16
19
 
20
+ // Intra-provider model fallback — when a model is overloaded, try a lower-tier model
21
+ // from the same provider instead of retrying the same overloaded endpoint.
22
+ export const MODEL_FALLBACK_MAP = {
23
+ "claude-opus-4-6": "claude-sonnet-4-6",
24
+ "claude-opus-4-5-20251101": "claude-sonnet-4-5-20250929",
25
+ "claude-opus-4-20250514": "claude-sonnet-4-20250514",
26
+ "claude-opus-4-1-20250805": "claude-sonnet-4-5-20250929"
27
+ };
28
+
17
29
  // ============================================================================
18
30
  // TOOL CHOICE MAPPING — convert ToolChoice to provider-specific format
19
31
  // ============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop-types.js","names":["MAX_RETRIES","RETRY_BASE_DELAY_MS","DEFAULT_MAX_CONCURRENT_TOOLS","mapToolChoiceForAnthropic","tc","toolChoice","type","omitTools","name"],"sources":["../../../src/server/lib/agent-loop-types.ts"],"sourcesContent":["// agent-loop-types.ts — Shared types, constants, and helpers for the server agent loop.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type {\n ContextManagementOverrides,\n LoopDetectorConfig,\n ToolChoice,\n} from \"../../shared/agent-core.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport type { ToolProgressCallback } from \"../tool-router.js\";\nimport type {\n SubagentProgressCallback,\n SubagentProgressEvent,\n} from \"./server-subagent.js\";\nimport type { RequestCapabilityRequirements } from \"./provider-capabilities.js\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface ToolDef {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\nexport type ToolResult = { success: boolean; data?: unknown; error?: string };\n\nexport interface ServerAgentLoopOptions {\n // Core\n anthropic: Anthropic;\n supabase: SupabaseClient;\n model: string;\n systemPrompt: string;\n messages: Anthropic.MessageParam[];\n tools: ToolDef[];\n\n // Behavior\n maxTurns: number;\n temperature: number;\n maxTokens?: number;\n /** @deprecated — Anthropic context_management handles limits. Ignored. */\n maxToolResultChars?: number;\n\n // Context (audit + subagent)\n storeId?: string;\n traceId?: string;\n userId?: string | null;\n userEmail?: string | null;\n source?: string;\n conversationId?: string;\n agentId?: string;\n\n // Tool execution — injected, avoids circular deps\n executeTool: (\n toolName: string,\n args: Record<string, unknown>,\n sourceOverride?: string,\n onToolProgress?: ToolProgressCallback,\n ) => Promise<ToolResult>;\n\n // Tool progress streaming (e.g. kali exec stdout/stderr)\n onToolProgress?: ToolProgressCallback;\n\n // Lazy tool loading — extended tools available via discover_tools\n extendedTools?: ToolDef[];\n\n // Tool choice control\n toolChoice?: ToolChoice; // Default: \"auto\" (smart resolution per turn)\n\n // Feature flags\n enableDelegation?: boolean; // Default: true (auto-inject for Opus)\n enablePromptCaching?: boolean; // Default: true\n enableStreaming?: boolean; // Default: true\n enableModelRouting?: boolean; // Default: true (controls routeModel on turn 1)\n maxConcurrentTools?: number; // Default: 7\n\n // Citations — source documents for Anthropic Citations API (opt-in)\n documents?: any[];\n\n // Callbacks (all optional)\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onToolResult?: (name: string, success: boolean, result: unknown) => void;\n onCitation?: (citation: CitationBlock) => void;\n onSubagentProgress?: SubagentProgressCallback;\n\n // Subagent overrides (from parent's context_config)\n subagentMaxTokens?: number; // Default: 8192\n subagentMaxTurns?: number; // Default: 6\n subagentTemperature?: number; // Default: 0.3\n\n // Budget control\n maxCostUsd?: number; // Default: DEFAULT_SESSION_COST_BUDGET_USD ($5)\n\n // Abort control\n clientDisconnected?: { value: boolean };\n startedAt?: number;\n maxDurationMs?: number;\n\n // Capability-aware routing — when set, failover skips providers that lack these capabilities\n requiredCapabilities?: RequestCapabilityRequirements;\n\n // Context management overrides from ai_agent_config.context_config JSONB\n contextOverrides?: ContextManagementOverrides;\n\n // Loop detector thresholds from ai_agent_config.context_config JSONB\n loopDetectorConfig?: Partial<LoopDetectorConfig>;\n /** Max errors from a single parallel batch before aborting remaining calls */\n batchErrorLimit?: number;\n}\n\nexport interface TurnMetrics {\n turn: number;\n inputTokens: number;\n outputTokens: number;\n cacheRead: number;\n cacheCreation: number;\n toolsUsed: string[];\n costUsd: number;\n /** Set when a failover occurred this turn */\n failover?: {\n originalProvider: string;\n activeProvider: string;\n model: string;\n };\n}\n\nexport interface ServerAgentLoopResult {\n finalText: string;\n allTextResponses: string[];\n turnCount: number;\n toolCallCount: number;\n toolsUsed: string[];\n /** Citations collected from all turns (only populated when documents are provided) */\n citations: CitationBlock[];\n tokens: {\n input: number;\n output: number;\n cacheCreation: number;\n cacheRead: number;\n };\n costUsd: number;\n loopDetectorStats: {\n totalErrors: number;\n failedStrategies: number;\n consecutiveFailedTurns: number;\n };\n turns: TurnMetrics[];\n /** Final stop reason from the last API response (end_turn, tool_use, max_tokens) */\n stopReason: string;\n}\n\n// Re-export for consumers\nexport type { SubagentProgressCallback, SubagentProgressEvent };\nexport type { ToolChoice };\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const MAX_RETRIES = 3;\nexport const RETRY_BASE_DELAY_MS = 1000;\nexport const DEFAULT_MAX_CONCURRENT_TOOLS = 7;\n\n// ============================================================================\n// TOOL CHOICE MAPPING — convert ToolChoice to provider-specific format\n// ============================================================================\n\n/**\n * Map ToolChoice to Anthropic API `tool_choice` format.\n * Returns undefined if tools should be omitted entirely.\n */\nexport function mapToolChoiceForAnthropic(\n tc: ToolChoice,\n): { toolChoice?: Record<string, unknown>; omitTools?: boolean } {\n if (tc === \"auto\") return { toolChoice: { type: \"auto\" } };\n if (tc === \"any\") return { toolChoice: { type: \"any\" } };\n if (tc === \"none\") return { omitTools: true };\n if (typeof tc === \"object\" && tc.type === \"tool\") {\n return { toolChoice: { type: \"tool\", name: tc.name } };\n }\n return { toolChoice: { type: \"auto\" } };\n}\n"],"mappings":"AAAA;;AAiBA;AACA;AACA;;AAuIA;;AAIA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,GAAG,CAAC;AAC5B,OAAO,MAAMC,mBAAmB,GAAG,IAAI;AACvC,OAAO,MAAMC,4BAA4B,GAAG,CAAC;;AAE7C;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CACvCC,EAAc,EACiD;EAC/D,IAAIA,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;EAC1D,IAAIF,EAAE,KAAK,KAAK,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAM;EAAE,CAAC;EACxD,IAAIF,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEG,SAAS,EAAE;EAAK,CAAC;EAC7C,IAAI,OAAOH,EAAE,KAAK,QAAQ,IAAIA,EAAE,CAACE,IAAI,KAAK,MAAM,EAAE;IAChD,OAAO;MAAED,UAAU,EAAE;QAAEC,IAAI,EAAE,MAAM;QAAEE,IAAI,EAAEJ,EAAE,CAACI;MAAK;IAAE,CAAC;EACxD;EACA,OAAO;IAAEH,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;AACzC","ignoreList":[]}
1
+ {"version":3,"file":"agent-loop-types.js","names":["MAX_RETRIES","RETRY_BASE_DELAY_MS","RETRY_MAX_DELAY_MS","RETRY_OVERLOADED_MULTIPLIER","MODEL_FALLBACK_AFTER_ATTEMPT","DEFAULT_MAX_CONCURRENT_TOOLS","MODEL_FALLBACK_MAP","mapToolChoiceForAnthropic","tc","toolChoice","type","omitTools","name"],"sources":["../../../src/server/lib/agent-loop-types.ts"],"sourcesContent":["// agent-loop-types.ts — Shared types, constants, and helpers for the server agent loop.\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type {\n ContextManagementOverrides,\n LoopDetectorConfig,\n ToolChoice,\n} from \"../../shared/agent-core.js\";\nimport type { CitationBlock } from \"../../shared/types.js\";\nimport type { ToolProgressCallback } from \"../tool-router.js\";\nimport type {\n SubagentProgressCallback,\n SubagentProgressEvent,\n} from \"./server-subagent.js\";\nimport type { RequestCapabilityRequirements } from \"./provider-capabilities.js\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface ToolDef {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\nexport type ToolResult = { success: boolean; data?: unknown; error?: string };\n\nexport interface ServerAgentLoopOptions {\n // Core\n anthropic: Anthropic;\n supabase: SupabaseClient;\n model: string;\n systemPrompt: string;\n messages: Anthropic.MessageParam[];\n tools: ToolDef[];\n\n // Behavior\n maxTurns: number;\n temperature: number;\n maxTokens?: number;\n /** @deprecated — Anthropic context_management handles limits. Ignored. */\n maxToolResultChars?: number;\n\n // Context (audit + subagent)\n storeId?: string;\n traceId?: string;\n userId?: string | null;\n userEmail?: string | null;\n source?: string;\n conversationId?: string;\n agentId?: string;\n\n // Tool execution — injected, avoids circular deps\n executeTool: (\n toolName: string,\n args: Record<string, unknown>,\n sourceOverride?: string,\n onToolProgress?: ToolProgressCallback,\n ) => Promise<ToolResult>;\n\n // Tool progress streaming (e.g. kali exec stdout/stderr)\n onToolProgress?: ToolProgressCallback;\n\n // Lazy tool loading — extended tools available via discover_tools\n extendedTools?: ToolDef[];\n\n // Tool choice control\n toolChoice?: ToolChoice; // Default: \"auto\" (smart resolution per turn)\n\n // Feature flags\n enableDelegation?: boolean; // Default: true (auto-inject for Opus)\n enablePromptCaching?: boolean; // Default: true\n enableStreaming?: boolean; // Default: true\n enableModelRouting?: boolean; // Default: true (controls routeModel on turn 1)\n maxConcurrentTools?: number; // Default: 7\n\n // Citations — source documents for Anthropic Citations API (opt-in)\n documents?: any[];\n\n // Callbacks (all optional)\n onText?: (text: string) => void;\n onToolStart?: (name: string, input?: Record<string, unknown>) => void;\n onToolResult?: (name: string, success: boolean, result: unknown) => void;\n onCitation?: (citation: CitationBlock) => void;\n onSubagentProgress?: SubagentProgressCallback;\n\n // Subagent overrides (from parent's context_config)\n subagentMaxTokens?: number; // Default: 8192\n subagentMaxTurns?: number; // Default: 6\n subagentTemperature?: number; // Default: 0.3\n\n // Budget control\n maxCostUsd?: number; // Default: DEFAULT_SESSION_COST_BUDGET_USD ($5)\n\n // Abort control\n clientDisconnected?: { value: boolean };\n startedAt?: number;\n maxDurationMs?: number;\n\n // Capability-aware routing — when set, failover skips providers that lack these capabilities\n requiredCapabilities?: RequestCapabilityRequirements;\n\n // Context management overrides from ai_agent_config.context_config JSONB\n contextOverrides?: ContextManagementOverrides;\n\n // Loop detector thresholds from ai_agent_config.context_config JSONB\n loopDetectorConfig?: Partial<LoopDetectorConfig>;\n /** Max errors from a single parallel batch before aborting remaining calls */\n batchErrorLimit?: number;\n}\n\nexport interface TurnMetrics {\n turn: number;\n inputTokens: number;\n outputTokens: number;\n cacheRead: number;\n cacheCreation: number;\n toolsUsed: string[];\n costUsd: number;\n /** Set when a failover occurred this turn */\n failover?: {\n originalProvider: string;\n activeProvider: string;\n model: string;\n };\n}\n\nexport interface ServerAgentLoopResult {\n finalText: string;\n allTextResponses: string[];\n turnCount: number;\n toolCallCount: number;\n toolsUsed: string[];\n /** Citations collected from all turns (only populated when documents are provided) */\n citations: CitationBlock[];\n tokens: {\n input: number;\n output: number;\n cacheCreation: number;\n cacheRead: number;\n };\n costUsd: number;\n loopDetectorStats: {\n totalErrors: number;\n failedStrategies: number;\n consecutiveFailedTurns: number;\n };\n turns: TurnMetrics[];\n /** Final stop reason from the last API response (end_turn, tool_use, max_tokens) */\n stopReason: string;\n}\n\n// Re-export for consumers\nexport type { SubagentProgressCallback, SubagentProgressEvent };\nexport type { ToolChoice };\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const MAX_RETRIES = 5;\nexport const RETRY_BASE_DELAY_MS = 2000;\nexport const RETRY_MAX_DELAY_MS = 60000;\nexport const RETRY_OVERLOADED_MULTIPLIER = 3;\nexport const MODEL_FALLBACK_AFTER_ATTEMPT = 2;\nexport const DEFAULT_MAX_CONCURRENT_TOOLS = 7;\n\n// Intra-provider model fallback — when a model is overloaded, try a lower-tier model\n// from the same provider instead of retrying the same overloaded endpoint.\nexport const MODEL_FALLBACK_MAP: Record<string, string> = {\n \"claude-opus-4-6\": \"claude-sonnet-4-6\",\n \"claude-opus-4-5-20251101\": \"claude-sonnet-4-5-20250929\",\n \"claude-opus-4-20250514\": \"claude-sonnet-4-20250514\",\n \"claude-opus-4-1-20250805\": \"claude-sonnet-4-5-20250929\",\n};\n\n// ============================================================================\n// TOOL CHOICE MAPPING — convert ToolChoice to provider-specific format\n// ============================================================================\n\n/**\n * Map ToolChoice to Anthropic API `tool_choice` format.\n * Returns undefined if tools should be omitted entirely.\n */\nexport function mapToolChoiceForAnthropic(\n tc: ToolChoice,\n): { toolChoice?: Record<string, unknown>; omitTools?: boolean } {\n if (tc === \"auto\") return { toolChoice: { type: \"auto\" } };\n if (tc === \"any\") return { toolChoice: { type: \"any\" } };\n if (tc === \"none\") return { omitTools: true };\n if (typeof tc === \"object\" && tc.type === \"tool\") {\n return { toolChoice: { type: \"tool\", name: tc.name } };\n }\n return { toolChoice: { type: \"auto\" } };\n}\n"],"mappings":"AAAA;;AAiBA;AACA;AACA;;AAuIA;;AAIA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,GAAG,CAAC;AAC5B,OAAO,MAAMC,mBAAmB,GAAG,IAAI;AACvC,OAAO,MAAMC,kBAAkB,GAAG,KAAK;AACvC,OAAO,MAAMC,2BAA2B,GAAG,CAAC;AAC5C,OAAO,MAAMC,4BAA4B,GAAG,CAAC;AAC7C,OAAO,MAAMC,4BAA4B,GAAG,CAAC;;AAE7C;AACA;AACA,OAAO,MAAMC,kBAA0C,GAAG;EACxD,iBAAiB,EAAE,mBAAmB;EACtC,0BAA0B,EAAE,4BAA4B;EACxD,wBAAwB,EAAE,0BAA0B;EACpD,0BAA0B,EAAE;AAC9B,CAAC;;AAED;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CACvCC,EAAc,EACiD;EAC/D,IAAIA,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;EAC1D,IAAIF,EAAE,KAAK,KAAK,EAAE,OAAO;IAAEC,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAM;EAAE,CAAC;EACxD,IAAIF,EAAE,KAAK,MAAM,EAAE,OAAO;IAAEG,SAAS,EAAE;EAAK,CAAC;EAC7C,IAAI,OAAOH,EAAE,KAAK,QAAQ,IAAIA,EAAE,CAACE,IAAI,KAAK,MAAM,EAAE;IAChD,OAAO;MAAED,UAAU,EAAE;QAAEC,IAAI,EAAE,MAAM;QAAEE,IAAI,EAAEJ,EAAE,CAACI;MAAK;IAAE,CAAC;EACxD;EACA,OAAO;IAAEH,UAAU,EAAE;MAAEC,IAAI,EAAE;IAAO;EAAE,CAAC;AACzC","ignoreList":[]}
@@ -57,7 +57,8 @@ class ClickHouseClient {
57
57
  ...this.authHeaders,
58
58
  "Content-Type": "application/json"
59
59
  },
60
- body
60
+ body,
61
+ signal: AbortSignal.timeout(10_000)
61
62
  });
62
63
  if (!response.ok) {
63
64
  const text = await response.text();
@@ -80,7 +81,8 @@ class ClickHouseClient {
80
81
  ...this.authHeaders,
81
82
  "Content-Type": "text/plain"
82
83
  },
83
- body: sql
84
+ body: sql,
85
+ signal: AbortSignal.timeout(10_000)
84
86
  });
85
87
  if (!response.ok) {
86
88
  const text = await response.text();
@@ -1 +1 @@
1
- {"version":3,"file":"clickhouse-client.js","names":["ClickHouseClient","constructor","host","process","env","CLICKHOUSE_HOST","user","CLICKHOUSE_USER","password","CLICKHOUSE_PASSWORD","database","CLICKHOUSE_DATABASE","enabled","CLICKHOUSE_ENABLED","isEnabled","baseUrl","authHeaders","credentials","Buffer","from","toString","Authorization","insert","table","rows","length","body","map","row","JSON","stringify","join","query","response","fetch","encodeURIComponent","method","headers","ok","text","console","error","status","slice","err","message","sql","trim","split","line","parse","queryOne","_client","getClickHouseClient","insertSpan","span","client","catch","insertError"],"sources":["../../../src/server/lib/clickhouse-client.ts"],"sourcesContent":["/**\n * ClickHouse HTTP Client for Node.js MCP Server\n *\n * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).\n * Uses the ClickHouse HTTP interface with JSONEachRow format.\n *\n * Environment variables:\n * CLICKHOUSE_HOST — e.g. \"xxx.us-east-1.aws.clickhouse.cloud\"\n * CLICKHOUSE_USER — default \"default\"\n * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console\n * CLICKHOUSE_DATABASE — default \"default\"\n * CLICKHOUSE_ENABLED — \"true\" to enable (graceful no-op otherwise)\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ClickHouseSpan {\n span_id: string;\n trace_id: string;\n parent_span_id?: string;\n store_id?: string;\n service_name: string;\n operation_name?: string;\n span_kind?: string;\n source?: string;\n started_at: string; // ISO 8601\n ended_at: string;\n duration_ms: number;\n status_code?: string;\n severity?: string;\n http_status?: number;\n http_method?: string;\n http_path?: string;\n model_name?: string;\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n token_cost_usd?: number;\n agent_id?: string;\n conversation_id?: string;\n tool_name?: string;\n error_message?: string;\n attributes?: string; // JSON string\n events?: string; // JSON string\n request_id?: string;\n user_id?: string;\n environment?: string;\n // Enriched columns (003_enrich_spans migration)\n user_email?: string;\n error_type?: string;\n retryable?: number; // UInt8: 0 or 1\n resource_type?: string;\n cache_read_tokens?: number;\n cache_creation_tokens?: number;\n input_bytes?: number;\n output_bytes?: number;\n stop_reason?: string;\n turn_number?: number;\n parent_conversation_id?: string;\n}\n\nexport interface ClickHouseError {\n fingerprint: string;\n severity?: string;\n error_type: string;\n error_message: string;\n error_code?: string;\n stack_trace?: string;\n source_file?: string;\n source_line?: number;\n source_function?: string;\n platform?: string;\n service_name: string;\n service_version?: string;\n environment?: string;\n store_id?: string;\n user_id?: string;\n user_email?: string;\n trace_id?: string;\n span_id?: string;\n request_id?: string;\n tags?: string; // JSON string\n extra?: string; // JSON string\n breadcrumbs?: string; // JSON string\n device_info?: string; // JSON string\n runtime_info?: string; // JSON string\n occurred_at: string; // ISO 8601\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\nclass ClickHouseClient {\n private host: string;\n private user: string;\n private password: string;\n private database: string;\n private enabled: boolean;\n\n constructor() {\n this.host = process.env.CLICKHOUSE_HOST || \"\";\n this.user = process.env.CLICKHOUSE_USER || \"default\";\n this.password = process.env.CLICKHOUSE_PASSWORD || \"\";\n this.database = process.env.CLICKHOUSE_DATABASE || \"default\";\n this.enabled = process.env.CLICKHOUSE_ENABLED === \"true\";\n }\n\n get isEnabled(): boolean {\n return this.enabled && !!this.host;\n }\n\n private get baseUrl(): string {\n return `https://${this.host}:8443`;\n }\n\n private get authHeaders(): Record<string, string> {\n const credentials = Buffer.from(`${this.user}:${this.password}`).toString(\"base64\");\n return {\n Authorization: `Basic ${credentials}`,\n \"X-ClickHouse-Database\": this.database,\n };\n }\n\n /**\n * Insert rows into a ClickHouse table using JSONEachRow format.\n * Fire-and-forget — logs errors but never throws.\n */\n async insert(table: string, rows: Record<string, unknown>[]): Promise<void> {\n if (!this.isEnabled || rows.length === 0) return;\n\n const body = rows.map((row) => JSON.stringify(row)).join(\"\\n\");\n const query = `INSERT INTO ${table} FORMAT JSONEachRow`;\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?query=${encodeURIComponent(query)}`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n },\n body,\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] insert ${table} failed (${response.status}): ${text.slice(0, 200)}`);\n }\n } catch (err) {\n console.error(`[clickhouse] insert ${table} error: ${(err as Error).message}`);\n }\n }\n\n /**\n * Execute a SELECT query and return parsed JSON rows.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<T[]> {\n if (!this.isEnabled) return [];\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?default_format=JSONEachRow`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"text/plain\",\n },\n body: sql,\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] query failed (${response.status}): ${text.slice(0, 200)}`);\n return [];\n }\n\n const text = await response.text();\n if (!text.trim()) return [];\n\n return text\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as T);\n } catch (err) {\n console.error(`[clickhouse] query error: ${(err as Error).message}`);\n return [];\n }\n }\n\n /**\n * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).\n * Useful for analytics RPCs that return one row with a complex JSON object.\n */\n async queryOne<T = Record<string, unknown>>(sql: string): Promise<T | null> {\n const rows = await this.query<T>(sql);\n return rows.length > 0 ? rows[0] : null;\n }\n}\n\n// Singleton — lazy init on first access\nlet _client: ClickHouseClient | null = null;\n\nexport function getClickHouseClient(): ClickHouseClient {\n if (!_client) {\n _client = new ClickHouseClient();\n }\n return _client;\n}\n\n// ============================================================================\n// Convenience: Insert a span\n// ============================================================================\n\nexport function insertSpan(span: ClickHouseSpan): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"ai_spans\", [span as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertSpan failed: ${(err as Error).message}`);\n });\n}\n\n// ============================================================================\n// Convenience: Insert an error event\n// ============================================================================\n\nexport function insertError(error: ClickHouseError): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"error_events\", [error as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertError failed: ${(err as Error).message}`);\n });\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"clickhouse-client.js","names":["ClickHouseClient","constructor","host","process","env","CLICKHOUSE_HOST","user","CLICKHOUSE_USER","password","CLICKHOUSE_PASSWORD","database","CLICKHOUSE_DATABASE","enabled","CLICKHOUSE_ENABLED","isEnabled","baseUrl","authHeaders","credentials","Buffer","from","toString","Authorization","insert","table","rows","length","body","map","row","JSON","stringify","join","query","response","fetch","encodeURIComponent","method","headers","signal","AbortSignal","timeout","ok","text","console","error","status","slice","err","message","sql","trim","split","line","parse","queryOne","_client","getClickHouseClient","insertSpan","span","client","catch","insertError"],"sources":["../../../src/server/lib/clickhouse-client.ts"],"sourcesContent":["/**\n * ClickHouse HTTP Client for Node.js MCP Server\n *\n * Ported from the Deno edge function client (supabase/functions/_shared/clickhouse.ts).\n * Uses the ClickHouse HTTP interface with JSONEachRow format.\n *\n * Environment variables:\n * CLICKHOUSE_HOST — e.g. \"xxx.us-east-1.aws.clickhouse.cloud\"\n * CLICKHOUSE_USER — default \"default\"\n * CLICKHOUSE_PASSWORD — from ClickHouse Cloud console\n * CLICKHOUSE_DATABASE — default \"default\"\n * CLICKHOUSE_ENABLED — \"true\" to enable (graceful no-op otherwise)\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ClickHouseSpan {\n span_id: string;\n trace_id: string;\n parent_span_id?: string;\n store_id?: string;\n service_name: string;\n operation_name?: string;\n span_kind?: string;\n source?: string;\n started_at: string; // ISO 8601\n ended_at: string;\n duration_ms: number;\n status_code?: string;\n severity?: string;\n http_status?: number;\n http_method?: string;\n http_path?: string;\n model_name?: string;\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n token_cost_usd?: number;\n agent_id?: string;\n conversation_id?: string;\n tool_name?: string;\n error_message?: string;\n attributes?: string; // JSON string\n events?: string; // JSON string\n request_id?: string;\n user_id?: string;\n environment?: string;\n // Enriched columns (003_enrich_spans migration)\n user_email?: string;\n error_type?: string;\n retryable?: number; // UInt8: 0 or 1\n resource_type?: string;\n cache_read_tokens?: number;\n cache_creation_tokens?: number;\n input_bytes?: number;\n output_bytes?: number;\n stop_reason?: string;\n turn_number?: number;\n parent_conversation_id?: string;\n}\n\nexport interface ClickHouseError {\n fingerprint: string;\n severity?: string;\n error_type: string;\n error_message: string;\n error_code?: string;\n stack_trace?: string;\n source_file?: string;\n source_line?: number;\n source_function?: string;\n platform?: string;\n service_name: string;\n service_version?: string;\n environment?: string;\n store_id?: string;\n user_id?: string;\n user_email?: string;\n trace_id?: string;\n span_id?: string;\n request_id?: string;\n tags?: string; // JSON string\n extra?: string; // JSON string\n breadcrumbs?: string; // JSON string\n device_info?: string; // JSON string\n runtime_info?: string; // JSON string\n occurred_at: string; // ISO 8601\n}\n\n// ============================================================================\n// Client\n// ============================================================================\n\nclass ClickHouseClient {\n private host: string;\n private user: string;\n private password: string;\n private database: string;\n private enabled: boolean;\n\n constructor() {\n this.host = process.env.CLICKHOUSE_HOST || \"\";\n this.user = process.env.CLICKHOUSE_USER || \"default\";\n this.password = process.env.CLICKHOUSE_PASSWORD || \"\";\n this.database = process.env.CLICKHOUSE_DATABASE || \"default\";\n this.enabled = process.env.CLICKHOUSE_ENABLED === \"true\";\n }\n\n get isEnabled(): boolean {\n return this.enabled && !!this.host;\n }\n\n private get baseUrl(): string {\n return `https://${this.host}:8443`;\n }\n\n private get authHeaders(): Record<string, string> {\n const credentials = Buffer.from(`${this.user}:${this.password}`).toString(\"base64\");\n return {\n Authorization: `Basic ${credentials}`,\n \"X-ClickHouse-Database\": this.database,\n };\n }\n\n /**\n * Insert rows into a ClickHouse table using JSONEachRow format.\n * Fire-and-forget — logs errors but never throws.\n */\n async insert(table: string, rows: Record<string, unknown>[]): Promise<void> {\n if (!this.isEnabled || rows.length === 0) return;\n\n const body = rows.map((row) => JSON.stringify(row)).join(\"\\n\");\n const query = `INSERT INTO ${table} FORMAT JSONEachRow`;\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?query=${encodeURIComponent(query)}`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n },\n body,\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] insert ${table} failed (${response.status}): ${text.slice(0, 200)}`);\n }\n } catch (err) {\n console.error(`[clickhouse] insert ${table} error: ${(err as Error).message}`);\n }\n }\n\n /**\n * Execute a SELECT query and return parsed JSON rows.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<T[]> {\n if (!this.isEnabled) return [];\n\n try {\n const response = await fetch(\n `${this.baseUrl}/?default_format=JSONEachRow`,\n {\n method: \"POST\",\n headers: {\n ...this.authHeaders,\n \"Content-Type\": \"text/plain\",\n },\n body: sql,\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n console.error(`[clickhouse] query failed (${response.status}): ${text.slice(0, 200)}`);\n return [];\n }\n\n const text = await response.text();\n if (!text.trim()) return [];\n\n return text\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as T);\n } catch (err) {\n console.error(`[clickhouse] query error: ${(err as Error).message}`);\n return [];\n }\n }\n\n /**\n * Execute a query and return a single aggregated result (e.g. JSONB_BUILD_OBJECT).\n * Useful for analytics RPCs that return one row with a complex JSON object.\n */\n async queryOne<T = Record<string, unknown>>(sql: string): Promise<T | null> {\n const rows = await this.query<T>(sql);\n return rows.length > 0 ? rows[0] : null;\n }\n}\n\n// Singleton — lazy init on first access\nlet _client: ClickHouseClient | null = null;\n\nexport function getClickHouseClient(): ClickHouseClient {\n if (!_client) {\n _client = new ClickHouseClient();\n }\n return _client;\n}\n\n// ============================================================================\n// Convenience: Insert a span\n// ============================================================================\n\nexport function insertSpan(span: ClickHouseSpan): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"ai_spans\", [span as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertSpan failed: ${(err as Error).message}`);\n });\n}\n\n// ============================================================================\n// Convenience: Insert an error event\n// ============================================================================\n\nexport function insertError(error: ClickHouseError): void {\n const client = getClickHouseClient();\n if (!client.isEnabled) return;\n\n client.insert(\"error_events\", [error as unknown as Record<string, unknown>]).catch((err) => {\n console.error(`[clickhouse] insertError failed: ${(err as Error).message}`);\n });\n}\n"],"mappings":"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","ignoreList":[]}
@@ -55,7 +55,10 @@ if (process.send) {
55
55
  const logs = [];
56
56
 
57
57
  // Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)
58
- const dangerousPatterns = [/constructor\s*\[/i, /constructor\s*\(/i, /\.constructor/i, /__proto__/i, /prototype\s*\[/i, /\bprocess\b/, /\brequire\b/, /\bimport\b/, /\bglobalThis\b/, /\bglobal\b/, /\bFunction\b/, /\beval\b/, /\bReflect\b/, /\bProxy\b/, /\bSymbol\b/, /\bWeakRef\b/];
58
+ const dangerousPatterns = [/constructor\s*\[/i, /constructor\s*\(/i, /\.constructor/i, /\["constructor"\]/i, /\['constructor'\]/i, /\[`constructor`\]/i, /__proto__/i, /prototype\s*\[/i, /\bprocess\b/, /\brequire\b/, /\bimport\b/, /\bglobalThis\b/, /\bglobal\b/, /\bFunction\b/, /\beval\b/, /\bReflect\b/, /\bProxy\b/, /\bSymbol\b/, /\bWeakRef\b/, /\[\s*['"`]__proto__['"`]\s*\]/i, /\[\s*['"`]prototype['"`]\s*\]/i, /\bthis\s*\[/,
59
+ // bracket notation on this
60
+ /\barguments\b/ // arguments object can leak caller
61
+ ];
59
62
  for (const pattern of dangerousPatterns) {
60
63
  if (pattern.test(msg.code)) {
61
64
  const result = {
@@ -1 +1 @@
1
- {"version":3,"file":"code-worker.js","names":["runInNewContext","randomUUID","safeFunction","fn","safe","args","apply","Object","defineProperty","value","undefined","writable","configurable","freeze","safeNamespace","source","keys","ns","create","key","val","process","send","on","msg","type","logs","dangerousPatterns","pattern","test","code","result","success","error","sandbox","steps","JSON","parse","stringify","context","trigger","input","output","logFn","push","map","String","join","consoleObj","log","console","mathKeys","Math","dateNs","now","Date","parseInt","parseFloat","isNaN","isFinite","encodeURIComponent","decodeURIComponent","urlNs","u","URL","href","protocol","host","hostname","port","pathname","search","hash","origin","searchParams","fromEntries","format","parts","bufferNs","from","Buffer","alloc","size","cryptoNs","crypto","require","global","globalThis","Function","eval","setTimeout","setInterval","Reflect","Proxy","Symbol","WeakRef","timeout","timeoutMs","filename","breakOnSigint","microtaskMode","workerResult","err","message"],"sources":["../../../src/server/lib/code-worker.ts"],"sourcesContent":["// server/lib/code-worker.ts — Isolated code execution worker\n// Runs as a child process via fork(). Receives code + context via IPC,\n// executes in a fresh vm context, returns result. Crash here cannot take down the server.\n\nimport { runInNewContext } from \"node:vm\";\nimport { randomUUID } from \"node:crypto\";\n\ninterface WorkerMessage {\n type: \"execute\";\n code: string;\n context: {\n steps: Record<string, unknown>;\n trigger: Record<string, unknown>;\n input?: unknown;\n };\n timeoutMs: number;\n}\n\ninterface WorkerResult {\n type: \"result\";\n success: boolean;\n output?: unknown;\n logs?: string[];\n error?: string;\n}\n\n/**\n * Create a safe wrapper function that cannot be used to reach the host Function constructor.\n * Wraps a host function in a proxy whose prototype chain is severed — .constructor returns\n * undefined instead of the host Function constructor.\n */\nfunction safeFunction<T extends (...args: any[]) => any>(fn: T): T {\n // Create a wrapper function whose .constructor and .prototype are severed.\n // We can't make a callable null-prototype object directly, so we override them on a regular function.\n const safe = function (this: unknown, ...args: unknown[]) {\n return fn.apply(this, args);\n };\n Object.defineProperty(safe, \"constructor\", { value: undefined, writable: false, configurable: false });\n Object.defineProperty(safe, \"prototype\", { value: undefined, writable: false, configurable: false });\n // Freeze to prevent re-assignment of constructor\n Object.freeze(safe);\n return safe as unknown as T;\n}\n\n/**\n * Create a safe namespace object (like Math, JSON, Buffer) whose properties\n * cannot reach the host Function constructor through any prototype chain.\n * All function-valued properties are wrapped via safeFunction().\n */\nfunction safeNamespace(source: Record<string, unknown>, keys: string[]): Readonly<Record<string, unknown>> {\n const ns = Object.create(null) as Record<string, unknown>;\n for (const key of keys) {\n const val = source[key];\n if (typeof val === \"function\") {\n ns[key] = safeFunction(val as (...args: any[]) => any);\n } else {\n ns[key] = val;\n }\n }\n return Object.freeze(ns);\n}\n\n// Only run when forked as a child process\nif (process.send) {\n process.on(\"message\", (msg: WorkerMessage) => {\n if (msg.type !== \"execute\") return;\n\n const logs: string[] = [];\n\n // Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)\n const dangerousPatterns = [\n /constructor\\s*\\[/i,\n /constructor\\s*\\(/i,\n /\\.constructor/i,\n /__proto__/i,\n /prototype\\s*\\[/i,\n /\\bprocess\\b/,\n /\\brequire\\b/,\n /\\bimport\\b/,\n /\\bglobalThis\\b/,\n /\\bglobal\\b/,\n /\\bFunction\\b/,\n /\\beval\\b/,\n /\\bReflect\\b/,\n /\\bProxy\\b/,\n /\\bSymbol\\b/,\n /\\bWeakRef\\b/,\n ];\n for (const pattern of dangerousPatterns) {\n if (pattern.test(msg.code)) {\n const result: WorkerResult = {\n type: \"result\",\n success: false,\n error: `Code contains blocked pattern: ${pattern.source}`,\n };\n process.send!(result);\n return;\n }\n }\n\n // Build hardened sandbox — NO host constructors or direct function references.\n // vm.runInNewContext provides its own Array, Object, String, Number, Boolean in the\n // sandbox context. We do NOT pass host realm constructors, which would allow\n // escaping via .constructor.constructor('return process')().\n const sandbox = Object.create(null) as Record<string, unknown>;\n sandbox.steps = JSON.parse(JSON.stringify(msg.context.steps));\n sandbox.trigger = JSON.parse(JSON.stringify(msg.context.trigger));\n sandbox.input = msg.context.input != null ? JSON.parse(JSON.stringify(msg.context.input)) : undefined;\n sandbox.output = undefined;\n\n // console — safe: null-prototype object with wrapped log function\n const logFn = safeFunction((...args: unknown[]) => logs.push(args.map(String).join(\" \")));\n const consoleObj = Object.create(null) as Record<string, unknown>;\n consoleObj.log = logFn;\n sandbox.console = Object.freeze(consoleObj);\n\n // JSON — safe namespace with only parse/stringify\n sandbox.JSON = safeNamespace(JSON as unknown as Record<string, unknown>, [\"parse\", \"stringify\"]);\n\n // Math — safe namespace (all methods wrapped, numeric constants preserved)\n const mathKeys = [\n \"abs\", \"ceil\", \"floor\", \"round\", \"max\", \"min\", \"pow\", \"sqrt\", \"log\", \"log2\", \"log10\",\n \"random\", \"sign\", \"trunc\", \"cbrt\", \"hypot\", \"clz32\", \"imul\", \"fround\",\n \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"atan2\", \"sinh\", \"cosh\", \"tanh\",\n \"PI\", \"E\", \"LN2\", \"LN10\", \"LOG2E\", \"LOG10E\", \"SQRT2\", \"SQRT1_2\",\n ];\n sandbox.Math = safeNamespace(Math as unknown as Record<string, unknown>, mathKeys);\n\n // Date — only expose Date.now() as a safe function, not the Date constructor itself.\n // The Date constructor is a host function whose .constructor leads to Function.\n const dateNs = Object.create(null) as Record<string, unknown>;\n dateNs.now = safeFunction(Date.now);\n sandbox.Date = Object.freeze(dateNs);\n\n // Utility functions — each wrapped to sever .constructor chain\n sandbox.parseInt = safeFunction(parseInt);\n sandbox.parseFloat = safeFunction(parseFloat);\n sandbox.isNaN = safeFunction(isNaN);\n sandbox.isFinite = safeFunction(isFinite);\n sandbox.encodeURIComponent = safeFunction(encodeURIComponent);\n sandbox.decodeURIComponent = safeFunction(decodeURIComponent);\n\n // URL — safe namespace with only parse method (no constructor exposure)\n const urlNs = Object.create(null) as Record<string, unknown>;\n urlNs.parse = safeFunction((input: string) => {\n try {\n const u = new URL(input);\n return { href: u.href, protocol: u.protocol, host: u.host, hostname: u.hostname,\n port: u.port, pathname: u.pathname, search: u.search, hash: u.hash,\n origin: u.origin, searchParams: Object.fromEntries(u.searchParams) };\n } catch { return null; }\n });\n urlNs.format = safeFunction((parts: Record<string, string>) => {\n try { return new URL(`${parts.protocol || \"https:\"}//${parts.host || parts.hostname || \"\"}${parts.pathname || \"\"}${parts.search || \"\"}`).href; }\n catch { return null; }\n });\n sandbox.URL = Object.freeze(urlNs);\n\n // Buffer — safe namespace with wrapped from/alloc\n const bufferNs = Object.create(null) as Record<string, unknown>;\n bufferNs.from = safeFunction((...args: unknown[]) => Buffer.from(args[0] as any, args[1] as any));\n bufferNs.alloc = safeFunction((size: number) => Buffer.alloc(size));\n sandbox.Buffer = Object.freeze(bufferNs);\n\n // crypto — safe namespace with wrapped randomUUID\n const cryptoNs = Object.create(null) as Record<string, unknown>;\n cryptoNs.randomUUID = safeFunction(randomUUID);\n sandbox.crypto = Object.freeze(cryptoNs);\n\n // DO NOT expose host constructors: Array, Object, String, Number, Boolean.\n // vm.runInNewContext creates its own versions of these in the sandbox context.\n // Passing host constructors would allow: [].constructor.constructor('return process')()\n\n // Block dangerous globals explicitly\n sandbox.process = undefined;\n sandbox.require = undefined;\n sandbox.global = undefined;\n sandbox.globalThis = undefined;\n sandbox.Function = undefined;\n sandbox.eval = undefined;\n sandbox.setTimeout = undefined;\n sandbox.setInterval = undefined;\n sandbox.Reflect = undefined;\n sandbox.Proxy = undefined;\n sandbox.Symbol = undefined;\n sandbox.WeakRef = undefined;\n\n try {\n const result = runInNewContext(msg.code, sandbox, {\n timeout: msg.timeoutMs,\n filename: \"workflow-code-step\",\n breakOnSigint: true,\n microtaskMode: \"afterEvaluate\",\n });\n\n const workerResult: WorkerResult = {\n type: \"result\",\n success: true,\n output: { result: sandbox.output ?? result, logs },\n };\n process.send!(workerResult);\n } catch (err: any) {\n const workerResult: WorkerResult = {\n type: \"result\",\n success: false,\n error: err.code === \"ERR_SCRIPT_EXECUTION_TIMEOUT\"\n ? `Code execution timed out after ${msg.timeoutMs}ms`\n : `Code error: ${err.message}`,\n logs,\n };\n process.send!(workerResult);\n }\n });\n\n // Signal ready\n process.send({ type: \"ready\" });\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"code-worker.js","names":["runInNewContext","randomUUID","safeFunction","fn","safe","args","apply","Object","defineProperty","value","undefined","writable","configurable","freeze","safeNamespace","source","keys","ns","create","key","val","process","send","on","msg","type","logs","dangerousPatterns","pattern","test","code","result","success","error","sandbox","steps","JSON","parse","stringify","context","trigger","input","output","logFn","push","map","String","join","consoleObj","log","console","mathKeys","Math","dateNs","now","Date","parseInt","parseFloat","isNaN","isFinite","encodeURIComponent","decodeURIComponent","urlNs","u","URL","href","protocol","host","hostname","port","pathname","search","hash","origin","searchParams","fromEntries","format","parts","bufferNs","from","Buffer","alloc","size","cryptoNs","crypto","require","global","globalThis","Function","eval","setTimeout","setInterval","Reflect","Proxy","Symbol","WeakRef","timeout","timeoutMs","filename","breakOnSigint","microtaskMode","workerResult","err","message"],"sources":["../../../src/server/lib/code-worker.ts"],"sourcesContent":["// server/lib/code-worker.ts — Isolated code execution worker\n// Runs as a child process via fork(). Receives code + context via IPC,\n// executes in a fresh vm context, returns result. Crash here cannot take down the server.\n\nimport { runInNewContext } from \"node:vm\";\nimport { randomUUID } from \"node:crypto\";\n\ninterface WorkerMessage {\n type: \"execute\";\n code: string;\n context: {\n steps: Record<string, unknown>;\n trigger: Record<string, unknown>;\n input?: unknown;\n };\n timeoutMs: number;\n}\n\ninterface WorkerResult {\n type: \"result\";\n success: boolean;\n output?: unknown;\n logs?: string[];\n error?: string;\n}\n\n/**\n * Create a safe wrapper function that cannot be used to reach the host Function constructor.\n * Wraps a host function in a proxy whose prototype chain is severed — .constructor returns\n * undefined instead of the host Function constructor.\n */\nfunction safeFunction<T extends (...args: any[]) => any>(fn: T): T {\n // Create a wrapper function whose .constructor and .prototype are severed.\n // We can't make a callable null-prototype object directly, so we override them on a regular function.\n const safe = function (this: unknown, ...args: unknown[]) {\n return fn.apply(this, args);\n };\n Object.defineProperty(safe, \"constructor\", { value: undefined, writable: false, configurable: false });\n Object.defineProperty(safe, \"prototype\", { value: undefined, writable: false, configurable: false });\n // Freeze to prevent re-assignment of constructor\n Object.freeze(safe);\n return safe as unknown as T;\n}\n\n/**\n * Create a safe namespace object (like Math, JSON, Buffer) whose properties\n * cannot reach the host Function constructor through any prototype chain.\n * All function-valued properties are wrapped via safeFunction().\n */\nfunction safeNamespace(source: Record<string, unknown>, keys: string[]): Readonly<Record<string, unknown>> {\n const ns = Object.create(null) as Record<string, unknown>;\n for (const key of keys) {\n const val = source[key];\n if (typeof val === \"function\") {\n ns[key] = safeFunction(val as (...args: any[]) => any);\n } else {\n ns[key] = val;\n }\n }\n return Object.freeze(ns);\n}\n\n// Only run when forked as a child process\nif (process.send) {\n process.on(\"message\", (msg: WorkerMessage) => {\n if (msg.type !== \"execute\") return;\n\n const logs: string[] = [];\n\n // Validate code for dangerous patterns (defense-in-depth — sandbox isolation is primary defense)\n const dangerousPatterns = [\n /constructor\\s*\\[/i,\n /constructor\\s*\\(/i,\n /\\.constructor/i,\n /\\[\"constructor\"\\]/i,\n /\\['constructor'\\]/i,\n /\\[`constructor`\\]/i,\n /__proto__/i,\n /prototype\\s*\\[/i,\n /\\bprocess\\b/,\n /\\brequire\\b/,\n /\\bimport\\b/,\n /\\bglobalThis\\b/,\n /\\bglobal\\b/,\n /\\bFunction\\b/,\n /\\beval\\b/,\n /\\bReflect\\b/,\n /\\bProxy\\b/,\n /\\bSymbol\\b/,\n /\\bWeakRef\\b/,\n /\\[\\s*['\"`]__proto__['\"`]\\s*\\]/i,\n /\\[\\s*['\"`]prototype['\"`]\\s*\\]/i,\n /\\bthis\\s*\\[/, // bracket notation on this\n /\\barguments\\b/, // arguments object can leak caller\n ];\n for (const pattern of dangerousPatterns) {\n if (pattern.test(msg.code)) {\n const result: WorkerResult = {\n type: \"result\",\n success: false,\n error: `Code contains blocked pattern: ${pattern.source}`,\n };\n process.send!(result);\n return;\n }\n }\n\n // Build hardened sandbox — NO host constructors or direct function references.\n // vm.runInNewContext provides its own Array, Object, String, Number, Boolean in the\n // sandbox context. We do NOT pass host realm constructors, which would allow\n // escaping via .constructor.constructor('return process')().\n const sandbox = Object.create(null) as Record<string, unknown>;\n sandbox.steps = JSON.parse(JSON.stringify(msg.context.steps));\n sandbox.trigger = JSON.parse(JSON.stringify(msg.context.trigger));\n sandbox.input = msg.context.input != null ? JSON.parse(JSON.stringify(msg.context.input)) : undefined;\n sandbox.output = undefined;\n\n // console — safe: null-prototype object with wrapped log function\n const logFn = safeFunction((...args: unknown[]) => logs.push(args.map(String).join(\" \")));\n const consoleObj = Object.create(null) as Record<string, unknown>;\n consoleObj.log = logFn;\n sandbox.console = Object.freeze(consoleObj);\n\n // JSON — safe namespace with only parse/stringify\n sandbox.JSON = safeNamespace(JSON as unknown as Record<string, unknown>, [\"parse\", \"stringify\"]);\n\n // Math — safe namespace (all methods wrapped, numeric constants preserved)\n const mathKeys = [\n \"abs\", \"ceil\", \"floor\", \"round\", \"max\", \"min\", \"pow\", \"sqrt\", \"log\", \"log2\", \"log10\",\n \"random\", \"sign\", \"trunc\", \"cbrt\", \"hypot\", \"clz32\", \"imul\", \"fround\",\n \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\", \"atan2\", \"sinh\", \"cosh\", \"tanh\",\n \"PI\", \"E\", \"LN2\", \"LN10\", \"LOG2E\", \"LOG10E\", \"SQRT2\", \"SQRT1_2\",\n ];\n sandbox.Math = safeNamespace(Math as unknown as Record<string, unknown>, mathKeys);\n\n // Date — only expose Date.now() as a safe function, not the Date constructor itself.\n // The Date constructor is a host function whose .constructor leads to Function.\n const dateNs = Object.create(null) as Record<string, unknown>;\n dateNs.now = safeFunction(Date.now);\n sandbox.Date = Object.freeze(dateNs);\n\n // Utility functions — each wrapped to sever .constructor chain\n sandbox.parseInt = safeFunction(parseInt);\n sandbox.parseFloat = safeFunction(parseFloat);\n sandbox.isNaN = safeFunction(isNaN);\n sandbox.isFinite = safeFunction(isFinite);\n sandbox.encodeURIComponent = safeFunction(encodeURIComponent);\n sandbox.decodeURIComponent = safeFunction(decodeURIComponent);\n\n // URL — safe namespace with only parse method (no constructor exposure)\n const urlNs = Object.create(null) as Record<string, unknown>;\n urlNs.parse = safeFunction((input: string) => {\n try {\n const u = new URL(input);\n return { href: u.href, protocol: u.protocol, host: u.host, hostname: u.hostname,\n port: u.port, pathname: u.pathname, search: u.search, hash: u.hash,\n origin: u.origin, searchParams: Object.fromEntries(u.searchParams) };\n } catch { return null; }\n });\n urlNs.format = safeFunction((parts: Record<string, string>) => {\n try { return new URL(`${parts.protocol || \"https:\"}//${parts.host || parts.hostname || \"\"}${parts.pathname || \"\"}${parts.search || \"\"}`).href; }\n catch { return null; }\n });\n sandbox.URL = Object.freeze(urlNs);\n\n // Buffer — safe namespace with wrapped from/alloc\n const bufferNs = Object.create(null) as Record<string, unknown>;\n bufferNs.from = safeFunction((...args: unknown[]) => Buffer.from(args[0] as any, args[1] as any));\n bufferNs.alloc = safeFunction((size: number) => Buffer.alloc(size));\n sandbox.Buffer = Object.freeze(bufferNs);\n\n // crypto — safe namespace with wrapped randomUUID\n const cryptoNs = Object.create(null) as Record<string, unknown>;\n cryptoNs.randomUUID = safeFunction(randomUUID);\n sandbox.crypto = Object.freeze(cryptoNs);\n\n // DO NOT expose host constructors: Array, Object, String, Number, Boolean.\n // vm.runInNewContext creates its own versions of these in the sandbox context.\n // Passing host constructors would allow: [].constructor.constructor('return process')()\n\n // Block dangerous globals explicitly\n sandbox.process = undefined;\n sandbox.require = undefined;\n sandbox.global = undefined;\n sandbox.globalThis = undefined;\n sandbox.Function = undefined;\n sandbox.eval = undefined;\n sandbox.setTimeout = undefined;\n sandbox.setInterval = undefined;\n sandbox.Reflect = undefined;\n sandbox.Proxy = undefined;\n sandbox.Symbol = undefined;\n sandbox.WeakRef = undefined;\n\n try {\n const result = runInNewContext(msg.code, sandbox, {\n timeout: msg.timeoutMs,\n filename: \"workflow-code-step\",\n breakOnSigint: true,\n microtaskMode: \"afterEvaluate\",\n });\n\n const workerResult: WorkerResult = {\n type: \"result\",\n success: true,\n output: { result: sandbox.output ?? result, logs },\n };\n process.send!(workerResult);\n } catch (err: any) {\n const workerResult: WorkerResult = {\n type: \"result\",\n success: false,\n error: err.code === \"ERR_SCRIPT_EXECUTION_TIMEOUT\"\n ? `Code execution timed out after ${msg.timeoutMs}ms`\n : `Code error: ${err.message}`,\n logs,\n };\n process.send!(workerResult);\n }\n });\n\n // Signal ready\n process.send({ type: \"ready\" });\n}\n"],"mappings":"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","ignoreList":[]}
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import Anthropic from "@anthropic-ai/sdk";
12
- import { sanitizeError, AGENT_DEFAULTS } from "../../shared/agent-core.js";
12
+ import { sanitizeError, AGENT_DEFAULTS, isRetryableError, categorizeError } from "../../shared/agent-core.js";
13
13
  import { getCapabilities } from "../lib/provider-capabilities.js";
14
14
  import { registerProvider } from "./registry.js";
15
15
  import { jsonResponse, writeSSEHeaders } from "./shared.js";
@@ -34,6 +34,48 @@ const MODEL_MAX_OUTPUT_TOKENS = {
34
34
  "claude-3-haiku-20240307": 4096
35
35
  };
36
36
 
37
+ // ============================================================================
38
+ // PROXY RETRY — retries overloaded/rate-limit errors before giving up
39
+ // ============================================================================
40
+
41
+ const PROXY_MAX_RETRIES = 4;
42
+ const PROXY_BASE_DELAY_MS = 3000;
43
+ const PROXY_MAX_DELAY_MS = 45000;
44
+ const PROXY_FALLBACK_AFTER = 2;
45
+
46
+ // Intra-provider model fallback for proxy (Opus → Sonnet when overloaded)
47
+ const PROXY_MODEL_FALLBACK = {
48
+ "claude-opus-4-6": "claude-sonnet-4-6",
49
+ "claude-opus-4-5-20251101": "claude-sonnet-4-5-20250929",
50
+ "claude-opus-4-20250514": "claude-sonnet-4-20250514",
51
+ "claude-opus-4-1-20250805": "claude-sonnet-4-5-20250929"
52
+ };
53
+ function proxyRetryDelay(attempt, err) {
54
+ let delay = Math.min(PROXY_BASE_DELAY_MS * Math.pow(2, attempt), PROXY_MAX_DELAY_MS);
55
+ const {
56
+ category
57
+ } = categorizeError(err);
58
+ if (category === "PROVIDER_DOWN" || category === "RATE_LIMIT") {
59
+ delay = Math.min(delay * 3, PROXY_MAX_DELAY_MS);
60
+ }
61
+ // Jitter ±25%
62
+ const jitter = delay * 0.25 * (Math.random() * 2 - 1);
63
+ return Math.round(delay + jitter);
64
+ }
65
+ function maybeDowngradeModel(apiParams, attempt, err) {
66
+ const {
67
+ category
68
+ } = categorizeError(err);
69
+ if ((category === "PROVIDER_DOWN" || category === "RATE_LIMIT") && attempt >= PROXY_FALLBACK_AFTER) {
70
+ const current = apiParams.model;
71
+ const fallback = PROXY_MODEL_FALLBACK[current];
72
+ if (fallback && current !== fallback) {
73
+ console.warn(`[proxy] Model fallback: ${current} → ${fallback} (overloaded after ${attempt + 1} attempts)`);
74
+ apiParams.model = fallback;
75
+ }
76
+ }
77
+ }
78
+
37
79
  // ============================================================================
38
80
  // ADAPTER
39
81
  // ============================================================================
@@ -134,32 +176,67 @@ export class AnthropicAdapter {
134
176
  }
135
177
  }
136
178
  if (!stream) {
179
+ for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
180
+ try {
181
+ const response = effectiveBetas.length ? await anthropic.beta.messages.create({
182
+ ...apiParams,
183
+ stream: false
184
+ }) : await anthropic.messages.create({
185
+ ...apiParams,
186
+ stream: false
187
+ });
188
+ jsonResponse(res, 200, response, corsHeaders);
189
+ return;
190
+ } catch (err) {
191
+ if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {
192
+ maybeDowngradeModel(apiParams, attempt, err);
193
+ const delay = proxyRetryDelay(attempt, err);
194
+ console.warn(`[proxy] Attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);
195
+ await new Promise(r => setTimeout(r, delay));
196
+ continue;
197
+ }
198
+ jsonResponse(res, 500, {
199
+ error: sanitizeError(err)
200
+ }, corsHeaders);
201
+ return;
202
+ }
203
+ }
204
+ return;
205
+ }
206
+
207
+ // Streaming: retry the initial API call with backoff before committing to SSE.
208
+ let heartbeat;
209
+ let streamResponse;
210
+ for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
137
211
  try {
138
- const response = effectiveBetas.length ? await anthropic.beta.messages.create({
212
+ streamResponse = effectiveBetas.length ? await anthropic.beta.messages.create({
139
213
  ...apiParams,
140
- stream: false
141
- }) : await anthropic.messages.create({
142
- ...apiParams,
143
- stream: false
144
- });
145
- jsonResponse(res, 200, response, corsHeaders);
214
+ stream: true
215
+ }) : await anthropic.messages.create(apiParams);
216
+ break;
146
217
  } catch (err) {
147
- jsonResponse(res, 500, {
148
- error: sanitizeError(err)
218
+ if (attempt < PROXY_MAX_RETRIES && isRetryableError(err)) {
219
+ maybeDowngradeModel(apiParams, attempt, err);
220
+ const delay = proxyRetryDelay(attempt, err);
221
+ console.warn(`[proxy] Stream attempt ${attempt + 1}/${PROXY_MAX_RETRIES + 1} failed (model=${apiParams.model}) — retrying in ${(delay / 1000).toFixed(1)}s: ${sanitizeError(err)}`);
222
+ await new Promise(r => setTimeout(r, delay));
223
+ continue;
224
+ }
225
+ jsonResponse(res, 502, {
226
+ error: `anthropic error: ${sanitizeError(err)}`
149
227
  }, corsHeaders);
228
+ res.end();
229
+ return;
150
230
  }
231
+ }
232
+ if (!streamResponse) {
233
+ jsonResponse(res, 502, {
234
+ error: "failed to create stream after retries"
235
+ }, corsHeaders);
236
+ res.end();
151
237
  return;
152
238
  }
153
-
154
- // Streaming: defer SSE headers until stream is successfully created
155
- // so we can return proper HTTP errors if the initial API call fails.
156
- let heartbeat;
157
239
  try {
158
- const response = effectiveBetas.length ? await anthropic.beta.messages.create({
159
- ...apiParams,
160
- stream: true
161
- }) : await anthropic.messages.create(apiParams);
162
-
163
240
  // Stream created successfully — now safe to commit to SSE
164
241
  writeSSEHeaders(res, corsHeaders);
165
242
 
@@ -167,24 +244,17 @@ export class AnthropicAdapter {
167
244
  heartbeat = setInterval(() => {
168
245
  if (!res.writableEnded) res.write(":ping\n\n");
169
246
  }, 15_000);
170
- for await (const event of response) {
247
+ for await (const event of streamResponse) {
171
248
  res.write(`data: ${JSON.stringify(event)}\n\n`);
172
249
  }
173
250
  res.write("data: [DONE]\n\n");
174
251
  } catch (err) {
175
- if (!res.headersSent) {
176
- // Haven't started streaming yet — return proper HTTP error
177
- jsonResponse(res, 502, {
178
- error: `anthropic error: ${sanitizeError(err)}`
179
- }, corsHeaders);
180
- } else {
181
- // Mid-stream failure — send error event so client doesn't hang
182
- res.write(`data: ${JSON.stringify({
183
- type: "error",
184
- error: sanitizeError(err)
185
- })}\n\n`);
186
- res.write("data: [DONE]\n\n");
187
- }
252
+ // Mid-stream failure — send error event so client doesn't hang
253
+ res.write(`data: ${JSON.stringify({
254
+ type: "error",
255
+ error: sanitizeError(err)
256
+ })}\n\n`);
257
+ res.write("data: [DONE]\n\n");
188
258
  } finally {
189
259
  if (heartbeat) clearInterval(heartbeat);
190
260
  }