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,689 @@
1
+ /**
2
+ * Teammate — Independent agent instance in a Team
3
+ *
4
+ * Following Anthropic's official patterns:
5
+ * - Runs in separate context window (worker thread)
6
+ * - Works on tasks from shared task list
7
+ * - Can message other teammates directly
8
+ * - Full tool access (not restricted like subagents)
9
+ */
10
+ import { Worker, parentPort, workerData, isMainThread } from "worker_threads";
11
+ import { fileURLToPath } from "url";
12
+ import { loadTeam, claimTask, completeTask, failTask, getAvailableTasks, sendMessage, getUnreadMessages, markMessagesRead, updateTeammate, } from "./team-state.js";
13
+ import { LoopDetector, estimateCostUsd } from "../../shared/agent-core.js";
14
+ import { MODEL_MAP, getProvider } from "../../shared/constants.js";
15
+ import { LOCAL_TOOL_DEFINITIONS, } from "./local-tools.js";
16
+ import { loadServerToolDefinitions, } from "./server-tools.js";
17
+ import { getValidToken } from "./auth-service.js";
18
+ import { logSpan, generateSpanId, generateTraceId, getConversationId, initializeTelemetryClient } from "./telemetry.js";
19
+ import { callAgentAPI, executeToolBlocks, extractTextBlocks, extractToolUseBlocks, } from "./agent-worker-base.js";
20
+ // ============================================================================
21
+ // CONSTANTS
22
+ // ============================================================================
23
+ const MAX_TURNS_PER_TASK = 12; // More turns than subagent since tasks are larger
24
+ const MAX_OUTPUT_TOKENS = 16384;
25
+ const MAX_TEAMMATE_TURNS = 50; // Outer loop safety: max task claim cycles
26
+ const MAX_TEAMMATE_DURATION_MS = 10 * 60 * 1000; // 10 minute hard timeout
27
+ const API_TIMEOUT_MS = 90_000; // 90s timeout on proxy/API fetch calls
28
+ const TOOL_TIMEOUT_MS = 60_000; // 60s timeout on individual tool execution
29
+ // ============================================================================
30
+ // TEAMMATE SYSTEM PROMPT
31
+ // ============================================================================
32
+ function buildTeammatePrompt(teammateName, team, currentTask, cwd) {
33
+ const taskList = team.tasks.map(t => {
34
+ const status = t.status === "in_progress" && t.assignedTo
35
+ ? `in_progress (${team.teammates.find(tm => tm.id === t.assignedTo)?.name || "unknown"})`
36
+ : t.status;
37
+ return `- [${status}] ${t.description}${t.id === currentTask?.id ? " (YOUR TASK)" : ""}`;
38
+ }).join("\n");
39
+ const teammates = team.teammates.map(t => `- ${t.name} (${t.id}): ${t.status}${t.currentTask ? ` - working on task` : ""}`).join("\n");
40
+ return `You are ${teammateName}, a teammate in the "${team.name}" team.
41
+
42
+ ## Working Directory
43
+ ${cwd}
44
+
45
+ ## Your Current Task
46
+ ${currentTask ? `
47
+ **Task ID**: ${currentTask.id}
48
+ **Description**: ${currentTask.description}
49
+ ${currentTask.files?.length ? `**Files to modify**: ${currentTask.files.join(", ")}` : ""}
50
+ ${currentTask.dependencies?.length ? `**Dependencies**: ${currentTask.dependencies.join(", ")} (all completed)` : ""}
51
+ ` : "No task assigned yet. Use team_claim_task to claim an available task."}
52
+
53
+ ## Team Task List
54
+ ${taskList}
55
+
56
+ ## Teammates
57
+ ${teammates}
58
+
59
+ ## Team Communication Tools
60
+ You have special tools to communicate with your team:
61
+ - **team_message**: Send a message to another teammate or the team lead
62
+ - **team_broadcast**: Send a message to all teammates
63
+ - **team_claim_task**: Claim an available task to work on
64
+ - **team_complete_task**: Mark your current task as complete with results
65
+ - **team_check_messages**: Check for messages from teammates
66
+
67
+ ## Subagent Delegation (task tool)
68
+ You can delegate sub-tasks to specialized subagents using the 'task' tool:
69
+ - Use subagent_type="explore" for quick file/code searches
70
+ - Use subagent_type="general-purpose" for autonomous sub-tasks
71
+ - Use model="haiku" for simple lookups (cheapest/fastest)
72
+ - This lets you work on multiple things in parallel while staying focused on your main task
73
+
74
+ ## Guidelines
75
+ 1. Focus on YOUR assigned task - don't work on others' tasks
76
+ 2. Communicate blockers or discoveries that affect other tasks
77
+ 3. When done, use team_complete_task with a clear summary
78
+ 4. Check messages periodically for updates from teammates
79
+ 5. Avoid modifying files assigned to other teammates' tasks
80
+ 6. Delegate research or exploration to subagents when your task requires checking multiple areas
81
+
82
+ ## Self-Monitoring
83
+ If a tool fails, use audit_trail (action="errors") to check patterns before retrying.
84
+ Use audit_trail (action="tool_stats") to see your tool success rates.
85
+ If a tool fails repeatedly, try a different approach instead of retrying the same call.
86
+
87
+ ## Output
88
+ Be concise. Report progress and results clearly. Use tools to do the work.`;
89
+ }
90
+ // ============================================================================
91
+ // TEAM-SPECIFIC TOOLS
92
+ // ============================================================================
93
+ const TEAM_TOOLS = [
94
+ {
95
+ name: "team_message",
96
+ description: "Send a message to a specific teammate or the team lead",
97
+ input_schema: {
98
+ type: "object",
99
+ properties: {
100
+ to: {
101
+ type: "string",
102
+ description: "Teammate ID or 'lead' for team lead",
103
+ },
104
+ message: {
105
+ type: "string",
106
+ description: "Message content",
107
+ },
108
+ },
109
+ required: ["to", "message"],
110
+ },
111
+ },
112
+ {
113
+ name: "team_broadcast",
114
+ description: "Send a message to all teammates",
115
+ input_schema: {
116
+ type: "object",
117
+ properties: {
118
+ message: {
119
+ type: "string",
120
+ description: "Message to broadcast",
121
+ },
122
+ },
123
+ required: ["message"],
124
+ },
125
+ },
126
+ {
127
+ name: "team_claim_task",
128
+ description: "Claim an available task to work on",
129
+ input_schema: {
130
+ type: "object",
131
+ properties: {
132
+ task_id: {
133
+ type: "string",
134
+ description: "ID of the task to claim",
135
+ },
136
+ },
137
+ required: ["task_id"],
138
+ },
139
+ },
140
+ {
141
+ name: "team_complete_task",
142
+ description: "Mark your current task as complete",
143
+ input_schema: {
144
+ type: "object",
145
+ properties: {
146
+ result: {
147
+ type: "string",
148
+ description: "Summary of what was accomplished",
149
+ },
150
+ },
151
+ required: ["result"],
152
+ },
153
+ },
154
+ {
155
+ name: "team_check_messages",
156
+ description: "Check for unread messages from teammates",
157
+ input_schema: {
158
+ type: "object",
159
+ properties: {},
160
+ },
161
+ },
162
+ ];
163
+ // ============================================================================
164
+ // TOOL EXECUTION
165
+ // ============================================================================
166
+ async function executeTeamTool(toolName, input, teamId, teammateId, currentTaskId) {
167
+ switch (toolName) {
168
+ case "team_message": {
169
+ const to = input.to;
170
+ const message = input.message;
171
+ const result = await sendMessage(teamId, teammateId, to, message);
172
+ return result
173
+ ? { success: true, output: `Message sent to ${to}` }
174
+ : { success: false, output: "Failed to send message" };
175
+ }
176
+ case "team_broadcast": {
177
+ const message = input.message;
178
+ const result = await sendMessage(teamId, teammateId, "all", message);
179
+ return result
180
+ ? { success: true, output: "Message broadcast to all teammates" }
181
+ : { success: false, output: "Failed to broadcast message" };
182
+ }
183
+ case "team_claim_task": {
184
+ const taskId = input.task_id;
185
+ const task = await claimTask(teamId, taskId, teammateId);
186
+ return task
187
+ ? { success: true, output: `Claimed task: ${task.description}` }
188
+ : { success: false, output: "Failed to claim task (may be unavailable or have unmet dependencies)" };
189
+ }
190
+ case "team_complete_task": {
191
+ if (!currentTaskId) {
192
+ return { success: false, output: "No task currently assigned" };
193
+ }
194
+ const result = input.result;
195
+ const success = await completeTask(teamId, currentTaskId, result);
196
+ return success
197
+ ? { success: true, output: `Task completed: ${result}` }
198
+ : { success: false, output: "Failed to complete task" };
199
+ }
200
+ case "team_check_messages": {
201
+ const messages = await getUnreadMessages(teamId, teammateId);
202
+ if (messages.length === 0) {
203
+ return { success: true, output: "No unread messages" };
204
+ }
205
+ const msgList = messages.map(m => `From ${m.from}: ${m.content}`).join("\n");
206
+ await markMessagesRead(teamId, messages.map(m => m.id));
207
+ return { success: true, output: `${messages.length} messages:\n${msgList}` };
208
+ }
209
+ default:
210
+ return { success: false, output: `Unknown team tool: ${toolName}` };
211
+ }
212
+ }
213
+ // ============================================================================
214
+ // API CLIENT — uses shared callAgentAPI from agent-worker-base
215
+ // ============================================================================
216
+ async function callAPI(modelId, systemPrompt, messages, tools, thinkingEnabled = false) {
217
+ return callAgentAPI({
218
+ modelId,
219
+ contextProfile: "teammate",
220
+ systemPrompt,
221
+ messages,
222
+ tools,
223
+ thinkingEnabled,
224
+ maxOutputTokens: MAX_OUTPUT_TOKENS,
225
+ timeoutMs: API_TIMEOUT_MS,
226
+ });
227
+ }
228
+ // ============================================================================
229
+ // TEAMMATE WORKER LOOP
230
+ // ============================================================================
231
+ async function runTeammateLoop(data) {
232
+ const { teamId, teammateId, teammateName, model, cwd, parentConversationId, teamName, authToken } = data;
233
+ const modelId = MODEL_MAP[model] || MODEL_MAP.opus; // Inherit parent default
234
+ const startTime = Date.now();
235
+ // Initialize telemetry client with auth token if provided
236
+ if (authToken) {
237
+ initializeTelemetryClient(authToken);
238
+ }
239
+ // Each teammate gets its own conversation_id (separate trace)
240
+ // but links to parent via parent_conversation_id
241
+ const teammateConversationId = getConversationId(); // Worker's own ID
242
+ // Create trace context for this teammate
243
+ const teammateTraceId = generateTraceId();
244
+ const teammateSpanId = generateSpanId();
245
+ // Log teammate start - links to parent for tree hierarchy
246
+ logSpan({
247
+ action: "team.teammate_start",
248
+ durationMs: 0,
249
+ context: {
250
+ traceId: teammateTraceId,
251
+ spanId: teammateSpanId,
252
+ conversationId: teammateConversationId,
253
+ source: "claude_code",
254
+ serviceName: "whale-cli",
255
+ serviceVersion: "2.1.0",
256
+ model: modelId,
257
+ },
258
+ details: {
259
+ is_team: true,
260
+ is_teammate: true,
261
+ team_id: teamId,
262
+ teammate_id: teammateId,
263
+ teammate_name: teammateName,
264
+ team_name: teamName,
265
+ parent_conversation_id: parentConversationId, // Link to parent trace
266
+ model: model,
267
+ display_name: teammateName,
268
+ display_icon: "person.fill",
269
+ display_color: "#3B82F6",
270
+ },
271
+ });
272
+ const report = (msg) => {
273
+ if (parentPort) {
274
+ parentPort.postMessage(msg);
275
+ }
276
+ };
277
+ // Report early — let parent know we're alive before async init
278
+ report({ type: "progress", teammateId, content: `${teammateName} initializing...` });
279
+ // Get all tools (local + server + team)
280
+ const localTools = LOCAL_TOOL_DEFINITIONS.map(t => ({
281
+ name: t.name,
282
+ description: t.description,
283
+ input_schema: t.input_schema,
284
+ }));
285
+ let serverTools = [];
286
+ try {
287
+ serverTools = await loadServerToolDefinitions();
288
+ }
289
+ catch { /* server tools unavailable — continue with local tools only */ }
290
+ // Deduplicate: local tools take priority over server tools with the same name
291
+ const localNames = new Set(localTools.map(t => t.name));
292
+ const uniqueServerTools = serverTools.filter(t => !localNames.has(t.name));
293
+ const allTools = [...localTools, ...uniqueServerTools, ...TEAM_TOOLS];
294
+ const loopDetector = new LoopDetector();
295
+ let totalIn = 0;
296
+ let totalOut = 0;
297
+ let currentTaskId = null;
298
+ let currentTaskModelId = modelId; // Per-task model (defaults to team model)
299
+ let currentTaskThinking = false; // Per-task thinking toggle
300
+ let messages = [];
301
+ let tasksCompleted = 0;
302
+ report({ type: "progress", teammateId, content: `${teammateName} ready (${allTools.length} tools)` });
303
+ // Helper to log teammate completion
304
+ const logTeammateComplete = (reason) => {
305
+ const teammateCost = estimateCostUsd(totalIn, totalOut, modelId);
306
+ logSpan({
307
+ action: "team.teammate_done",
308
+ durationMs: Date.now() - startTime,
309
+ context: {
310
+ traceId: teammateTraceId,
311
+ spanId: generateSpanId(),
312
+ parentSpanId: teammateSpanId,
313
+ conversationId: teammateConversationId,
314
+ source: "claude_code",
315
+ serviceName: "whale-cli",
316
+ serviceVersion: "2.1.0",
317
+ model: modelId,
318
+ inputTokens: totalIn,
319
+ outputTokens: totalOut,
320
+ totalCost: teammateCost,
321
+ },
322
+ details: {
323
+ is_team: true,
324
+ is_teammate: true,
325
+ team_id: teamId,
326
+ teammate_id: teammateId,
327
+ teammate_name: teammateName,
328
+ team_name: teamName,
329
+ parent_conversation_id: parentConversationId,
330
+ tasks_completed: tasksCompleted,
331
+ completion_reason: reason,
332
+ "gen_ai.request.model": modelId,
333
+ "gen_ai.usage.input_tokens": totalIn,
334
+ "gen_ai.usage.output_tokens": totalOut,
335
+ "gen_ai.usage.cost": teammateCost,
336
+ display_name: `${teammateName} done`,
337
+ display_icon: "checkmark.circle.fill",
338
+ display_color: "#10B981",
339
+ },
340
+ });
341
+ };
342
+ // Main work loop - keep working until no more tasks
343
+ let outerTurn = 0;
344
+ while (true) {
345
+ // Safety: max turns guard
346
+ if (++outerTurn > MAX_TEAMMATE_TURNS) {
347
+ logTeammateComplete("max_turns_reached");
348
+ report({ type: "done", teammateId, content: `${teammateName} reached max turns (${MAX_TEAMMATE_TURNS})`, tokensUsed: { input: totalIn, output: totalOut } });
349
+ break;
350
+ }
351
+ // Safety: elapsed time guard
352
+ if (Date.now() - startTime > MAX_TEAMMATE_DURATION_MS) {
353
+ logTeammateComplete("timeout");
354
+ report({ type: "done", teammateId, content: `${teammateName} timed out after ${Math.round(MAX_TEAMMATE_DURATION_MS / 60_000)}min`, tokensUsed: { input: totalIn, output: totalOut } });
355
+ break;
356
+ }
357
+ const team = loadTeam(teamId);
358
+ if (!team || team.status !== "active") {
359
+ logTeammateComplete("team_inactive");
360
+ report({ type: "done", teammateId, content: "Team completed or inactive", tokensUsed: { input: totalIn, output: totalOut } });
361
+ break;
362
+ }
363
+ // Find current task or claim a new one
364
+ const currentTask = currentTaskId
365
+ ? team.tasks.find(t => t.id === currentTaskId)
366
+ : null;
367
+ // If no current task, try to claim one
368
+ if (!currentTask || currentTask.status === "completed") {
369
+ const available = getAvailableTasks(team);
370
+ if (available.length === 0) {
371
+ // No tasks available, check if all done or waiting
372
+ const inProgress = team.tasks.filter(t => t.status === "in_progress").length;
373
+ if (inProgress === 0) {
374
+ logTeammateComplete("all_tasks_done");
375
+ report({ type: "done", teammateId, content: "All tasks completed", tokensUsed: { input: totalIn, output: totalOut } });
376
+ break;
377
+ }
378
+ // Wait and retry
379
+ await new Promise(r => setTimeout(r, 2000));
380
+ continue;
381
+ }
382
+ // Claim first available task
383
+ const claimed = await claimTask(teamId, available[0].id, teammateId);
384
+ if (!claimed) {
385
+ await new Promise(r => setTimeout(r, 1000));
386
+ continue;
387
+ }
388
+ currentTaskId = claimed.id;
389
+ // Resolve per-task model: task-level → team default
390
+ const taskModel = claimed.model || model;
391
+ currentTaskModelId = MODEL_MAP[taskModel] || MODEL_MAP[model] || MODEL_MAP.opus;
392
+ // Enable thinking for capable models (Opus/Sonnet adaptive, others budget/disabled)
393
+ const taskProvider = getProvider(currentTaskModelId);
394
+ currentTaskThinking = (taskProvider === "anthropic" || taskProvider === "bedrock")
395
+ && (currentTaskModelId.includes("opus") || currentTaskModelId.includes("sonnet-4-6") || currentTaskModelId.includes("sonnet-4-5"));
396
+ await updateTeammate(teamId, teammateId, { status: "working", currentTask: currentTaskId });
397
+ report({ type: "task_started", teammateId, taskId: currentTaskId, content: claimed.description });
398
+ // Start fresh conversation for new task
399
+ messages = [{
400
+ role: "user",
401
+ content: `Your task: ${claimed.description}\n\nBegin working on this task. Use the available tools to complete it, then use team_complete_task when done.`,
402
+ }];
403
+ }
404
+ // Build system prompt with current state
405
+ const freshTeam = loadTeam(teamId);
406
+ const freshTask = freshTeam.tasks.find(t => t.id === currentTaskId) || null;
407
+ const systemPrompt = buildTeammatePrompt(teammateName, freshTeam, freshTask, cwd);
408
+ // Agent loop for current task
409
+ let apiError = null;
410
+ for (let turn = 0; turn < MAX_TURNS_PER_TASK; turn++) {
411
+ const apiStart = Date.now();
412
+ let response;
413
+ try {
414
+ response = await callAPI(currentTaskModelId, systemPrompt, messages, allTools, currentTaskThinking);
415
+ }
416
+ catch (err) {
417
+ apiError = err.message || String(err);
418
+ report({ type: "progress", teammateId, content: `API error: ${apiError.slice(0, 80)}` });
419
+ logSpan({
420
+ action: "claude_api_request",
421
+ durationMs: Date.now() - apiStart,
422
+ severity: "error",
423
+ error: apiError || undefined,
424
+ context: {
425
+ traceId: teammateTraceId,
426
+ spanId: generateSpanId(),
427
+ parentSpanId: teammateSpanId,
428
+ conversationId: teammateConversationId,
429
+ source: "claude_code",
430
+ serviceName: "whale-cli",
431
+ serviceVersion: "2.1.0",
432
+ model: currentTaskModelId,
433
+ },
434
+ details: {
435
+ is_team: true,
436
+ is_teammate: true,
437
+ team_id: teamId,
438
+ teammate_id: teammateId,
439
+ teammate_name: teammateName,
440
+ parent_conversation_id: parentConversationId,
441
+ turn_number: turn + 1,
442
+ task_id: currentTaskId,
443
+ },
444
+ });
445
+ break; // Exit inner loop — force-complete handler below will deal with the task
446
+ }
447
+ const apiDuration = Date.now() - apiStart;
448
+ totalIn += response.usage.input_tokens;
449
+ totalOut += response.usage.output_tokens;
450
+ // Log Claude API request telemetry
451
+ const turnCostUsd = estimateCostUsd(response.usage.input_tokens, response.usage.output_tokens, currentTaskModelId);
452
+ logSpan({
453
+ action: "claude_api_request",
454
+ durationMs: apiDuration,
455
+ context: {
456
+ traceId: teammateTraceId,
457
+ spanId: generateSpanId(),
458
+ parentSpanId: teammateSpanId,
459
+ conversationId: teammateConversationId,
460
+ source: "claude_code",
461
+ serviceName: "whale-cli",
462
+ serviceVersion: "2.1.0",
463
+ model: currentTaskModelId,
464
+ inputTokens: response.usage.input_tokens,
465
+ outputTokens: response.usage.output_tokens,
466
+ totalCost: turnCostUsd,
467
+ },
468
+ details: {
469
+ is_team: true,
470
+ is_teammate: true,
471
+ team_id: teamId,
472
+ teammate_id: teammateId,
473
+ teammate_name: teammateName,
474
+ parent_conversation_id: parentConversationId,
475
+ turn_number: turn + 1,
476
+ task_id: currentTaskId,
477
+ stop_reason: response.stop_reason,
478
+ "gen_ai.request.model": currentTaskModelId,
479
+ "gen_ai.usage.input_tokens": response.usage.input_tokens,
480
+ "gen_ai.usage.output_tokens": response.usage.output_tokens,
481
+ "gen_ai.usage.cost": turnCostUsd,
482
+ },
483
+ });
484
+ const textBlocks = extractTextBlocks(response.content);
485
+ const toolBlocks = extractToolUseBlocks(response.content);
486
+ // Report progress
487
+ if (textBlocks.length) {
488
+ report({ type: "progress", teammateId, taskId: currentTaskId || undefined, content: textBlocks[0].text.slice(0, 200) });
489
+ }
490
+ // No tools = done with this turn
491
+ if (toolBlocks.length === 0)
492
+ break;
493
+ // Execute tools using shared executeToolBlocks with team-specific custom executor
494
+ let taskCompleted = false;
495
+ const { toolResults } = await executeToolBlocks({
496
+ toolBlocks,
497
+ loopDetector,
498
+ toolTimeoutMs: TOOL_TIMEOUT_MS,
499
+ customExecutor: async (toolName, input) => {
500
+ // Only handle team tools; return null to fall through to standard routing
501
+ if (!TEAM_TOOLS.some(t => t.name === toolName))
502
+ return null;
503
+ const result = await executeTeamTool(toolName, input, teamId, teammateId, currentTaskId);
504
+ // Check if task was completed
505
+ if (toolName === "team_complete_task" && result.success) {
506
+ taskCompleted = true;
507
+ tasksCompleted++;
508
+ report({ type: "task_completed", teammateId, taskId: currentTaskId || undefined, content: result.output });
509
+ // Log task completion to telemetry
510
+ logSpan({
511
+ action: "team.task_complete",
512
+ durationMs: 0,
513
+ context: {
514
+ traceId: teammateTraceId,
515
+ spanId: generateSpanId(),
516
+ parentSpanId: teammateSpanId,
517
+ conversationId: teammateConversationId,
518
+ source: "claude_code",
519
+ serviceName: "whale-cli",
520
+ serviceVersion: "2.1.0",
521
+ },
522
+ details: {
523
+ is_team: true,
524
+ is_teammate: true,
525
+ team_id: teamId,
526
+ teammate_id: teammateId,
527
+ teammate_name: teammateName,
528
+ parent_conversation_id: parentConversationId,
529
+ task_id: currentTaskId,
530
+ task_result: result.output.slice(0, 500),
531
+ display_name: "Task completed",
532
+ display_icon: "checkmark.square.fill",
533
+ display_color: "#10B981",
534
+ },
535
+ });
536
+ }
537
+ return result;
538
+ },
539
+ callbacks: {
540
+ onToolEnd: (toolName, success, durationMs) => {
541
+ // Determine tool category for telemetry
542
+ const toolCategory = TEAM_TOOLS.some(t => t.name === toolName) ? "team" : "standard";
543
+ logSpan({
544
+ action: `tool.${toolName}`,
545
+ durationMs,
546
+ severity: success ? "info" : "error",
547
+ context: {
548
+ traceId: teammateTraceId,
549
+ spanId: generateSpanId(),
550
+ parentSpanId: teammateSpanId,
551
+ conversationId: teammateConversationId,
552
+ source: "claude_code",
553
+ serviceName: "whale-cli",
554
+ serviceVersion: "2.1.0",
555
+ },
556
+ error: success ? undefined : "(see tool result)",
557
+ details: {
558
+ is_team: true,
559
+ is_teammate: true,
560
+ team_id: teamId,
561
+ teammate_id: teammateId,
562
+ teammate_name: teammateName,
563
+ parent_conversation_id: parentConversationId,
564
+ tool_category: toolCategory,
565
+ },
566
+ });
567
+ },
568
+ },
569
+ });
570
+ // Append to messages
571
+ messages.push({ role: "assistant", content: response.content });
572
+ messages.push({ role: "user", content: toolResults });
573
+ // If task was completed, break out to get next task
574
+ if (taskCompleted) {
575
+ currentTaskId = null;
576
+ await updateTeammate(teamId, teammateId, { status: "idle", currentTask: undefined });
577
+ break;
578
+ }
579
+ }
580
+ // If inner loop ended without completing the task (exhausted turns, API error, or no-tool response)
581
+ if (currentTaskId) {
582
+ const team = loadTeam(teamId);
583
+ const task = team?.tasks.find(t => t.id === currentTaskId);
584
+ if (task && task.status === "in_progress") {
585
+ if (apiError) {
586
+ // API failed — mark task as failed, not completed
587
+ await failTask(teamId, currentTaskId, apiError);
588
+ // Report as progress (not error) — error type causes red flash in UI via handleTeammateFailure double-handling
589
+ report({ type: "progress", teammateId, taskId: currentTaskId, content: `Task failed: ${apiError.slice(0, 80)}` });
590
+ }
591
+ else if (totalIn === 0 && totalOut === 0) {
592
+ // Zero tokens used means no real work was done — fail the task
593
+ await failTask(teamId, currentTaskId, "No API response received (0 tokens)");
594
+ report({ type: "progress", teammateId, taskId: currentTaskId, content: "Task failed: no API response" });
595
+ }
596
+ else {
597
+ // Exhausted turns or model stopped — auto-complete with partial result
598
+ const lastText = messages.length > 0 ? messages[messages.length - 1] : null;
599
+ let partialResult = "Task auto-completed after reaching turn limit.";
600
+ if (lastText && typeof lastText === "object" && "content" in lastText) {
601
+ const content = lastText.content;
602
+ if (typeof content === "string")
603
+ partialResult = content.slice(0, 500);
604
+ else if (Array.isArray(content)) {
605
+ const txt = content.find((b) => b.type === "text");
606
+ if (txt && "text" in txt)
607
+ partialResult = txt.text.slice(0, 500);
608
+ }
609
+ }
610
+ await completeTask(teamId, currentTaskId, partialResult);
611
+ tasksCompleted++;
612
+ report({ type: "task_completed", teammateId, taskId: currentTaskId, content: partialResult });
613
+ }
614
+ }
615
+ currentTaskId = null;
616
+ await updateTeammate(teamId, teammateId, { status: "idle", currentTask: undefined });
617
+ }
618
+ }
619
+ // Final update
620
+ await updateTeammate(teamId, teammateId, {
621
+ status: "done",
622
+ currentTask: undefined,
623
+ tokensUsed: { input: totalIn, output: totalOut },
624
+ });
625
+ }
626
+ // ============================================================================
627
+ // WORKER ENTRY POINT
628
+ // ============================================================================
629
+ if (!isMainThread && parentPort) {
630
+ const data = workerData;
631
+ // Catch ALL errors including module-level and unhandled rejections
632
+ process.on("uncaughtException", (err) => {
633
+ try {
634
+ parentPort.postMessage({
635
+ type: "error",
636
+ teammateId: data.teammateId,
637
+ content: `Uncaught exception: ${err.message || String(err)}`,
638
+ });
639
+ }
640
+ catch { /* last resort — nothing we can do */ }
641
+ process.exit(1);
642
+ });
643
+ process.on("unhandledRejection", (reason) => {
644
+ try {
645
+ parentPort.postMessage({
646
+ type: "error",
647
+ teammateId: data.teammateId,
648
+ content: `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`,
649
+ });
650
+ }
651
+ catch { /* last resort */ }
652
+ });
653
+ runTeammateLoop(data)
654
+ .catch(err => {
655
+ parentPort.postMessage({
656
+ type: "error",
657
+ teammateId: data.teammateId,
658
+ content: err.message || String(err),
659
+ });
660
+ })
661
+ .finally(() => {
662
+ // Force-exit the Worker thread — lingering async resources (HTTP connections,
663
+ // Supabase clients, telemetry timers) prevent natural exit and cause the
664
+ // team to hang forever waiting for worker.on("exit").
665
+ setTimeout(() => process.exit(0), 100); // Brief delay for final messages to flush
666
+ });
667
+ }
668
+ // ============================================================================
669
+ // SPAWN TEAMMATE (from main thread)
670
+ // ============================================================================
671
+ export async function spawnTeammate(teamId, teammateId, teammateName, model, cwd, parentConversationId, teamName) {
672
+ const __filename = fileURLToPath(import.meta.url);
673
+ const workerPath = __filename.replace(".js", ".js"); // Same file
674
+ // Get auth token to pass to worker for telemetry
675
+ const authToken = await getValidToken();
676
+ const worker = new Worker(workerPath, {
677
+ workerData: {
678
+ teamId,
679
+ teammateId,
680
+ teammateName,
681
+ model,
682
+ cwd,
683
+ parentConversationId,
684
+ teamName,
685
+ authToken: authToken || undefined,
686
+ },
687
+ });
688
+ return worker;
689
+ }