whale-code 6.4.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 (319) hide show
  1. package/README.md +95 -0
  2. package/bin/swag-agent.js +9 -0
  3. package/bin/swagmanager-mcp.js +321 -0
  4. package/dist/cli/app.d.ts +26 -0
  5. package/dist/cli/app.js +64 -0
  6. package/dist/cli/chat/AgentSelector.d.ts +14 -0
  7. package/dist/cli/chat/AgentSelector.js +14 -0
  8. package/dist/cli/chat/ChatApp.d.ts +9 -0
  9. package/dist/cli/chat/ChatApp.js +267 -0
  10. package/dist/cli/chat/ChatInput.d.ts +39 -0
  11. package/dist/cli/chat/ChatInput.js +509 -0
  12. package/dist/cli/chat/MarkdownText.d.ts +10 -0
  13. package/dist/cli/chat/MarkdownText.js +20 -0
  14. package/dist/cli/chat/MessageList.d.ts +37 -0
  15. package/dist/cli/chat/MessageList.js +80 -0
  16. package/dist/cli/chat/ModelSelector.d.ts +20 -0
  17. package/dist/cli/chat/ModelSelector.js +73 -0
  18. package/dist/cli/chat/RewindViewer.d.ts +26 -0
  19. package/dist/cli/chat/RewindViewer.js +185 -0
  20. package/dist/cli/chat/StoreSelector.d.ts +14 -0
  21. package/dist/cli/chat/StoreSelector.js +24 -0
  22. package/dist/cli/chat/StreamingText.d.ts +12 -0
  23. package/dist/cli/chat/StreamingText.js +12 -0
  24. package/dist/cli/chat/SubagentPanel.d.ts +45 -0
  25. package/dist/cli/chat/SubagentPanel.js +110 -0
  26. package/dist/cli/chat/TeamPanel.d.ts +21 -0
  27. package/dist/cli/chat/TeamPanel.js +42 -0
  28. package/dist/cli/chat/ToolIndicator.d.ts +25 -0
  29. package/dist/cli/chat/ToolIndicator.js +436 -0
  30. package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
  31. package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
  32. package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
  33. package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
  34. package/dist/cli/commands/config-cmd.d.ts +10 -0
  35. package/dist/cli/commands/config-cmd.js +99 -0
  36. package/dist/cli/commands/doctor.d.ts +14 -0
  37. package/dist/cli/commands/doctor.js +172 -0
  38. package/dist/cli/commands/init.d.ts +16 -0
  39. package/dist/cli/commands/init.js +278 -0
  40. package/dist/cli/commands/mcp.d.ts +12 -0
  41. package/dist/cli/commands/mcp.js +162 -0
  42. package/dist/cli/login/LoginApp.d.ts +7 -0
  43. package/dist/cli/login/LoginApp.js +157 -0
  44. package/dist/cli/print-mode.d.ts +31 -0
  45. package/dist/cli/print-mode.js +202 -0
  46. package/dist/cli/serve-mode.d.ts +37 -0
  47. package/dist/cli/serve-mode.js +636 -0
  48. package/dist/cli/services/agent-definitions.d.ts +25 -0
  49. package/dist/cli/services/agent-definitions.js +91 -0
  50. package/dist/cli/services/agent-events.d.ts +178 -0
  51. package/dist/cli/services/agent-events.js +175 -0
  52. package/dist/cli/services/agent-loop.d.ts +90 -0
  53. package/dist/cli/services/agent-loop.js +762 -0
  54. package/dist/cli/services/agent-worker-base.d.ts +97 -0
  55. package/dist/cli/services/agent-worker-base.js +220 -0
  56. package/dist/cli/services/auth-service.d.ts +30 -0
  57. package/dist/cli/services/auth-service.js +160 -0
  58. package/dist/cli/services/background-processes.d.ts +126 -0
  59. package/dist/cli/services/background-processes.js +318 -0
  60. package/dist/cli/services/browser-auth.d.ts +24 -0
  61. package/dist/cli/services/browser-auth.js +180 -0
  62. package/dist/cli/services/claude-md-loader.d.ts +16 -0
  63. package/dist/cli/services/claude-md-loader.js +58 -0
  64. package/dist/cli/services/config-store.d.ts +47 -0
  65. package/dist/cli/services/config-store.js +79 -0
  66. package/dist/cli/services/debug-log.d.ts +10 -0
  67. package/dist/cli/services/debug-log.js +52 -0
  68. package/dist/cli/services/error-logger.d.ts +58 -0
  69. package/dist/cli/services/error-logger.js +269 -0
  70. package/dist/cli/services/file-history.d.ts +21 -0
  71. package/dist/cli/services/file-history.js +83 -0
  72. package/dist/cli/services/format-server-response.d.ts +16 -0
  73. package/dist/cli/services/format-server-response.js +440 -0
  74. package/dist/cli/services/git-context.d.ts +11 -0
  75. package/dist/cli/services/git-context.js +66 -0
  76. package/dist/cli/services/hooks.d.ts +85 -0
  77. package/dist/cli/services/hooks.js +258 -0
  78. package/dist/cli/services/interactive-tools.d.ts +125 -0
  79. package/dist/cli/services/interactive-tools.js +260 -0
  80. package/dist/cli/services/keybinding-manager.d.ts +52 -0
  81. package/dist/cli/services/keybinding-manager.js +115 -0
  82. package/dist/cli/services/local-tools.d.ts +22 -0
  83. package/dist/cli/services/local-tools.js +697 -0
  84. package/dist/cli/services/lsp-manager.d.ts +18 -0
  85. package/dist/cli/services/lsp-manager.js +717 -0
  86. package/dist/cli/services/mcp-client.d.ts +48 -0
  87. package/dist/cli/services/mcp-client.js +157 -0
  88. package/dist/cli/services/memory-manager.d.ts +16 -0
  89. package/dist/cli/services/memory-manager.js +57 -0
  90. package/dist/cli/services/model-manager.d.ts +18 -0
  91. package/dist/cli/services/model-manager.js +71 -0
  92. package/dist/cli/services/model-router.d.ts +26 -0
  93. package/dist/cli/services/model-router.js +149 -0
  94. package/dist/cli/services/permission-modes.d.ts +13 -0
  95. package/dist/cli/services/permission-modes.js +43 -0
  96. package/dist/cli/services/rewind.d.ts +84 -0
  97. package/dist/cli/services/rewind.js +194 -0
  98. package/dist/cli/services/ripgrep.d.ts +28 -0
  99. package/dist/cli/services/ripgrep.js +138 -0
  100. package/dist/cli/services/sandbox.d.ts +29 -0
  101. package/dist/cli/services/sandbox.js +97 -0
  102. package/dist/cli/services/server-tools.d.ts +61 -0
  103. package/dist/cli/services/server-tools.js +543 -0
  104. package/dist/cli/services/session-persistence.d.ts +23 -0
  105. package/dist/cli/services/session-persistence.js +99 -0
  106. package/dist/cli/services/subagent-worker.d.ts +19 -0
  107. package/dist/cli/services/subagent-worker.js +41 -0
  108. package/dist/cli/services/subagent.d.ts +47 -0
  109. package/dist/cli/services/subagent.js +647 -0
  110. package/dist/cli/services/system-prompt.d.ts +7 -0
  111. package/dist/cli/services/system-prompt.js +198 -0
  112. package/dist/cli/services/team-lead.d.ts +73 -0
  113. package/dist/cli/services/team-lead.js +512 -0
  114. package/dist/cli/services/team-state.d.ts +77 -0
  115. package/dist/cli/services/team-state.js +398 -0
  116. package/dist/cli/services/teammate.d.ts +31 -0
  117. package/dist/cli/services/teammate.js +689 -0
  118. package/dist/cli/services/telemetry.d.ts +61 -0
  119. package/dist/cli/services/telemetry.js +209 -0
  120. package/dist/cli/services/tools/agent-tools.d.ts +14 -0
  121. package/dist/cli/services/tools/agent-tools.js +347 -0
  122. package/dist/cli/services/tools/file-ops.d.ts +15 -0
  123. package/dist/cli/services/tools/file-ops.js +487 -0
  124. package/dist/cli/services/tools/search-tools.d.ts +8 -0
  125. package/dist/cli/services/tools/search-tools.js +186 -0
  126. package/dist/cli/services/tools/shell-exec.d.ts +10 -0
  127. package/dist/cli/services/tools/shell-exec.js +168 -0
  128. package/dist/cli/services/tools/task-manager.d.ts +28 -0
  129. package/dist/cli/services/tools/task-manager.js +209 -0
  130. package/dist/cli/services/tools/web-tools.d.ts +11 -0
  131. package/dist/cli/services/tools/web-tools.js +395 -0
  132. package/dist/cli/setup/SetupApp.d.ts +9 -0
  133. package/dist/cli/setup/SetupApp.js +191 -0
  134. package/dist/cli/shared/MatrixIntro.d.ts +4 -0
  135. package/dist/cli/shared/MatrixIntro.js +83 -0
  136. package/dist/cli/shared/Theme.d.ts +74 -0
  137. package/dist/cli/shared/Theme.js +127 -0
  138. package/dist/cli/shared/WhaleBanner.d.ts +10 -0
  139. package/dist/cli/shared/WhaleBanner.js +12 -0
  140. package/dist/cli/shared/markdown.d.ts +21 -0
  141. package/dist/cli/shared/markdown.js +756 -0
  142. package/dist/cli/status/StatusApp.d.ts +4 -0
  143. package/dist/cli/status/StatusApp.js +105 -0
  144. package/dist/cli/stores/StoreApp.d.ts +7 -0
  145. package/dist/cli/stores/StoreApp.js +81 -0
  146. package/dist/index.d.ts +15 -0
  147. package/dist/index.js +538 -0
  148. package/dist/local-agent/connection.d.ts +48 -0
  149. package/dist/local-agent/connection.js +332 -0
  150. package/dist/local-agent/discovery.d.ts +18 -0
  151. package/dist/local-agent/discovery.js +146 -0
  152. package/dist/local-agent/executor.d.ts +34 -0
  153. package/dist/local-agent/executor.js +241 -0
  154. package/dist/local-agent/index.d.ts +14 -0
  155. package/dist/local-agent/index.js +198 -0
  156. package/dist/node/adapters/base.d.ts +35 -0
  157. package/dist/node/adapters/base.js +10 -0
  158. package/dist/node/adapters/discord.d.ts +29 -0
  159. package/dist/node/adapters/discord.js +299 -0
  160. package/dist/node/adapters/email.d.ts +23 -0
  161. package/dist/node/adapters/email.js +218 -0
  162. package/dist/node/adapters/imessage.d.ts +17 -0
  163. package/dist/node/adapters/imessage.js +118 -0
  164. package/dist/node/adapters/slack.d.ts +26 -0
  165. package/dist/node/adapters/slack.js +259 -0
  166. package/dist/node/adapters/sms.d.ts +23 -0
  167. package/dist/node/adapters/sms.js +161 -0
  168. package/dist/node/adapters/telegram.d.ts +17 -0
  169. package/dist/node/adapters/telegram.js +101 -0
  170. package/dist/node/adapters/webchat.d.ts +27 -0
  171. package/dist/node/adapters/webchat.js +160 -0
  172. package/dist/node/adapters/whatsapp.d.ts +28 -0
  173. package/dist/node/adapters/whatsapp.js +230 -0
  174. package/dist/node/cli.d.ts +2 -0
  175. package/dist/node/cli.js +325 -0
  176. package/dist/node/config.d.ts +17 -0
  177. package/dist/node/config.js +31 -0
  178. package/dist/node/runtime.d.ts +50 -0
  179. package/dist/node/runtime.js +351 -0
  180. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
  181. package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
  182. package/dist/server/handlers/analytics.d.ts +17 -0
  183. package/dist/server/handlers/analytics.js +266 -0
  184. package/dist/server/handlers/api-keys.d.ts +6 -0
  185. package/dist/server/handlers/api-keys.js +221 -0
  186. package/dist/server/handlers/billing.d.ts +33 -0
  187. package/dist/server/handlers/billing.js +272 -0
  188. package/dist/server/handlers/browser.d.ts +10 -0
  189. package/dist/server/handlers/browser.js +517 -0
  190. package/dist/server/handlers/catalog.d.ts +99 -0
  191. package/dist/server/handlers/catalog.js +976 -0
  192. package/dist/server/handlers/comms.d.ts +254 -0
  193. package/dist/server/handlers/comms.js +588 -0
  194. package/dist/server/handlers/creations.d.ts +6 -0
  195. package/dist/server/handlers/creations.js +479 -0
  196. package/dist/server/handlers/crm.d.ts +89 -0
  197. package/dist/server/handlers/crm.js +538 -0
  198. package/dist/server/handlers/discovery.d.ts +6 -0
  199. package/dist/server/handlers/discovery.js +288 -0
  200. package/dist/server/handlers/embeddings.d.ts +92 -0
  201. package/dist/server/handlers/embeddings.js +197 -0
  202. package/dist/server/handlers/enrichment.d.ts +8 -0
  203. package/dist/server/handlers/enrichment.js +768 -0
  204. package/dist/server/handlers/image-gen.d.ts +6 -0
  205. package/dist/server/handlers/image-gen.js +409 -0
  206. package/dist/server/handlers/inventory.d.ts +319 -0
  207. package/dist/server/handlers/inventory.js +447 -0
  208. package/dist/server/handlers/kali.d.ts +10 -0
  209. package/dist/server/handlers/kali.js +210 -0
  210. package/dist/server/handlers/llm-providers.d.ts +6 -0
  211. package/dist/server/handlers/llm-providers.js +673 -0
  212. package/dist/server/handlers/local-agent.d.ts +6 -0
  213. package/dist/server/handlers/local-agent.js +118 -0
  214. package/dist/server/handlers/meta-ads.d.ts +111 -0
  215. package/dist/server/handlers/meta-ads.js +2279 -0
  216. package/dist/server/handlers/nodes.d.ts +33 -0
  217. package/dist/server/handlers/nodes.js +699 -0
  218. package/dist/server/handlers/operations.d.ts +138 -0
  219. package/dist/server/handlers/operations.js +131 -0
  220. package/dist/server/handlers/platform.d.ts +23 -0
  221. package/dist/server/handlers/platform.js +227 -0
  222. package/dist/server/handlers/supply-chain.d.ts +19 -0
  223. package/dist/server/handlers/supply-chain.js +327 -0
  224. package/dist/server/handlers/transcription.d.ts +17 -0
  225. package/dist/server/handlers/transcription.js +121 -0
  226. package/dist/server/handlers/video-gen.d.ts +6 -0
  227. package/dist/server/handlers/video-gen.js +466 -0
  228. package/dist/server/handlers/voice.d.ts +8 -0
  229. package/dist/server/handlers/voice.js +1146 -0
  230. package/dist/server/handlers/workflow-steps.d.ts +86 -0
  231. package/dist/server/handlers/workflow-steps.js +2349 -0
  232. package/dist/server/handlers/workflows.d.ts +7 -0
  233. package/dist/server/handlers/workflows.js +989 -0
  234. package/dist/server/index.d.ts +1 -0
  235. package/dist/server/index.js +2427 -0
  236. package/dist/server/lib/batch-client.d.ts +80 -0
  237. package/dist/server/lib/batch-client.js +467 -0
  238. package/dist/server/lib/code-worker-pool.d.ts +31 -0
  239. package/dist/server/lib/code-worker-pool.js +224 -0
  240. package/dist/server/lib/code-worker.d.ts +1 -0
  241. package/dist/server/lib/code-worker.js +188 -0
  242. package/dist/server/lib/compaction-service.d.ts +32 -0
  243. package/dist/server/lib/compaction-service.js +162 -0
  244. package/dist/server/lib/logger.d.ts +19 -0
  245. package/dist/server/lib/logger.js +46 -0
  246. package/dist/server/lib/otel.d.ts +38 -0
  247. package/dist/server/lib/otel.js +126 -0
  248. package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
  249. package/dist/server/lib/pg-rate-limiter.js +86 -0
  250. package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
  251. package/dist/server/lib/prompt-sanitizer.js +177 -0
  252. package/dist/server/lib/provider-capabilities.d.ts +85 -0
  253. package/dist/server/lib/provider-capabilities.js +190 -0
  254. package/dist/server/lib/provider-failover.d.ts +74 -0
  255. package/dist/server/lib/provider-failover.js +210 -0
  256. package/dist/server/lib/rate-limiter.d.ts +39 -0
  257. package/dist/server/lib/rate-limiter.js +147 -0
  258. package/dist/server/lib/server-agent-loop.d.ts +107 -0
  259. package/dist/server/lib/server-agent-loop.js +667 -0
  260. package/dist/server/lib/server-subagent.d.ts +78 -0
  261. package/dist/server/lib/server-subagent.js +203 -0
  262. package/dist/server/lib/session-checkpoint.d.ts +51 -0
  263. package/dist/server/lib/session-checkpoint.js +145 -0
  264. package/dist/server/lib/ssrf-guard.d.ts +13 -0
  265. package/dist/server/lib/ssrf-guard.js +240 -0
  266. package/dist/server/lib/supabase-client.d.ts +7 -0
  267. package/dist/server/lib/supabase-client.js +78 -0
  268. package/dist/server/lib/template-resolver.d.ts +31 -0
  269. package/dist/server/lib/template-resolver.js +215 -0
  270. package/dist/server/lib/utils.d.ts +16 -0
  271. package/dist/server/lib/utils.js +147 -0
  272. package/dist/server/local-agent-gateway.d.ts +82 -0
  273. package/dist/server/local-agent-gateway.js +426 -0
  274. package/dist/server/providers/anthropic.d.ts +20 -0
  275. package/dist/server/providers/anthropic.js +199 -0
  276. package/dist/server/providers/bedrock.d.ts +20 -0
  277. package/dist/server/providers/bedrock.js +194 -0
  278. package/dist/server/providers/gemini.d.ts +24 -0
  279. package/dist/server/providers/gemini.js +486 -0
  280. package/dist/server/providers/openai.d.ts +24 -0
  281. package/dist/server/providers/openai.js +522 -0
  282. package/dist/server/providers/registry.d.ts +32 -0
  283. package/dist/server/providers/registry.js +58 -0
  284. package/dist/server/providers/shared.d.ts +32 -0
  285. package/dist/server/providers/shared.js +124 -0
  286. package/dist/server/providers/types.d.ts +92 -0
  287. package/dist/server/providers/types.js +12 -0
  288. package/dist/server/proxy-handlers.d.ts +6 -0
  289. package/dist/server/proxy-handlers.js +89 -0
  290. package/dist/server/tool-router.d.ts +149 -0
  291. package/dist/server/tool-router.js +803 -0
  292. package/dist/server/validation.d.ts +24 -0
  293. package/dist/server/validation.js +301 -0
  294. package/dist/server/worker.d.ts +19 -0
  295. package/dist/server/worker.js +201 -0
  296. package/dist/setup.d.ts +8 -0
  297. package/dist/setup.js +181 -0
  298. package/dist/shared/agent-core.d.ts +157 -0
  299. package/dist/shared/agent-core.js +534 -0
  300. package/dist/shared/anthropic-types.d.ts +105 -0
  301. package/dist/shared/anthropic-types.js +7 -0
  302. package/dist/shared/api-client.d.ts +90 -0
  303. package/dist/shared/api-client.js +379 -0
  304. package/dist/shared/constants.d.ts +33 -0
  305. package/dist/shared/constants.js +80 -0
  306. package/dist/shared/sse-parser.d.ts +26 -0
  307. package/dist/shared/sse-parser.js +259 -0
  308. package/dist/shared/tool-dispatch.d.ts +52 -0
  309. package/dist/shared/tool-dispatch.js +191 -0
  310. package/dist/shared/types.d.ts +72 -0
  311. package/dist/shared/types.js +7 -0
  312. package/dist/updater.d.ts +25 -0
  313. package/dist/updater.js +140 -0
  314. package/dist/webchat/widget.d.ts +0 -0
  315. package/dist/webchat/widget.js +397 -0
  316. package/package.json +95 -0
  317. package/src/cli/services/builtin-skills/commit.md +19 -0
  318. package/src/cli/services/builtin-skills/review-pr.md +21 -0
  319. package/src/cli/services/builtin-skills/review.md +18 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Batch API Client — queues non-streaming LLM requests and submits them as batch jobs.
3
+ *
4
+ * Supports:
5
+ * - Anthropic Message Batches API (POST /v1/messages/batches)
6
+ * - OpenAI Batch API (POST /v1/batches via file upload)
7
+ *
8
+ * Batch API typically saves ~50% cost for non-latency-sensitive operations
9
+ * like workflow steps, bulk classification, and background processing.
10
+ */
11
+ export interface BatchRequest {
12
+ id: string;
13
+ provider: "anthropic" | "openai";
14
+ model: string;
15
+ messages: unknown[];
16
+ system?: unknown;
17
+ tools?: unknown[];
18
+ max_tokens: number;
19
+ temperature?: number;
20
+ }
21
+ export interface BatchResult {
22
+ id: string;
23
+ success: boolean;
24
+ content?: unknown;
25
+ text?: string;
26
+ error?: string;
27
+ usage?: {
28
+ input_tokens: number;
29
+ output_tokens: number;
30
+ };
31
+ }
32
+ export declare class BatchClient {
33
+ private queue;
34
+ /**
35
+ * Add a request to the queue. Call submitBatch() to send them all.
36
+ */
37
+ queueRequest(id: string, provider: "anthropic" | "openai", model: string, messages: unknown[], config?: {
38
+ system?: unknown;
39
+ tools?: unknown[];
40
+ max_tokens?: number;
41
+ temperature?: number;
42
+ }): void;
43
+ /**
44
+ * Submit all queued requests as a batch job. Returns the batch ID.
45
+ * Clears the queue after submission.
46
+ */
47
+ submitBatch(): Promise<{
48
+ batchId: string;
49
+ provider: "anthropic" | "openai";
50
+ count: number;
51
+ }>;
52
+ /**
53
+ * Poll a batch until it completes. Returns a map of custom_id → result.
54
+ */
55
+ pollBatch(batchId: string, provider: "anthropic" | "openai"): Promise<Map<string, BatchResult>>;
56
+ /**
57
+ * Convenience: queue a single request, submit it as a batch, poll until done, return result.
58
+ * Best for workflow steps where you want batch pricing but only have one request.
59
+ */
60
+ processSingle(id: string, provider: "anthropic" | "openai", model: string, messages: unknown[], config?: {
61
+ system?: unknown;
62
+ tools?: unknown[];
63
+ max_tokens?: number;
64
+ temperature?: number;
65
+ }): Promise<BatchResult>;
66
+ /** Return the current queue size (for diagnostics). */
67
+ get queueSize(): number;
68
+ private submitAnthropicBatch;
69
+ private pollAnthropicBatch;
70
+ private fetchAnthropicResults;
71
+ private submitOpenAIBatch;
72
+ private pollOpenAIBatch;
73
+ private fetchOpenAIResults;
74
+ /**
75
+ * Convert Anthropic-format messages to OpenAI chat format for batch requests.
76
+ * Simplified conversion — handles the common patterns (text, system, tool_use/tool_result).
77
+ */
78
+ private buildOpenAIMessages;
79
+ }
80
+ export declare const batchClient: BatchClient;
@@ -0,0 +1,467 @@
1
+ /**
2
+ * Batch API Client — queues non-streaming LLM requests and submits them as batch jobs.
3
+ *
4
+ * Supports:
5
+ * - Anthropic Message Batches API (POST /v1/messages/batches)
6
+ * - OpenAI Batch API (POST /v1/batches via file upload)
7
+ *
8
+ * Batch API typically saves ~50% cost for non-latency-sensitive operations
9
+ * like workflow steps, bulk classification, and background processing.
10
+ */
11
+ import { createLogger } from "./logger.js";
12
+ const log = createLogger("batch-client");
13
+ // ============================================================================
14
+ // CONSTANTS
15
+ // ============================================================================
16
+ const ANTHROPIC_BASE_URL = "https://api.anthropic.com";
17
+ const OPENAI_BASE_URL = "https://api.openai.com";
18
+ const POLL_INTERVAL_MS = 5_000;
19
+ const MAX_POLL_DURATION_MS = 10 * 60 * 1000; // 10 minutes
20
+ const ANTHROPIC_API_VERSION = "2023-06-01";
21
+ const ANTHROPIC_BATCH_BETA = "message-batches-2024-09-24";
22
+ // ============================================================================
23
+ // BATCH CLIENT
24
+ // ============================================================================
25
+ export class BatchClient {
26
+ queue = [];
27
+ /**
28
+ * Add a request to the queue. Call submitBatch() to send them all.
29
+ */
30
+ queueRequest(id, provider, model, messages, config = {}) {
31
+ this.queue.push({
32
+ id,
33
+ provider,
34
+ model,
35
+ messages,
36
+ system: config.system,
37
+ tools: config.tools,
38
+ max_tokens: config.max_tokens ?? 4096,
39
+ temperature: config.temperature,
40
+ });
41
+ log.info({ id, provider, model, queueSize: this.queue.length }, "queued batch request");
42
+ }
43
+ /**
44
+ * Submit all queued requests as a batch job. Returns the batch ID.
45
+ * Clears the queue after submission.
46
+ */
47
+ async submitBatch() {
48
+ if (this.queue.length === 0) {
49
+ throw new Error("No requests queued for batch submission");
50
+ }
51
+ // All requests in a batch must use the same provider
52
+ const provider = this.queue[0].provider;
53
+ const mixedProvider = this.queue.find(r => r.provider !== provider);
54
+ if (mixedProvider) {
55
+ throw new Error(`Mixed providers in batch: ${provider} and ${mixedProvider.provider}. Submit separate batches per provider.`);
56
+ }
57
+ const requests = [...this.queue];
58
+ this.queue = [];
59
+ if (provider === "anthropic") {
60
+ return this.submitAnthropicBatch(requests);
61
+ }
62
+ else {
63
+ return this.submitOpenAIBatch(requests);
64
+ }
65
+ }
66
+ /**
67
+ * Poll a batch until it completes. Returns a map of custom_id → result.
68
+ */
69
+ async pollBatch(batchId, provider) {
70
+ if (provider === "anthropic") {
71
+ return this.pollAnthropicBatch(batchId);
72
+ }
73
+ else {
74
+ return this.pollOpenAIBatch(batchId);
75
+ }
76
+ }
77
+ /**
78
+ * Convenience: queue a single request, submit it as a batch, poll until done, return result.
79
+ * Best for workflow steps where you want batch pricing but only have one request.
80
+ */
81
+ async processSingle(id, provider, model, messages, config = {}) {
82
+ this.queueRequest(id, provider, model, messages, config);
83
+ const { batchId } = await this.submitBatch();
84
+ const results = await this.pollBatch(batchId, provider);
85
+ return results.get(id) || { id, success: false, error: "Result not found in batch response" };
86
+ }
87
+ /** Return the current queue size (for diagnostics). */
88
+ get queueSize() {
89
+ return this.queue.length;
90
+ }
91
+ // ==========================================================================
92
+ // ANTHROPIC BATCH API
93
+ // ==========================================================================
94
+ async submitAnthropicBatch(requests) {
95
+ const apiKey = process.env.ANTHROPIC_API_KEY;
96
+ if (!apiKey)
97
+ throw new Error("ANTHROPIC_API_KEY not set");
98
+ const batchRequests = requests.map(r => {
99
+ const params = {
100
+ model: r.model,
101
+ messages: r.messages,
102
+ max_tokens: r.max_tokens,
103
+ };
104
+ if (r.system)
105
+ params.system = r.system;
106
+ if (r.tools?.length) {
107
+ params.tools = r.tools.map((t) => ({
108
+ name: t.name,
109
+ description: typeof t.description === "string" ? t.description.slice(0, 4096) : "",
110
+ input_schema: t.input_schema,
111
+ }));
112
+ }
113
+ if (r.temperature !== undefined)
114
+ params.temperature = r.temperature;
115
+ return { custom_id: r.id, params };
116
+ });
117
+ const resp = await fetch(`${ANTHROPIC_BASE_URL}/v1/messages/batches`, {
118
+ method: "POST",
119
+ headers: {
120
+ "Content-Type": "application/json",
121
+ "x-api-key": apiKey,
122
+ "anthropic-version": ANTHROPIC_API_VERSION,
123
+ "anthropic-beta": ANTHROPIC_BATCH_BETA,
124
+ },
125
+ body: JSON.stringify({ requests: batchRequests }),
126
+ });
127
+ if (!resp.ok) {
128
+ const errText = await resp.text();
129
+ throw new Error(`Anthropic batch submit failed (${resp.status}): ${errText.substring(0, 500)}`);
130
+ }
131
+ const data = await resp.json();
132
+ log.info({ batchId: data.id, count: requests.length }, "anthropic batch submitted");
133
+ return { batchId: data.id, provider: "anthropic", count: requests.length };
134
+ }
135
+ async pollAnthropicBatch(batchId) {
136
+ const apiKey = process.env.ANTHROPIC_API_KEY;
137
+ if (!apiKey)
138
+ throw new Error("ANTHROPIC_API_KEY not set");
139
+ const startTime = Date.now();
140
+ while (Date.now() - startTime < MAX_POLL_DURATION_MS) {
141
+ const resp = await fetch(`${ANTHROPIC_BASE_URL}/v1/messages/batches/${batchId}`, {
142
+ headers: {
143
+ "x-api-key": apiKey,
144
+ "anthropic-version": ANTHROPIC_API_VERSION,
145
+ "anthropic-beta": ANTHROPIC_BATCH_BETA,
146
+ },
147
+ });
148
+ if (!resp.ok) {
149
+ const errText = await resp.text();
150
+ throw new Error(`Anthropic batch poll failed (${resp.status}): ${errText.substring(0, 500)}`);
151
+ }
152
+ const batch = await resp.json();
153
+ log.info({
154
+ batchId,
155
+ status: batch.processing_status,
156
+ counts: batch.request_counts,
157
+ elapsed: Date.now() - startTime,
158
+ }, "anthropic batch poll");
159
+ if (batch.processing_status === "ended") {
160
+ return this.fetchAnthropicResults(batchId);
161
+ }
162
+ if (batch.processing_status === "canceling" || batch.processing_status === "canceled") {
163
+ throw new Error(`Anthropic batch ${batchId} was canceled`);
164
+ }
165
+ await sleep(POLL_INTERVAL_MS);
166
+ }
167
+ throw new Error(`Anthropic batch ${batchId} timed out after ${MAX_POLL_DURATION_MS / 1000}s`);
168
+ }
169
+ async fetchAnthropicResults(batchId) {
170
+ const apiKey = process.env.ANTHROPIC_API_KEY;
171
+ if (!apiKey)
172
+ throw new Error("ANTHROPIC_API_KEY not set");
173
+ const resp = await fetch(`${ANTHROPIC_BASE_URL}/v1/messages/batches/${batchId}/results`, {
174
+ headers: {
175
+ "x-api-key": apiKey,
176
+ "anthropic-version": ANTHROPIC_API_VERSION,
177
+ "anthropic-beta": ANTHROPIC_BATCH_BETA,
178
+ },
179
+ });
180
+ if (!resp.ok) {
181
+ const errText = await resp.text();
182
+ throw new Error(`Anthropic batch results fetch failed (${resp.status}): ${errText.substring(0, 500)}`);
183
+ }
184
+ // Results come as JSONL (one JSON object per line)
185
+ const text = await resp.text();
186
+ const results = new Map();
187
+ for (const line of text.split("\n")) {
188
+ if (!line.trim())
189
+ continue;
190
+ try {
191
+ const item = JSON.parse(line);
192
+ if (item.result.type === "succeeded" && item.result.message) {
193
+ const textBlocks = item.result.message.content
194
+ .filter((b) => b.type === "text" && b.text)
195
+ .map((b) => b.text);
196
+ results.set(item.custom_id, {
197
+ id: item.custom_id,
198
+ success: true,
199
+ content: item.result.message.content,
200
+ text: textBlocks.join("\n"),
201
+ usage: item.result.message.usage,
202
+ });
203
+ }
204
+ else {
205
+ results.set(item.custom_id, {
206
+ id: item.custom_id,
207
+ success: false,
208
+ error: item.result.error?.message || `Batch item ${item.result.type}`,
209
+ });
210
+ }
211
+ }
212
+ catch {
213
+ // Skip malformed lines
214
+ }
215
+ }
216
+ log.info({ batchId, resultCount: results.size }, "anthropic batch results fetched");
217
+ return results;
218
+ }
219
+ // ==========================================================================
220
+ // OPENAI BATCH API
221
+ // ==========================================================================
222
+ async submitOpenAIBatch(requests) {
223
+ const apiKey = process.env.OPENAI_API_KEY;
224
+ if (!apiKey)
225
+ throw new Error("OPENAI_API_KEY not set");
226
+ // Step 1: Build JSONL content
227
+ const lines = requests.map(r => {
228
+ const body = {
229
+ model: r.model,
230
+ messages: this.buildOpenAIMessages(r),
231
+ max_tokens: r.max_tokens,
232
+ };
233
+ if (r.tools?.length) {
234
+ body.tools = r.tools.map((t) => ({
235
+ type: "function",
236
+ function: {
237
+ name: t.name,
238
+ description: typeof t.description === "string" ? t.description.slice(0, 4096) : "",
239
+ parameters: t.input_schema || {},
240
+ },
241
+ }));
242
+ }
243
+ if (r.temperature !== undefined)
244
+ body.temperature = r.temperature;
245
+ const item = {
246
+ custom_id: r.id,
247
+ method: "POST",
248
+ url: "/v1/chat/completions",
249
+ body,
250
+ };
251
+ return JSON.stringify(item);
252
+ });
253
+ const jsonlContent = lines.join("\n");
254
+ // Step 2: Upload JSONL as a file
255
+ const formData = new FormData();
256
+ const blob = new Blob([jsonlContent], { type: "application/jsonl" });
257
+ formData.append("file", blob, "batch_requests.jsonl");
258
+ formData.append("purpose", "batch");
259
+ const uploadResp = await fetch(`${OPENAI_BASE_URL}/v1/files`, {
260
+ method: "POST",
261
+ headers: { Authorization: `Bearer ${apiKey}` },
262
+ body: formData,
263
+ });
264
+ if (!uploadResp.ok) {
265
+ const errText = await uploadResp.text();
266
+ throw new Error(`OpenAI file upload failed (${uploadResp.status}): ${errText.substring(0, 500)}`);
267
+ }
268
+ const fileData = await uploadResp.json();
269
+ // Step 3: Create batch
270
+ const batchResp = await fetch(`${OPENAI_BASE_URL}/v1/batches`, {
271
+ method: "POST",
272
+ headers: {
273
+ "Content-Type": "application/json",
274
+ Authorization: `Bearer ${apiKey}`,
275
+ },
276
+ body: JSON.stringify({
277
+ input_file_id: fileData.id,
278
+ endpoint: "/v1/chat/completions",
279
+ completion_window: "24h",
280
+ }),
281
+ });
282
+ if (!batchResp.ok) {
283
+ const errText = await batchResp.text();
284
+ throw new Error(`OpenAI batch create failed (${batchResp.status}): ${errText.substring(0, 500)}`);
285
+ }
286
+ const batch = await batchResp.json();
287
+ log.info({ batchId: batch.id, fileId: fileData.id, count: requests.length }, "openai batch submitted");
288
+ return { batchId: batch.id, provider: "openai", count: requests.length };
289
+ }
290
+ async pollOpenAIBatch(batchId) {
291
+ const apiKey = process.env.OPENAI_API_KEY;
292
+ if (!apiKey)
293
+ throw new Error("OPENAI_API_KEY not set");
294
+ const startTime = Date.now();
295
+ while (Date.now() - startTime < MAX_POLL_DURATION_MS) {
296
+ const resp = await fetch(`${OPENAI_BASE_URL}/v1/batches/${batchId}`, {
297
+ headers: { Authorization: `Bearer ${apiKey}` },
298
+ });
299
+ if (!resp.ok) {
300
+ const errText = await resp.text();
301
+ throw new Error(`OpenAI batch poll failed (${resp.status}): ${errText.substring(0, 500)}`);
302
+ }
303
+ const batch = await resp.json();
304
+ log.info({
305
+ batchId,
306
+ status: batch.status,
307
+ counts: batch.request_counts,
308
+ elapsed: Date.now() - startTime,
309
+ }, "openai batch poll");
310
+ if (batch.status === "completed" && batch.output_file_id) {
311
+ return this.fetchOpenAIResults(batch.output_file_id);
312
+ }
313
+ if (batch.status === "failed" || batch.status === "cancelled" || batch.status === "expired") {
314
+ throw new Error(`OpenAI batch ${batchId} ended with status: ${batch.status}`);
315
+ }
316
+ await sleep(POLL_INTERVAL_MS);
317
+ }
318
+ throw new Error(`OpenAI batch ${batchId} timed out after ${MAX_POLL_DURATION_MS / 1000}s`);
319
+ }
320
+ async fetchOpenAIResults(outputFileId) {
321
+ const apiKey = process.env.OPENAI_API_KEY;
322
+ if (!apiKey)
323
+ throw new Error("OPENAI_API_KEY not set");
324
+ const resp = await fetch(`${OPENAI_BASE_URL}/v1/files/${outputFileId}/content`, {
325
+ headers: { Authorization: `Bearer ${apiKey}` },
326
+ });
327
+ if (!resp.ok) {
328
+ const errText = await resp.text();
329
+ throw new Error(`OpenAI results fetch failed (${resp.status}): ${errText.substring(0, 500)}`);
330
+ }
331
+ const text = await resp.text();
332
+ const results = new Map();
333
+ for (const line of text.split("\n")) {
334
+ if (!line.trim())
335
+ continue;
336
+ try {
337
+ const item = JSON.parse(line);
338
+ if (item.response && item.response.status_code === 200) {
339
+ const choice = item.response.body.choices[0];
340
+ results.set(item.custom_id, {
341
+ id: item.custom_id,
342
+ success: true,
343
+ content: item.response.body.choices,
344
+ text: choice?.message?.content || "",
345
+ usage: {
346
+ input_tokens: item.response.body.usage.prompt_tokens,
347
+ output_tokens: item.response.body.usage.completion_tokens,
348
+ },
349
+ });
350
+ }
351
+ else {
352
+ results.set(item.custom_id, {
353
+ id: item.custom_id,
354
+ success: false,
355
+ error: item.error?.message || `HTTP ${item.response?.status_code || "unknown"}`,
356
+ });
357
+ }
358
+ }
359
+ catch {
360
+ // Skip malformed lines
361
+ }
362
+ }
363
+ log.info({ outputFileId, resultCount: results.size }, "openai batch results fetched");
364
+ return results;
365
+ }
366
+ /**
367
+ * Convert Anthropic-format messages to OpenAI chat format for batch requests.
368
+ * Simplified conversion — handles the common patterns (text, system, tool_use/tool_result).
369
+ */
370
+ buildOpenAIMessages(r) {
371
+ const out = [];
372
+ // System prompt → system message
373
+ if (r.system) {
374
+ if (typeof r.system === "string") {
375
+ out.push({ role: "system", content: r.system });
376
+ }
377
+ else if (Array.isArray(r.system)) {
378
+ const systemText = r.system
379
+ .map(s => s.text || "")
380
+ .filter(Boolean)
381
+ .join("\n");
382
+ if (systemText)
383
+ out.push({ role: "system", content: systemText });
384
+ }
385
+ }
386
+ // Convert messages
387
+ for (const msg of r.messages) {
388
+ if (msg.role === "user") {
389
+ if (typeof msg.content === "string") {
390
+ out.push({ role: "user", content: msg.content });
391
+ }
392
+ else if (Array.isArray(msg.content)) {
393
+ const parts = [];
394
+ for (const block of msg.content) {
395
+ if (block.type === "text" && block.text) {
396
+ parts.push({ type: "text", text: block.text });
397
+ }
398
+ else if (block.type === "tool_result") {
399
+ // OpenAI tool results are separate messages
400
+ let output = "";
401
+ if (typeof block.content === "string")
402
+ output = block.content;
403
+ else if (Array.isArray(block.content)) {
404
+ output = block.content
405
+ .filter(c => c.type === "text")
406
+ .map(c => c.text)
407
+ .join("\n");
408
+ }
409
+ out.push({
410
+ role: "tool",
411
+ tool_call_id: block.tool_use_id,
412
+ content: output || "(no output)",
413
+ });
414
+ }
415
+ }
416
+ if (parts.length === 1 && parts[0].type === "text") {
417
+ out.push({ role: "user", content: parts[0].text });
418
+ }
419
+ else if (parts.length > 0) {
420
+ out.push({ role: "user", content: parts });
421
+ }
422
+ }
423
+ }
424
+ else if (msg.role === "assistant") {
425
+ if (typeof msg.content === "string") {
426
+ out.push({ role: "assistant", content: msg.content });
427
+ }
428
+ else if (Array.isArray(msg.content)) {
429
+ let textContent = "";
430
+ const toolCalls = [];
431
+ for (const block of msg.content) {
432
+ if (block.type === "text" && block.text) {
433
+ textContent += block.text;
434
+ }
435
+ else if (block.type === "tool_use") {
436
+ toolCalls.push({
437
+ id: block.id,
438
+ type: "function",
439
+ function: {
440
+ name: block.name,
441
+ arguments: JSON.stringify(block.input || {}),
442
+ },
443
+ });
444
+ }
445
+ }
446
+ const assistantMsg = { role: "assistant" };
447
+ if (textContent)
448
+ assistantMsg.content = textContent;
449
+ if (toolCalls.length)
450
+ assistantMsg.tool_calls = toolCalls;
451
+ out.push(assistantMsg);
452
+ }
453
+ }
454
+ }
455
+ return out;
456
+ }
457
+ }
458
+ // ============================================================================
459
+ // HELPERS
460
+ // ============================================================================
461
+ function sleep(ms) {
462
+ return new Promise(resolve => setTimeout(resolve, ms));
463
+ }
464
+ // ============================================================================
465
+ // SINGLETON EXPORT
466
+ // ============================================================================
467
+ export const batchClient = new BatchClient();
@@ -0,0 +1,31 @@
1
+ interface ExecuteRequest {
2
+ code: string;
3
+ context: {
4
+ steps: Record<string, unknown>;
5
+ trigger: Record<string, unknown>;
6
+ input?: unknown;
7
+ };
8
+ timeoutMs: number;
9
+ }
10
+ interface ExecuteResult {
11
+ success: boolean;
12
+ output?: unknown;
13
+ error?: string;
14
+ logs?: string[];
15
+ }
16
+ /** Initialize the worker pool. Safe to call multiple times. */
17
+ export declare function initWorkerPool(): void;
18
+ /**
19
+ * Execute code using the worker pool. Falls back to direct fork if pool is unavailable.
20
+ */
21
+ export declare function executeWithPool(req: ExecuteRequest): Promise<ExecuteResult>;
22
+ /** Gracefully shutdown all workers */
23
+ export declare function shutdownPool(): void;
24
+ /** Pool stats for monitoring */
25
+ export declare function getPoolStats(): {
26
+ total: number;
27
+ busy: number;
28
+ idle: number;
29
+ queue: number;
30
+ };
31
+ export {};