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,78 @@
1
+ /**
2
+ * Server Subagent — enables Opus to delegate tool execution to Haiku/Sonnet.
3
+ *
4
+ * The parent agent loop injects all dependencies; this module has zero imports
5
+ * from index.ts (avoids circular deps) and is fully testable in isolation.
6
+ */
7
+ import type Anthropic from "@anthropic-ai/sdk";
8
+ import type { SupabaseClient } from "@supabase/supabase-js";
9
+ export interface SubagentResult {
10
+ success: boolean;
11
+ output: string;
12
+ tokensUsed: {
13
+ input: number;
14
+ output: number;
15
+ };
16
+ costUsd: number;
17
+ toolsUsed: string[];
18
+ turnCount: number;
19
+ }
20
+ export interface SubagentProgressEvent {
21
+ subagentId: string;
22
+ event: "started" | "tool_start" | "tool_result" | "turn" | "done" | "error";
23
+ model?: string;
24
+ toolName?: string;
25
+ toolSuccess?: boolean;
26
+ turn?: number;
27
+ maxTurns?: number;
28
+ output?: string;
29
+ }
30
+ export type SubagentProgressCallback = (event: SubagentProgressEvent) => void;
31
+ export interface RunServerSubagentOptions {
32
+ anthropic: Anthropic;
33
+ supabase: SupabaseClient;
34
+ storeId: string | undefined;
35
+ prompt: string;
36
+ model: "haiku" | "sonnet" | "opus";
37
+ maxTurns: number;
38
+ tools: Array<{
39
+ name: string;
40
+ description: string;
41
+ input_schema: Record<string, unknown>;
42
+ }>;
43
+ executeTool: (toolName: string, args: Record<string, unknown>) => Promise<{
44
+ success: boolean;
45
+ data?: unknown;
46
+ error?: string;
47
+ }>;
48
+ onProgress?: SubagentProgressCallback;
49
+ clientDisconnected: {
50
+ value: boolean;
51
+ };
52
+ startedAt: number;
53
+ maxDurationMs: number;
54
+ }
55
+ export declare const DELEGATE_TASK_TOOL_DEF: {
56
+ name: string;
57
+ description: string;
58
+ input_schema: {
59
+ type: "object";
60
+ properties: {
61
+ prompt: {
62
+ type: string;
63
+ description: string;
64
+ };
65
+ model: {
66
+ type: string;
67
+ enum: string[];
68
+ description: string;
69
+ };
70
+ max_turns: {
71
+ type: string;
72
+ description: string;
73
+ };
74
+ };
75
+ required: string[];
76
+ };
77
+ };
78
+ export declare function runServerSubagent(opts: RunServerSubagentOptions): Promise<SubagentResult>;
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Server Subagent — enables Opus to delegate tool execution to Haiku/Sonnet.
3
+ *
4
+ * The parent agent loop injects all dependencies; this module has zero imports
5
+ * from index.ts (avoids circular deps) and is fully testable in isolation.
6
+ */
7
+ import { randomUUID } from "node:crypto";
8
+ import { LoopDetector, estimateCostUsd, sanitizeError, isRetryableError, } from "../../shared/agent-core.js";
9
+ import { buildAPIRequest } from "../../shared/api-client.js";
10
+ import { MODELS, MODEL_MAP } from "../../shared/constants.js";
11
+ import { dispatchTools } from "../../shared/tool-dispatch.js";
12
+ // ============================================================================
13
+ // TOOL DEFINITION — exposed to parent Opus agent
14
+ // ============================================================================
15
+ export const DELEGATE_TASK_TOOL_DEF = {
16
+ name: "delegate_task",
17
+ description: "Delegate a focused task to a faster sub-agent. Use for: data lookups, " +
18
+ "multi-step tool chains, analytics queries, inventory checks, customer lookups. " +
19
+ "Do NOT delegate decisions requiring your judgment or creative writing.",
20
+ input_schema: {
21
+ type: "object",
22
+ properties: {
23
+ prompt: {
24
+ type: "string",
25
+ description: "Clear instruction for the sub-agent. Include all context needed.",
26
+ },
27
+ model: {
28
+ type: "string",
29
+ enum: ["haiku", "sonnet", "opus"],
30
+ description: "haiku (fast/$1) for simple lookups. sonnet ($3) for multi-step chains. opus ($5) for complex reasoning. Default: haiku.",
31
+ },
32
+ max_turns: {
33
+ type: "number",
34
+ description: "Max agentic turns (1-12). Default: 6.",
35
+ },
36
+ },
37
+ required: ["prompt"],
38
+ },
39
+ };
40
+ // ============================================================================
41
+ // SUBAGENT SYSTEM PROMPT
42
+ // ============================================================================
43
+ const SUBAGENT_SYSTEM_PROMPT = `You are a focused tool executor. Your job is to complete the delegated task using the available tools, then return a clear summary of the results.
44
+
45
+ Rules:
46
+ - Execute tools as needed to fulfill the request
47
+ - Return factual results — do not add opinions or recommendations
48
+ - If a tool fails, try an alternative approach or report the failure
49
+ - Be concise in your final response
50
+ - Always include store_id in tool calls when it's available in your context`;
51
+ // ============================================================================
52
+ // CORE LOOP
53
+ // ============================================================================
54
+ const SUBAGENT_MAX_OUTPUT_TOKENS = 8192;
55
+ export async function runServerSubagent(opts) {
56
+ const { anthropic, prompt, model: modelAlias, maxTurns, tools, executeTool, onProgress, clientDisconnected, startedAt, maxDurationMs, storeId, } = opts;
57
+ const subagentId = `sub-${randomUUID().slice(0, 8)}`;
58
+ const modelId = MODEL_MAP[modelAlias] || MODELS.HAIKU;
59
+ const loopDetector = new LoopDetector();
60
+ const toolsUsed = [];
61
+ let totalIn = 0;
62
+ let totalOut = 0;
63
+ let cacheCreation = 0;
64
+ let cacheRead = 0;
65
+ // Enable thinking for capable models; adjust max tokens for Opus
66
+ const shouldThink = modelAlias === "opus" || modelAlias === "sonnet";
67
+ const effectiveMaxTokens = modelAlias === "opus" ? 16384 : SUBAGENT_MAX_OUTPUT_TOKENS;
68
+ // Context management for subagent: clear at 60K, keep 2, no compaction
69
+ const apiConfig = buildAPIRequest({
70
+ model: modelId,
71
+ contextProfile: "subagent",
72
+ thinkingEnabled: shouldThink,
73
+ maxOutputTokens: effectiveMaxTokens,
74
+ });
75
+ const system = storeId
76
+ ? `${SUBAGENT_SYSTEM_PROMPT}\n\nstore_id: ${storeId}`
77
+ : SUBAGENT_SYSTEM_PROMPT;
78
+ const messages = [{ role: "user", content: prompt }];
79
+ onProgress?.({ subagentId, event: "started", model: modelAlias });
80
+ let turn = 0;
81
+ let lastText = "";
82
+ while (turn < maxTurns) {
83
+ // Abort checks
84
+ if (clientDisconnected.value) {
85
+ return makeResult(false, "Client disconnected", totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
86
+ }
87
+ if (Date.now() - startedAt > maxDurationMs) {
88
+ return makeResult(false, "Parent request timeout", totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
89
+ }
90
+ turn++;
91
+ loopDetector.resetTurn();
92
+ onProgress?.({ subagentId, event: "turn", turn, maxTurns });
93
+ // Non-streaming API call with retry
94
+ let response;
95
+ try {
96
+ response = await withSubagentRetry(async () => {
97
+ return await anthropic.beta.messages.create({
98
+ model: modelId,
99
+ max_tokens: apiConfig.maxTokens,
100
+ temperature: shouldThink ? 1 : 0.3, // Anthropic requires temp=1 with thinking
101
+ system,
102
+ tools: tools,
103
+ messages: messages,
104
+ betas: apiConfig.betas,
105
+ context_management: apiConfig.contextManagement,
106
+ ...(apiConfig.thinking ? { thinking: apiConfig.thinking } : {}),
107
+ });
108
+ });
109
+ }
110
+ catch (err) {
111
+ const errMsg = sanitizeError(err);
112
+ onProgress?.({ subagentId, event: "error", output: errMsg });
113
+ return makeResult(false, `API error: ${errMsg}`, totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
114
+ }
115
+ // Track tokens (including cache for accurate cost)
116
+ totalIn += response.usage?.input_tokens || 0;
117
+ totalOut += response.usage?.output_tokens || 0;
118
+ cacheCreation += response.usage?.cache_creation_input_tokens ?? 0;
119
+ cacheRead += response.usage?.cache_read_input_tokens ?? 0;
120
+ // Extract text and tool_use blocks
121
+ const textBlocks = [];
122
+ const toolUseBlocks = [];
123
+ for (const block of response.content) {
124
+ if (block.type === "text") {
125
+ textBlocks.push(block.text);
126
+ }
127
+ else if (block.type === "tool_use") {
128
+ toolUseBlocks.push({ id: block.id, name: block.name, input: block.input });
129
+ }
130
+ }
131
+ lastText = textBlocks.join("\n");
132
+ // No tool calls → done
133
+ if (toolUseBlocks.length === 0) {
134
+ onProgress?.({ subagentId, event: "done", output: lastText });
135
+ return makeResult(true, lastText, totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
136
+ }
137
+ // Execute tools in parallel via dispatchTools
138
+ const toolExecutor = async (name, input) => {
139
+ toolsUsed.push(name);
140
+ onProgress?.({ subagentId, event: "tool_start", toolName: name });
141
+ try {
142
+ const result = await executeTool(name, input);
143
+ onProgress?.({ subagentId, event: "tool_result", toolName: name, toolSuccess: result.success });
144
+ return {
145
+ success: result.success,
146
+ output: result.success ? JSON.stringify(result.data) : (result.error || "Unknown error"),
147
+ };
148
+ }
149
+ catch (err) {
150
+ const errMsg = sanitizeError(err);
151
+ onProgress?.({ subagentId, event: "tool_result", toolName: name, toolSuccess: false });
152
+ return { success: false, output: errMsg };
153
+ }
154
+ };
155
+ const { results: toolResults, bailOut, bailMessage } = await dispatchTools(toolUseBlocks, toolExecutor, { loopDetector, maxConcurrent: 3 });
156
+ if (bailOut) {
157
+ onProgress?.({ subagentId, event: "error", output: bailMessage });
158
+ return makeResult(false, bailMessage || "Too many errors", totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
159
+ }
160
+ // Append for next turn
161
+ messages.push({
162
+ role: "assistant",
163
+ content: response.content,
164
+ });
165
+ messages.push({
166
+ role: "user",
167
+ content: toolResults,
168
+ });
169
+ }
170
+ // Exhausted max turns
171
+ onProgress?.({ subagentId, event: "done", output: lastText });
172
+ return makeResult(true, lastText || "(subagent completed without text response)", totalIn, totalOut, modelId, toolsUsed, turn, cacheRead, cacheCreation);
173
+ }
174
+ // ============================================================================
175
+ // HELPERS
176
+ // ============================================================================
177
+ function makeResult(success, output, inputTokens, outputTokens, modelId, toolsUsed, turnCount, cacheReadTokens = 0, cacheCreationTokens = 0) {
178
+ return {
179
+ success,
180
+ output,
181
+ tokensUsed: { input: inputTokens, output: outputTokens },
182
+ costUsd: estimateCostUsd(inputTokens, outputTokens, modelId, 0, cacheReadTokens, cacheCreationTokens),
183
+ toolsUsed: [...new Set(toolsUsed)],
184
+ turnCount,
185
+ };
186
+ }
187
+ async function withSubagentRetry(fn, maxRetries = 2) {
188
+ let lastError;
189
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
190
+ try {
191
+ return await fn();
192
+ }
193
+ catch (err) {
194
+ lastError = err;
195
+ if (attempt < maxRetries && isRetryableError(err)) {
196
+ await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
197
+ continue;
198
+ }
199
+ throw err;
200
+ }
201
+ }
202
+ throw lastError;
203
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Session Checkpoint Manager — Phase 4.2
3
+ *
4
+ * Periodically saves conversation state to Supabase so that server restarts
5
+ * don't lose in-progress conversations. Checkpoints are written every 5 turns
6
+ * (fire-and-forget to avoid blocking the agent loop) and can be loaded to
7
+ * resume an interrupted conversation.
8
+ */
9
+ import type { SupabaseClient } from "@supabase/supabase-js";
10
+ export interface Checkpoint {
11
+ conversation_id: string;
12
+ turn: number;
13
+ messages: string;
14
+ tokens_used: {
15
+ input: number;
16
+ output: number;
17
+ cacheRead: number;
18
+ cacheCreation: number;
19
+ };
20
+ cost_so_far: number;
21
+ tools_used: string[];
22
+ created_at: string;
23
+ }
24
+ /**
25
+ * Upsert a checkpoint row for the given conversation.
26
+ *
27
+ * Only actually writes when `turn % CHECKPOINT_INTERVAL === 0` so callers can
28
+ * invoke this every turn without worrying about write frequency.
29
+ *
30
+ * The write is fire-and-forget — errors are logged but never thrown so the
31
+ * agent loop is never blocked.
32
+ */
33
+ export declare function saveCheckpoint(supabase: SupabaseClient, conversationId: string, turn: number, messages: unknown[], tokensSoFar: {
34
+ input: number;
35
+ output: number;
36
+ cacheRead: number;
37
+ cacheCreation: number;
38
+ }, costSoFar: number, toolsUsed: string[]): Promise<void>;
39
+ /**
40
+ * Load the most recent checkpoint for a conversation.
41
+ * Returns null if no checkpoint exists or the table hasn't been created yet.
42
+ */
43
+ export declare function loadCheckpoint(supabase: SupabaseClient, conversationId: string): Promise<Checkpoint | null>;
44
+ /**
45
+ * Find conversations that were in_progress before the given server start time
46
+ * and mark them as resumable. Returns the count of conversations marked.
47
+ *
48
+ * This should be called once during server startup to flag any conversations
49
+ * that were abandoned when the previous server instance went down.
50
+ */
51
+ export declare function markOrphaned(supabase: SupabaseClient, serverStartTime: Date): Promise<number>;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Session Checkpoint Manager — Phase 4.2
3
+ *
4
+ * Periodically saves conversation state to Supabase so that server restarts
5
+ * don't lose in-progress conversations. Checkpoints are written every 5 turns
6
+ * (fire-and-forget to avoid blocking the agent loop) and can be loaded to
7
+ * resume an interrupted conversation.
8
+ */
9
+ /** How often (in turns) to persist a checkpoint. */
10
+ const CHECKPOINT_INTERVAL = 5;
11
+ // ============================================================================
12
+ // SAVE CHECKPOINT
13
+ // ============================================================================
14
+ /**
15
+ * Upsert a checkpoint row for the given conversation.
16
+ *
17
+ * Only actually writes when `turn % CHECKPOINT_INTERVAL === 0` so callers can
18
+ * invoke this every turn without worrying about write frequency.
19
+ *
20
+ * The write is fire-and-forget — errors are logged but never thrown so the
21
+ * agent loop is never blocked.
22
+ */
23
+ export async function saveCheckpoint(supabase, conversationId, turn, messages, tokensSoFar, costSoFar, toolsUsed) {
24
+ // Only checkpoint at the configured interval
25
+ if (turn % CHECKPOINT_INTERVAL !== 0)
26
+ return;
27
+ try {
28
+ const checkpoint = {
29
+ conversation_id: conversationId,
30
+ turn,
31
+ messages: JSON.stringify(messages),
32
+ tokens_used: { ...tokensSoFar },
33
+ cost_so_far: costSoFar,
34
+ tools_used: [...new Set(toolsUsed)],
35
+ created_at: new Date().toISOString(),
36
+ };
37
+ const { error } = await supabase
38
+ .from("ai_conversation_checkpoints")
39
+ .upsert({
40
+ conversation_id: checkpoint.conversation_id,
41
+ turn: checkpoint.turn,
42
+ messages: checkpoint.messages,
43
+ tokens_used: checkpoint.tokens_used,
44
+ cost_so_far: checkpoint.cost_so_far,
45
+ tools_used: checkpoint.tools_used,
46
+ created_at: checkpoint.created_at,
47
+ }, { onConflict: "conversation_id" });
48
+ if (error) {
49
+ // Table may not exist yet — log once and move on
50
+ console.warn(`[checkpoint] Save failed for ${conversationId} turn ${turn}: ${error.message}`);
51
+ }
52
+ else {
53
+ console.log(`[checkpoint] Saved conversation ${conversationId} at turn ${turn}`);
54
+ }
55
+ }
56
+ catch (err) {
57
+ // Graceful degradation — never crash the agent loop
58
+ console.warn(`[checkpoint] Unexpected error saving ${conversationId}: ${err.message}`);
59
+ }
60
+ }
61
+ // ============================================================================
62
+ // LOAD CHECKPOINT
63
+ // ============================================================================
64
+ /**
65
+ * Load the most recent checkpoint for a conversation.
66
+ * Returns null if no checkpoint exists or the table hasn't been created yet.
67
+ */
68
+ export async function loadCheckpoint(supabase, conversationId) {
69
+ try {
70
+ const { data, error } = await supabase
71
+ .from("ai_conversation_checkpoints")
72
+ .select("conversation_id, turn, messages, tokens_used, cost_so_far, tools_used, created_at")
73
+ .eq("conversation_id", conversationId)
74
+ .order("turn", { ascending: false })
75
+ .limit(1)
76
+ .single();
77
+ if (error || !data) {
78
+ if (error && !error.message.includes("0 rows")) {
79
+ console.warn(`[checkpoint] Load failed for ${conversationId}: ${error.message}`);
80
+ }
81
+ return null;
82
+ }
83
+ return {
84
+ conversation_id: data.conversation_id,
85
+ turn: data.turn,
86
+ messages: data.messages,
87
+ tokens_used: data.tokens_used,
88
+ cost_so_far: data.cost_so_far,
89
+ tools_used: data.tools_used ?? [],
90
+ created_at: data.created_at,
91
+ };
92
+ }
93
+ catch (err) {
94
+ console.warn(`[checkpoint] Unexpected error loading ${conversationId}: ${err.message}`);
95
+ return null;
96
+ }
97
+ }
98
+ // ============================================================================
99
+ // MARK ORPHANED CONVERSATIONS
100
+ // ============================================================================
101
+ /**
102
+ * Find conversations that were in_progress before the given server start time
103
+ * and mark them as resumable. Returns the count of conversations marked.
104
+ *
105
+ * This should be called once during server startup to flag any conversations
106
+ * that were abandoned when the previous server instance went down.
107
+ */
108
+ export async function markOrphaned(supabase, serverStartTime) {
109
+ try {
110
+ // Find checkpoints whose created_at is before this server boot AND whose
111
+ // conversation is still in an "active" / "in_progress" state (no final
112
+ // assistant message after the checkpoint timestamp).
113
+ const { data: orphans, error: selectError } = await supabase
114
+ .from("ai_conversation_checkpoints")
115
+ .select("conversation_id, turn, created_at")
116
+ .lt("created_at", serverStartTime.toISOString());
117
+ if (selectError) {
118
+ console.warn(`[checkpoint] markOrphaned select failed: ${selectError.message}`);
119
+ return 0;
120
+ }
121
+ if (!orphans || orphans.length === 0)
122
+ return 0;
123
+ const orphanIds = orphans.map((o) => o.conversation_id);
124
+ // Tag conversations as resumable in their metadata
125
+ const { error: updateError } = await supabase
126
+ .from("ai_conversations")
127
+ .update({
128
+ metadata: {
129
+ resumable: true,
130
+ orphaned_at: serverStartTime.toISOString(),
131
+ },
132
+ })
133
+ .in("id", orphanIds);
134
+ if (updateError) {
135
+ console.warn(`[checkpoint] markOrphaned update failed: ${updateError.message}`);
136
+ return 0;
137
+ }
138
+ console.log(`[checkpoint] Marked ${orphanIds.length} orphaned conversation(s) as resumable`);
139
+ return orphanIds.length;
140
+ }
141
+ catch (err) {
142
+ console.warn(`[checkpoint] markOrphaned unexpected error: ${err.message}`);
143
+ return 0;
144
+ }
145
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Validate a URL is safe to fetch (not targeting internal/private addresses).
3
+ * Does basic hostname checks first, then resolves DNS to catch rebinding attacks.
4
+ *
5
+ * @returns null if safe, or a string error message if blocked.
6
+ */
7
+ export declare function validateUrl(urlStr: string): Promise<string | null>;
8
+ /**
9
+ * Synchronous fast-path check (no DNS resolution).
10
+ * Use this when you can't await, or for backward compatibility.
11
+ * For full protection (including DNS rebinding), use validateUrl().
12
+ */
13
+ export declare function isBlockedUrl(urlStr: string): boolean;