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,24 @@
1
+ /**
2
+ * Zod input validation for server tool dispatch.
3
+ *
4
+ * Only mutation actions are validated — reads (find, get, list, summary, etc.)
5
+ * pass through without schema checks. This prevents malformed AI tool calls
6
+ * from crashing handlers with TypeErrors on unchecked `as` casts.
7
+ */
8
+ export type ValidationResult = {
9
+ valid: true;
10
+ data: Record<string, unknown>;
11
+ } | {
12
+ valid: false;
13
+ error: string;
14
+ };
15
+ /**
16
+ * Validate tool args before dispatch to handler.
17
+ *
18
+ * Returns `{ valid: true, data }` with parsed (coerced) data on success,
19
+ * or `{ valid: false, error }` with a human-readable message on failure.
20
+ *
21
+ * If no schema is registered for the (tool, action) pair, returns passthrough
22
+ * with `{ valid: true, data: args }` — no validation, no blocking.
23
+ */
24
+ export declare function validateToolArgs(toolName: string, args: Record<string, unknown>): ValidationResult;
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Zod input validation for server tool dispatch.
3
+ *
4
+ * Only mutation actions are validated — reads (find, get, list, summary, etc.)
5
+ * pass through without schema checks. This prevents malformed AI tool calls
6
+ * from crashing handlers with TypeErrors on unchecked `as` casts.
7
+ */
8
+ import { z } from "zod";
9
+ // ============================================================================
10
+ // SHARED PRIMITIVES
11
+ // ============================================================================
12
+ const uuid = z.string().uuid();
13
+ const positiveNumber = z.number().positive();
14
+ const nonNegativeNumber = z.number().min(0);
15
+ // ============================================================================
16
+ // INVENTORY MUTATIONS
17
+ // ============================================================================
18
+ const inventoryAdjustSchema = z.object({
19
+ action: z.literal("adjust"),
20
+ product_id: uuid,
21
+ location_id: uuid,
22
+ adjustment: z.number(),
23
+ reason: z.string().optional(),
24
+ });
25
+ const inventorySetSchema = z.object({
26
+ action: z.literal("set"),
27
+ product_id: uuid,
28
+ location_id: uuid,
29
+ quantity: nonNegativeNumber,
30
+ });
31
+ const inventoryTransferSchema = z.object({
32
+ action: z.literal("transfer"),
33
+ product_id: uuid,
34
+ from_location_id: uuid,
35
+ to_location_id: uuid,
36
+ quantity: positiveNumber,
37
+ });
38
+ const inventoryBulkSetSchema = z.object({
39
+ action: z.literal("bulk_set"),
40
+ items: z.array(z.object({
41
+ product_id: uuid,
42
+ location_id: uuid,
43
+ quantity: nonNegativeNumber,
44
+ })).min(1),
45
+ });
46
+ const inventoryBulkAdjustSchema = z.object({
47
+ action: z.literal("bulk_adjust"),
48
+ items: z.array(z.object({
49
+ product_id: uuid,
50
+ location_id: uuid,
51
+ adjustment: z.number(),
52
+ })).min(1),
53
+ });
54
+ const inventoryBulkClearSchema = z.object({
55
+ action: z.literal("bulk_clear"),
56
+ location_id: uuid,
57
+ });
58
+ // ============================================================================
59
+ // SUPPLY CHAIN — PURCHASE ORDERS
60
+ // ============================================================================
61
+ const poCreateSchema = z.object({
62
+ action: z.literal("create"),
63
+ supplier_id: uuid,
64
+ location_id: uuid,
65
+ items: z.array(z.object({
66
+ product_id: uuid,
67
+ quantity: positiveNumber,
68
+ unit_price: nonNegativeNumber.optional(),
69
+ unit_cost: nonNegativeNumber.optional(),
70
+ })).min(1).optional(),
71
+ notes: z.string().optional(),
72
+ expected_delivery_date: z.string().optional(),
73
+ po_type: z.string().optional(),
74
+ });
75
+ const poAddItemsSchema = z.object({
76
+ action: z.literal("add_items"),
77
+ purchase_order_id: uuid,
78
+ items: z.array(z.object({
79
+ product_id: uuid,
80
+ quantity: positiveNumber,
81
+ unit_price: nonNegativeNumber.optional(),
82
+ unit_cost: nonNegativeNumber.optional(),
83
+ })).min(1),
84
+ });
85
+ const poApproveSchema = z.object({
86
+ action: z.literal("approve"),
87
+ purchase_order_id: uuid,
88
+ });
89
+ const poReceiveSchema = z.object({
90
+ action: z.literal("receive"),
91
+ purchase_order_id: uuid,
92
+ });
93
+ const poCancelSchema = z.object({
94
+ action: z.literal("cancel"),
95
+ purchase_order_id: uuid,
96
+ });
97
+ // ============================================================================
98
+ // SUPPLY CHAIN — TRANSFERS
99
+ // ============================================================================
100
+ const transferCreateSchema = z.object({
101
+ action: z.literal("create"),
102
+ from_location_id: uuid.optional(),
103
+ source_location_id: uuid.optional(),
104
+ to_location_id: uuid.optional(),
105
+ destination_location_id: uuid.optional(),
106
+ items: z.array(z.object({
107
+ product_id: uuid,
108
+ quantity: positiveNumber,
109
+ })).min(1).optional(),
110
+ notes: z.string().optional(),
111
+ }).refine((d) => d.from_location_id || d.source_location_id, { message: "from_location_id or source_location_id is required", path: ["from_location_id"] }).refine((d) => d.to_location_id || d.destination_location_id, { message: "to_location_id or destination_location_id is required", path: ["to_location_id"] });
112
+ const transferApproveSchema = z.object({
113
+ action: z.literal("approve"),
114
+ transfer_id: uuid,
115
+ });
116
+ const transferShipSchema = z.object({
117
+ action: z.union([z.literal("ship"), z.literal("mark_in_transit")]),
118
+ transfer_id: uuid,
119
+ });
120
+ const transferReceiveSchema = z.object({
121
+ action: z.union([z.literal("receive"), z.literal("complete")]),
122
+ transfer_id: uuid,
123
+ });
124
+ const transferCancelSchema = z.object({
125
+ action: z.literal("cancel"),
126
+ transfer_id: uuid,
127
+ });
128
+ // ============================================================================
129
+ // CUSTOMERS
130
+ // ============================================================================
131
+ const customerCreateSchema = z.object({
132
+ action: z.literal("create"),
133
+ first_name: z.string().min(1),
134
+ last_name: z.string().min(1),
135
+ email: z.string().email().optional(),
136
+ phone: z.string().optional(),
137
+ date_of_birth: z.string().optional(),
138
+ email_consent: z.boolean().optional(),
139
+ sms_consent: z.boolean().optional(),
140
+ street_address: z.string().optional(),
141
+ city: z.string().optional(),
142
+ state: z.string().optional(),
143
+ postal_code: z.string().optional(),
144
+ drivers_license_number: z.string().optional(),
145
+ medical_card_number: z.string().optional(),
146
+ medical_card_expiry: z.string().optional(),
147
+ });
148
+ const customerUpdateSchema = z.object({
149
+ action: z.literal("update"),
150
+ customer_id: uuid,
151
+ first_name: z.string().min(1).optional(),
152
+ last_name: z.string().min(1).optional(),
153
+ email: z.string().email().optional(),
154
+ phone: z.string().optional(),
155
+ date_of_birth: z.string().optional(),
156
+ status: z.string().optional(),
157
+ email_consent: z.boolean().optional(),
158
+ sms_consent: z.boolean().optional(),
159
+ push_consent: z.boolean().optional(),
160
+ loyalty_points: z.number().optional(),
161
+ loyalty_tier: z.string().optional(),
162
+ street_address: z.string().optional(),
163
+ city: z.string().optional(),
164
+ state: z.string().optional(),
165
+ postal_code: z.string().optional(),
166
+ drivers_license_number: z.string().optional(),
167
+ id_verified: z.boolean().optional(),
168
+ medical_card_number: z.string().optional(),
169
+ medical_card_expiry: z.string().optional(),
170
+ is_wholesale_approved: z.boolean().optional(),
171
+ wholesale_tier: z.string().optional(),
172
+ wholesale_business_name: z.string().optional(),
173
+ wholesale_license_number: z.string().optional(),
174
+ wholesale_tax_id: z.string().optional(),
175
+ });
176
+ const customerMergeSchema = z.object({
177
+ action: z.literal("merge"),
178
+ primary_customer_id: uuid,
179
+ secondary_customer_id: uuid,
180
+ });
181
+ const productMergeSchema = z.object({
182
+ action: z.literal("merge"),
183
+ primary_product_id: uuid,
184
+ secondary_product_id: uuid,
185
+ });
186
+ const productSchemas = {
187
+ merge: productMergeSchema,
188
+ };
189
+ const inventorySchemas = {
190
+ adjust: inventoryAdjustSchema,
191
+ set: inventorySetSchema,
192
+ transfer: inventoryTransferSchema,
193
+ bulk_set: inventoryBulkSetSchema,
194
+ bulk_adjust: inventoryBulkAdjustSchema,
195
+ bulk_clear: inventoryBulkClearSchema,
196
+ };
197
+ const purchaseOrderSchemas = {
198
+ create: poCreateSchema,
199
+ add_items: poAddItemsSchema,
200
+ approve: poApproveSchema,
201
+ mark_ordered: poApproveSchema,
202
+ receive: poReceiveSchema,
203
+ cancel: poCancelSchema,
204
+ };
205
+ const transferSchemas = {
206
+ create: transferCreateSchema,
207
+ approve: transferApproveSchema,
208
+ ship: transferShipSchema,
209
+ mark_in_transit: transferShipSchema,
210
+ receive: transferReceiveSchema,
211
+ complete: transferReceiveSchema,
212
+ cancel: transferCancelSchema,
213
+ };
214
+ const customerSchemas = {
215
+ create: customerCreateSchema,
216
+ update: customerUpdateSchema,
217
+ merge: customerMergeSchema,
218
+ };
219
+ /**
220
+ * Top-level registry keyed by tool name.
221
+ * Only tools with mutation actions are listed.
222
+ * Read-only tools (analytics, orders.find, etc.) are intentionally absent — passthrough.
223
+ */
224
+ const toolSchemaRegistry = {
225
+ products: productSchemas,
226
+ inventory: inventorySchemas,
227
+ purchase_orders: purchaseOrderSchemas,
228
+ transfers: transferSchemas,
229
+ customers: customerSchemas,
230
+ };
231
+ // ============================================================================
232
+ // SUPPLY_CHAIN META-TOOL RESOLUTION
233
+ // ============================================================================
234
+ /**
235
+ * The "supply_chain" tool dispatches by action prefix:
236
+ * po_create → purchase_orders / create
237
+ * transfer_create → transfers / create
238
+ *
239
+ * This helper resolves supply_chain actions to the underlying tool + action.
240
+ */
241
+ function resolveSupplyChainAction(action) {
242
+ if (action.startsWith("po_"))
243
+ return { tool: "purchase_orders", action: action.slice(3) };
244
+ if (action.startsWith("transfer_"))
245
+ return { tool: "transfers", action: action.slice(9) };
246
+ return null;
247
+ }
248
+ /**
249
+ * Validate tool args before dispatch to handler.
250
+ *
251
+ * Returns `{ valid: true, data }` with parsed (coerced) data on success,
252
+ * or `{ valid: false, error }` with a human-readable message on failure.
253
+ *
254
+ * If no schema is registered for the (tool, action) pair, returns passthrough
255
+ * with `{ valid: true, data: args }` — no validation, no blocking.
256
+ */
257
+ export function validateToolArgs(toolName, args) {
258
+ const action = args.action;
259
+ if (!action) {
260
+ // No action param = passthrough (some tools don't use action)
261
+ return { valid: true, data: args };
262
+ }
263
+ let schemaMap;
264
+ if (toolName === "supply_chain") {
265
+ const resolved = resolveSupplyChainAction(action);
266
+ if (resolved) {
267
+ schemaMap = toolSchemaRegistry[resolved.tool];
268
+ // Look up by the resolved action (e.g. "create" not "po_create")
269
+ const schema = schemaMap?.[resolved.action];
270
+ if (!schema)
271
+ return { valid: true, data: args };
272
+ // For supply_chain, we validate using the inner action name
273
+ // Rebuild args with the inner action for schema parsing
274
+ const innerArgs = { ...args, action: resolved.action };
275
+ return runSchema(schema, innerArgs, args);
276
+ }
277
+ // Unknown supply_chain action (e.g. find_suppliers) — passthrough
278
+ return { valid: true, data: args };
279
+ }
280
+ schemaMap = toolSchemaRegistry[toolName];
281
+ if (!schemaMap)
282
+ return { valid: true, data: args };
283
+ const schema = schemaMap[action];
284
+ if (!schema)
285
+ return { valid: true, data: args };
286
+ return runSchema(schema, args, args);
287
+ }
288
+ /**
289
+ * Parse args against schema. On success, merge parsed data back onto original
290
+ * args so extra fields (not in schema) are preserved for the handler.
291
+ */
292
+ function runSchema(schema, parseTarget, originalArgs) {
293
+ const result = schema.safeParse(parseTarget);
294
+ if (!result.success) {
295
+ const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
296
+ return { valid: false, error: `Validation failed: ${issues.join(", ")}` };
297
+ }
298
+ // Merge parsed data back with original args so extra passthrough fields survive
299
+ const parsed = result.data;
300
+ return { valid: true, data: { ...originalArgs, ...parsed } };
301
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * worker.ts — Standalone workflow worker process
3
+ *
4
+ * Phase 3.2: Separates workflow step execution from the HTTP server.
5
+ * Deploy as a separate Fly.io process group so long-running agent conversations
6
+ * on the web process don't compete with step execution.
7
+ *
8
+ * Both processes use claim_step_for_run RPC with FOR UPDATE SKIP LOCKED —
9
+ * no double-claiming even if both web + worker run simultaneously.
10
+ *
11
+ * Usage:
12
+ * node dist/server/worker.js
13
+ *
14
+ * Fly.io config:
15
+ * [processes]
16
+ * web = "node dist/server/index.js"
17
+ * worker = "node dist/server/worker.js"
18
+ */
19
+ export {};
@@ -0,0 +1,201 @@
1
+ /**
2
+ * worker.ts — Standalone workflow worker process
3
+ *
4
+ * Phase 3.2: Separates workflow step execution from the HTTP server.
5
+ * Deploy as a separate Fly.io process group so long-running agent conversations
6
+ * on the web process don't compete with step execution.
7
+ *
8
+ * Both processes use claim_step_for_run RPC with FOR UPDATE SKIP LOCKED —
9
+ * no double-claiming even if both web + worker run simultaneously.
10
+ *
11
+ * Usage:
12
+ * node dist/server/worker.js
13
+ *
14
+ * Fly.io config:
15
+ * [processes]
16
+ * web = "node dist/server/index.js"
17
+ * worker = "node dist/server/worker.js"
18
+ */
19
+ import pg from "pg";
20
+ import { createClient } from "@supabase/supabase-js";
21
+ import { processWorkflowSteps, processWaitingSteps, initWorkerPool, shutdownPool, processScheduleTriggers, enforceWorkflowTimeouts, processEventTriggers, cleanupOrphanedSteps, processDlqRetries, setToolExecutor, } from "./handlers/workflows.js";
22
+ import { executeTool, loadTools } from "./tool-router.js";
23
+ import { createLogger } from "./lib/logger.js";
24
+ const log = createLogger("worker");
25
+ // ============================================================================
26
+ // CONFIG
27
+ // ============================================================================
28
+ const SUPABASE_URL = process.env.SUPABASE_URL || "";
29
+ const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || "";
30
+ const DATABASE_URL = process.env.DATABASE_URL || "";
31
+ const BASE_INTERVAL_MS = 15_000; // Safety net interval (NOTIFY is the fast path)
32
+ const MAX_INTERVAL_MS = 60_000;
33
+ if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) {
34
+ log.fatal("SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are required");
35
+ process.exit(1);
36
+ }
37
+ // ============================================================================
38
+ // SUPABASE CLIENT
39
+ // ============================================================================
40
+ const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, {
41
+ auth: { persistSession: false, autoRefreshToken: false },
42
+ });
43
+ // ============================================================================
44
+ // WORKER LOOP
45
+ // ============================================================================
46
+ let running = false;
47
+ let notifyProcessing = false;
48
+ let consecutiveErrors = 0;
49
+ let currentInterval = BASE_INTERVAL_MS;
50
+ let intervalHandle;
51
+ async function workerTick() {
52
+ if (running || notifyProcessing)
53
+ return;
54
+ running = true;
55
+ try {
56
+ const [stepResult, waitingResolved] = await Promise.all([
57
+ processWorkflowSteps(supabase, 10),
58
+ processWaitingSteps(supabase),
59
+ Promise.resolve(supabase.rpc("expire_pending_waitpoints")).then(() => { }).catch((e) => log.warn({ err: e.message }, "expire_pending_waitpoints failed")),
60
+ ]);
61
+ const [scheduled, timedOut, eventsProcessed, orphansCleaned, dlqRetried] = await Promise.all([
62
+ processScheduleTriggers(supabase).catch((e) => { log.warn({ err: e.message }, "schedule trigger failed"); return 0; }),
63
+ enforceWorkflowTimeouts(supabase).catch((e) => { log.warn({ err: e.message }, "timeout enforcement failed"); return 0; }),
64
+ processEventTriggers(supabase).catch((e) => { log.warn({ err: e.message }, "event trigger failed"); return 0; }),
65
+ cleanupOrphanedSteps(supabase).catch((e) => { log.warn({ err: e.message }, "orphan cleanup failed"); return 0; }),
66
+ processDlqRetries(supabase).catch((e) => { log.warn({ err: e.message }, "DLQ retry failed"); return 0; }),
67
+ ]);
68
+ if (stepResult.processed > 0 || waitingResolved > 0 || scheduled > 0 || timedOut > 0 || eventsProcessed > 0 || orphansCleaned > 0 || dlqRetried > 0) {
69
+ log.info({
70
+ processed: stepResult.processed, errors: stepResult.errors,
71
+ reclaimed: stepResult.reclaimed || 0, waiting: waitingResolved,
72
+ scheduled, timedOut, events: eventsProcessed, orphans: orphansCleaned, dlqRetries: dlqRetried,
73
+ }, "worker tick");
74
+ }
75
+ // Reset backoff on success
76
+ if (consecutiveErrors > 0) {
77
+ consecutiveErrors = 0;
78
+ if (currentInterval !== BASE_INTERVAL_MS) {
79
+ currentInterval = BASE_INTERVAL_MS;
80
+ clearInterval(intervalHandle);
81
+ intervalHandle = setInterval(workerTick, currentInterval);
82
+ log.info({ intervalMs: currentInterval }, "interval reset after recovery");
83
+ }
84
+ }
85
+ }
86
+ catch (err) {
87
+ consecutiveErrors++;
88
+ log.error({ err: err.message, consecutiveErrors }, "worker error");
89
+ if (consecutiveErrors >= 3) {
90
+ const newInterval = Math.min(BASE_INTERVAL_MS * Math.pow(2, consecutiveErrors - 2), MAX_INTERVAL_MS);
91
+ if (newInterval !== currentInterval) {
92
+ currentInterval = newInterval;
93
+ clearInterval(intervalHandle);
94
+ intervalHandle = setInterval(workerTick, currentInterval);
95
+ log.warn({ intervalMs: currentInterval, consecutiveErrors }, "interval increased due to errors");
96
+ }
97
+ }
98
+ }
99
+ finally {
100
+ running = false;
101
+ }
102
+ }
103
+ // ============================================================================
104
+ // NOTIFY LISTENER (fast path)
105
+ // ============================================================================
106
+ let pgClient = null;
107
+ async function setupPgListen() {
108
+ if (!DATABASE_URL) {
109
+ log.warn("no DATABASE_URL — NOTIFY-driven execution disabled, polling only");
110
+ return;
111
+ }
112
+ try {
113
+ const cleanUrl = DATABASE_URL.replace(/[?&]sslmode=[^&]*/g, "").replace(/\?$/, "");
114
+ pgClient = new pg.Client({ connectionString: cleanUrl, ssl: { rejectUnauthorized: false } });
115
+ await pgClient.connect();
116
+ await pgClient.query("LISTEN workflow_step_pending");
117
+ // P1 FIX: Debounce NOTIFY to prevent thundering herd when many steps
118
+ // become pending simultaneously (e.g., for_each expansion).
119
+ let notifyTimer = null;
120
+ let notifyPending = 0;
121
+ pgClient.on("notification", (msg) => {
122
+ if (msg.channel === "workflow_step_pending") {
123
+ notifyPending++;
124
+ if (!notifyTimer) {
125
+ notifyTimer = setTimeout(() => {
126
+ const batch = Math.min(notifyPending, 10);
127
+ notifyPending = 0;
128
+ notifyTimer = null;
129
+ if (notifyProcessing || running)
130
+ return; // Skip if already processing
131
+ notifyProcessing = true;
132
+ processWorkflowSteps(supabase, batch)
133
+ .catch((err) => {
134
+ log.error({ err: err.message }, "NOTIFY step processing failed");
135
+ })
136
+ .finally(() => { notifyProcessing = false; });
137
+ }, 50); // 50ms debounce — still fast, but batches concurrent notifications
138
+ }
139
+ }
140
+ });
141
+ pgClient.on("error", (err) => {
142
+ log.error({ err: err.message }, "pg-listen connection error");
143
+ pgClient = null;
144
+ setTimeout(() => setupPgListen(), 5000);
145
+ });
146
+ log.info("pg LISTEN active on workflow_step_pending");
147
+ }
148
+ catch (err) {
149
+ log.error({ err: err.message }, "pg-listen failed");
150
+ pgClient = null;
151
+ setTimeout(() => setupPgListen(), 5000);
152
+ }
153
+ }
154
+ // ============================================================================
155
+ // STARTUP + SHUTDOWN
156
+ // ============================================================================
157
+ async function start() {
158
+ log.info("starting workflow worker process");
159
+ // Wire tool executor for workflow steps that need tool calls
160
+ setToolExecutor((sb, toolName, args, storeId, traceId) => executeTool(sb, toolName, args, storeId, traceId));
161
+ // Load tools registry
162
+ await loadTools(supabase);
163
+ // Initialize code worker pool
164
+ try {
165
+ initWorkerPool();
166
+ log.info("worker pool initialized");
167
+ }
168
+ catch (err) {
169
+ log.error({ err: err.message }, "worker pool init failed");
170
+ }
171
+ // Start NOTIFY listener
172
+ await setupPgListen();
173
+ // Start polling loop (safety net)
174
+ intervalHandle = setInterval(workerTick, currentInterval);
175
+ log.info({ intervalMs: currentInterval }, "worker loop started");
176
+ // Run first tick immediately
177
+ workerTick();
178
+ }
179
+ function shutdown(signal) {
180
+ log.info({ signal }, "worker shutting down");
181
+ clearInterval(intervalHandle);
182
+ try {
183
+ shutdownPool();
184
+ }
185
+ catch { /* ignore */ }
186
+ if (pgClient) {
187
+ pgClient.end().catch(() => { });
188
+ pgClient = null;
189
+ }
190
+ // Give in-flight work 10s to finish
191
+ setTimeout(() => process.exit(0), 10_000).unref();
192
+ }
193
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
194
+ process.on("SIGINT", () => shutdown("SIGINT"));
195
+ process.on("unhandledRejection", (reason) => {
196
+ log.error({ err: reason }, "unhandled rejection in worker");
197
+ });
198
+ start().catch((err) => {
199
+ log.fatal({ err: err.message }, "worker failed to start");
200
+ process.exit(1);
201
+ });
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SwagManager MCP — Interactive Setup
4
+ *
5
+ * Detects installed MCP-compatible CLIs and writes config for each.
6
+ * Usage: npx swagmanager-mcp setup
7
+ */
8
+ export declare function runSetup(): Promise<void>;