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.
- package/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent System — Claude Code-style Task tool for spawning specialized agents
|
|
3
|
+
*
|
|
4
|
+
* Subagents run in isolated context, enabling:
|
|
5
|
+
* - Parallel exploration without polluting main context
|
|
6
|
+
* - Specialized prompts per agent type
|
|
7
|
+
* - Cost optimization (Haiku for simple tasks)
|
|
8
|
+
* - Resume capability for long-running tasks
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, appendFileSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir, tmpdir } from "os";
|
|
13
|
+
import { LOCAL_TOOL_DEFINITIONS, } from "./local-tools.js";
|
|
14
|
+
import { LoopDetector, estimateCostUsd } from "../../shared/agent-core.js";
|
|
15
|
+
import { MODEL_MAP } from "../../shared/constants.js";
|
|
16
|
+
import { loadServerToolDefinitions, } from "./server-tools.js";
|
|
17
|
+
import { logSpan, generateSpanId, generateTraceId } from "./telemetry.js";
|
|
18
|
+
import { loadClaudeMd, getModel } from "./agent-loop.js";
|
|
19
|
+
import { getGlobalEmitter } from "./agent-events.js";
|
|
20
|
+
import { getAgentDefinition } from "./agent-definitions.js";
|
|
21
|
+
import { callAgentAPI, executeToolBlocks, extractTextBlocks, extractToolUseBlocks, getResponseText, yieldToEventLoop, yieldForRender, } from "./agent-worker-base.js";
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// CONSTANTS
|
|
24
|
+
// ============================================================================
|
|
25
|
+
const AGENTS_DIR = join(homedir(), ".swagmanager", "agents");
|
|
26
|
+
// Claude Code pattern: subagents should be discrete tasks, not open-ended
|
|
27
|
+
// Low turn limit prevents infinite loops and forces focused execution
|
|
28
|
+
const MAX_TURNS = 8;
|
|
29
|
+
const SUBAGENT_MAX_OUTPUT_TOKENS = 32_000; // Claude Code parity — subagents get 32K output
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// AGENT PROMPTS — specialized per type
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Build agent prompt with working directory context
|
|
34
|
+
function buildAgentPrompt(type, cwd) {
|
|
35
|
+
const cwdContext = `
|
|
36
|
+
## Working Directory
|
|
37
|
+
You are working in: ${cwd}
|
|
38
|
+
All file paths should be relative to or absolute from this directory.
|
|
39
|
+
IMPORTANT: Focus ONLY on files within this directory. Do not get confused by other projects.
|
|
40
|
+
`;
|
|
41
|
+
// Check for custom agent definition first
|
|
42
|
+
const custom = getAgentDefinition(type);
|
|
43
|
+
if (custom)
|
|
44
|
+
return custom.prompt + cwdContext;
|
|
45
|
+
const prompts = {
|
|
46
|
+
explore: `You are an exploration agent. Your ONLY job is to quickly find specific information in the codebase, then STOP.
|
|
47
|
+
|
|
48
|
+
Tools available:
|
|
49
|
+
- glob: Find files by pattern (e.g., "**/*.ts")
|
|
50
|
+
- grep: Search file contents with regex
|
|
51
|
+
- read_file: Read file contents
|
|
52
|
+
- list_directory: List directory contents
|
|
53
|
+
${cwdContext}
|
|
54
|
+
## STOP CONDITIONS — You MUST stop when ANY of these are met:
|
|
55
|
+
1. You found the specific information requested
|
|
56
|
+
2. You've searched 3+ patterns without finding anything new
|
|
57
|
+
3. You've read the key files that answer the question
|
|
58
|
+
4. You've exhausted reasonable search patterns
|
|
59
|
+
|
|
60
|
+
## Guidelines:
|
|
61
|
+
- Call multiple tools per turn — batch glob + grep + read_file in a single response
|
|
62
|
+
- Read only the necessary files to answer the question
|
|
63
|
+
- DO NOT continue exploring after you have the answer
|
|
64
|
+
- DO NOT read files that aren't directly relevant
|
|
65
|
+
|
|
66
|
+
## Output Format:
|
|
67
|
+
Return a concise summary with:
|
|
68
|
+
- File paths and line numbers for relevant code
|
|
69
|
+
- Key code snippets (brief)
|
|
70
|
+
- Direct answer to the question asked
|
|
71
|
+
|
|
72
|
+
IMPORTANT: Complete in 2-4 turns. If you haven't found it by turn 4, summarize what you found and STOP.`,
|
|
73
|
+
plan: `You are a planning agent. Your ONLY job is to analyze the codebase and create an implementation plan, then STOP.
|
|
74
|
+
|
|
75
|
+
Tools available:
|
|
76
|
+
- glob: Find files by pattern
|
|
77
|
+
- grep: Search file contents
|
|
78
|
+
- read_file: Read file contents
|
|
79
|
+
- list_directory: List directory contents
|
|
80
|
+
${cwdContext}
|
|
81
|
+
## STOP CONDITIONS — You MUST stop when:
|
|
82
|
+
1. You've identified the files that need changes
|
|
83
|
+
2. You've understood the existing patterns/architecture
|
|
84
|
+
3. You've created a concrete plan with specific steps
|
|
85
|
+
|
|
86
|
+
## Guidelines:
|
|
87
|
+
- FIRST: Quick search to find relevant files
|
|
88
|
+
- Read only files needed to understand the architecture
|
|
89
|
+
- DO NOT read every file — focus on entry points and key modules
|
|
90
|
+
- Create the plan as soon as you understand the structure
|
|
91
|
+
|
|
92
|
+
## Output Format — Return EXACTLY this structure:
|
|
93
|
+
### Summary
|
|
94
|
+
[1-2 sentences on the approach]
|
|
95
|
+
|
|
96
|
+
### Files to Modify
|
|
97
|
+
- path/to/file.ts: [what changes]
|
|
98
|
+
|
|
99
|
+
### Implementation Steps
|
|
100
|
+
1. [Specific step]
|
|
101
|
+
2. [Specific step]
|
|
102
|
+
|
|
103
|
+
### Risks
|
|
104
|
+
- [Any considerations]
|
|
105
|
+
|
|
106
|
+
IMPORTANT: Complete in 3-5 turns. If you haven't finished by turn 5, output your best plan and STOP.`,
|
|
107
|
+
"general-purpose": `You are an autonomous agent for discrete tasks. Complete the task, then STOP.
|
|
108
|
+
|
|
109
|
+
Tools available:
|
|
110
|
+
- File operations: read_file, write_file, edit_file, glob, grep
|
|
111
|
+
- Shell: run_command
|
|
112
|
+
- Search: search_files, search_content
|
|
113
|
+
- Web: web_fetch
|
|
114
|
+
${cwdContext}
|
|
115
|
+
## STOP CONDITIONS — You MUST stop when:
|
|
116
|
+
1. The specific task is complete
|
|
117
|
+
2. You've verified the changes work (if applicable)
|
|
118
|
+
3. You encounter a blocker you cannot resolve
|
|
119
|
+
|
|
120
|
+
## Guidelines:
|
|
121
|
+
- Understand what exists before making changes
|
|
122
|
+
- Make targeted changes — don't over-engineer
|
|
123
|
+
- Verify your changes if possible (run tests, check syntax)
|
|
124
|
+
- If blocked, explain the blocker and STOP
|
|
125
|
+
|
|
126
|
+
## Output Format:
|
|
127
|
+
### What I Did
|
|
128
|
+
[Brief summary of actions taken]
|
|
129
|
+
|
|
130
|
+
### Files Modified
|
|
131
|
+
- path/to/file.ts: [change description]
|
|
132
|
+
|
|
133
|
+
### Verification
|
|
134
|
+
[How you verified it works, or N/A]
|
|
135
|
+
|
|
136
|
+
### Issues (if any)
|
|
137
|
+
[Any problems encountered]
|
|
138
|
+
|
|
139
|
+
IMPORTANT: This is a discrete task. Complete it in 4-6 turns maximum. Do not loop.`,
|
|
140
|
+
research: `You are a research agent. Find the specific information requested, then STOP.
|
|
141
|
+
|
|
142
|
+
Tools available:
|
|
143
|
+
- web_fetch: Fetch and parse web content
|
|
144
|
+
- web_search: Search the web
|
|
145
|
+
- read_file: Read local files
|
|
146
|
+
- grep: Search local file contents
|
|
147
|
+
${cwdContext}
|
|
148
|
+
## STOP CONDITIONS — You MUST stop when:
|
|
149
|
+
1. You found the answer to the question
|
|
150
|
+
2. You've checked 2-3 authoritative sources
|
|
151
|
+
3. You've gathered enough information to answer
|
|
152
|
+
|
|
153
|
+
## Guidelines:
|
|
154
|
+
- Search for official documentation first
|
|
155
|
+
- Check 2-3 sources, not 10
|
|
156
|
+
- DO NOT keep searching after you have the answer
|
|
157
|
+
- Summarize findings immediately when you have enough
|
|
158
|
+
|
|
159
|
+
## Output Format:
|
|
160
|
+
### Answer
|
|
161
|
+
[Direct answer to the question]
|
|
162
|
+
|
|
163
|
+
### Key Points
|
|
164
|
+
- [Important detail 1]
|
|
165
|
+
- [Important detail 2]
|
|
166
|
+
|
|
167
|
+
### Sources
|
|
168
|
+
- [URL 1]: [what it says]
|
|
169
|
+
- [URL 2]: [what it says]
|
|
170
|
+
|
|
171
|
+
### Caveats
|
|
172
|
+
[Any version requirements or limitations]
|
|
173
|
+
|
|
174
|
+
IMPORTANT: Complete in 2-4 turns. Stop as soon as you have a good answer.`,
|
|
175
|
+
};
|
|
176
|
+
return prompts[type] || prompts["general-purpose"];
|
|
177
|
+
}
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// AGENT STATE PERSISTENCE
|
|
180
|
+
// ============================================================================
|
|
181
|
+
function ensureAgentsDir() {
|
|
182
|
+
if (!existsSync(AGENTS_DIR))
|
|
183
|
+
mkdirSync(AGENTS_DIR, { recursive: true });
|
|
184
|
+
}
|
|
185
|
+
function saveAgentState(state) {
|
|
186
|
+
ensureAgentsDir();
|
|
187
|
+
const path = join(AGENTS_DIR, `${state.id}.json`);
|
|
188
|
+
writeFileSync(path, JSON.stringify(state, null, 2), "utf-8");
|
|
189
|
+
}
|
|
190
|
+
function loadAgentState(agentId) {
|
|
191
|
+
const path = join(AGENTS_DIR, `${agentId}.json`);
|
|
192
|
+
if (!existsSync(path))
|
|
193
|
+
return null;
|
|
194
|
+
try {
|
|
195
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function generateAgentId() {
|
|
202
|
+
return `agent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
203
|
+
}
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// TOOL FILTERING — restrict tools per agent type
|
|
206
|
+
// ============================================================================
|
|
207
|
+
function getToolsForAgent(type) {
|
|
208
|
+
// Check for custom agent definition with explicit tools
|
|
209
|
+
const custom = getAgentDefinition(type);
|
|
210
|
+
if (custom?.tools && custom.tools.length > 0)
|
|
211
|
+
return custom.tools;
|
|
212
|
+
switch (type) {
|
|
213
|
+
case "explore":
|
|
214
|
+
return ["glob", "grep", "read_file", "list_directory", "search_files", "search_content"];
|
|
215
|
+
case "plan":
|
|
216
|
+
return ["glob", "grep", "read_file", "list_directory", "search_files", "search_content"];
|
|
217
|
+
case "research":
|
|
218
|
+
return ["web_fetch", "web_search", "read_file", "grep", "glob"];
|
|
219
|
+
case "general-purpose":
|
|
220
|
+
// All tools
|
|
221
|
+
return [
|
|
222
|
+
"read_file", "write_file", "edit_file", "list_directory",
|
|
223
|
+
"search_files", "search_content", "run_command",
|
|
224
|
+
"glob", "grep", "notebook_edit", "web_fetch", "tasks",
|
|
225
|
+
];
|
|
226
|
+
default:
|
|
227
|
+
return ["read_file", "glob", "grep"];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function getFilteredTools(type) {
|
|
231
|
+
const allowedNames = new Set(getToolsForAgent(type));
|
|
232
|
+
// Local tools
|
|
233
|
+
const localTools = LOCAL_TOOL_DEFINITIONS
|
|
234
|
+
.filter((t) => allowedNames.has(t.name))
|
|
235
|
+
.map((t) => ({
|
|
236
|
+
name: t.name,
|
|
237
|
+
description: t.description,
|
|
238
|
+
input_schema: t.input_schema,
|
|
239
|
+
}));
|
|
240
|
+
// Add web_search from server tools if allowed
|
|
241
|
+
if (allowedNames.has("web_search")) {
|
|
242
|
+
try {
|
|
243
|
+
const serverTools = await loadServerToolDefinitions();
|
|
244
|
+
const webSearch = serverTools.find((t) => t.name === "web_search");
|
|
245
|
+
if (webSearch)
|
|
246
|
+
localTools.push(webSearch);
|
|
247
|
+
}
|
|
248
|
+
catch { /* server tools unavailable */ }
|
|
249
|
+
}
|
|
250
|
+
return localTools;
|
|
251
|
+
}
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// DISPLAY HELPERS — icons and colors for trace visualization
|
|
254
|
+
// ============================================================================
|
|
255
|
+
function getAgentIcon(type) {
|
|
256
|
+
const icons = {
|
|
257
|
+
explore: "🔍", // Magnifying glass for exploration
|
|
258
|
+
plan: "📋", // Clipboard for planning
|
|
259
|
+
"general-purpose": "🤖", // Robot for autonomous work
|
|
260
|
+
research: "📚", // Books for research
|
|
261
|
+
};
|
|
262
|
+
return icons[type] || "⚙️";
|
|
263
|
+
}
|
|
264
|
+
function getAgentColor(type) {
|
|
265
|
+
const colors = {
|
|
266
|
+
explore: "#3B82F6", // Blue for exploration
|
|
267
|
+
plan: "#8B5CF6", // Purple for planning
|
|
268
|
+
"general-purpose": "#10B981", // Green for general work
|
|
269
|
+
research: "#F59E0B", // Amber for research
|
|
270
|
+
};
|
|
271
|
+
return colors[type] || "#6B7280";
|
|
272
|
+
}
|
|
273
|
+
// ============================================================================
|
|
274
|
+
// SUBAGENT EXECUTION
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Progress emitter — uses event system instead of stderr to avoid UI conflicts
|
|
277
|
+
// The global emitter batches and routes these to ChatApp without causing re-renders
|
|
278
|
+
// NOTE: No module-level mutable state — agentId/turn passed through function params for parallel safety
|
|
279
|
+
function emitSubagentProgress(agentType, agentId, message, turn, toolName) {
|
|
280
|
+
const emitter = getGlobalEmitter();
|
|
281
|
+
emitter.emitSubagentProgress(agentId, agentType, message, turn, toolName);
|
|
282
|
+
}
|
|
283
|
+
export async function runSubagent(options) {
|
|
284
|
+
const { prompt, subagent_type, model, resume, max_turns, name, parentContext, parentTraceContext } = options;
|
|
285
|
+
const agentId = resume || generateAgentId();
|
|
286
|
+
// Inherit parent model when not specified (Anthropic pattern)
|
|
287
|
+
const modelId = model ? MODEL_MAP[model] : getModel();
|
|
288
|
+
const cwd = process.cwd();
|
|
289
|
+
const systemPrompt = buildAgentPrompt(subagent_type, cwd);
|
|
290
|
+
const startTime = Date.now();
|
|
291
|
+
const effectiveMaxTurns = max_turns ? Math.max(1, Math.min(50, max_turns)) : MAX_TURNS;
|
|
292
|
+
// Extract short description from prompt (first sentence or 60 chars)
|
|
293
|
+
const descMatch = prompt.match(/^[^.!?\n]+/);
|
|
294
|
+
const shortDescription = name || (descMatch
|
|
295
|
+
? descMatch[0].slice(0, 60) + (descMatch[0].length > 60 ? "…" : "")
|
|
296
|
+
: prompt.slice(0, 60) + (prompt.length > 60 ? "…" : ""));
|
|
297
|
+
// Emit subagent start event
|
|
298
|
+
const emitter = getGlobalEmitter();
|
|
299
|
+
emitter.emitSubagentStart(agentId, subagent_type, model || "inherited", shortDescription);
|
|
300
|
+
// Load or create agent state
|
|
301
|
+
let state = resume ? loadAgentState(resume) : null;
|
|
302
|
+
if (!state) {
|
|
303
|
+
state = {
|
|
304
|
+
id: agentId,
|
|
305
|
+
type: subagent_type,
|
|
306
|
+
model: modelId,
|
|
307
|
+
messages: [],
|
|
308
|
+
toolsUsed: [],
|
|
309
|
+
createdAt: new Date().toISOString(),
|
|
310
|
+
updatedAt: new Date().toISOString(),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const loopDetector = new LoopDetector();
|
|
314
|
+
// Get filtered tools for this agent type
|
|
315
|
+
const tools = await getFilteredTools(subagent_type);
|
|
316
|
+
// Build full system prompt with optional parent context
|
|
317
|
+
let fullSystemPrompt = systemPrompt;
|
|
318
|
+
if (parentContext) {
|
|
319
|
+
fullSystemPrompt += `\n\n## Parent Conversation Context\n${parentContext}`;
|
|
320
|
+
}
|
|
321
|
+
// Add CLAUDE.md if available
|
|
322
|
+
const claudeMd = loadClaudeMd();
|
|
323
|
+
if (claudeMd) {
|
|
324
|
+
fullSystemPrompt += `\n\n## Project Instructions\n${claudeMd.content}`;
|
|
325
|
+
}
|
|
326
|
+
// Add user prompt to messages
|
|
327
|
+
state.messages.push({ role: "user", content: prompt });
|
|
328
|
+
let totalIn = 0;
|
|
329
|
+
let totalOut = 0;
|
|
330
|
+
let finalText = "";
|
|
331
|
+
// Create subagent span context — inherit parent's trace context for hierarchy
|
|
332
|
+
// IMPORTANT: Don't increment global turn number — subagent is part of parent turn
|
|
333
|
+
const subagentSpanId = generateSpanId();
|
|
334
|
+
const turnCtx = {
|
|
335
|
+
source: "claude_code",
|
|
336
|
+
serviceName: "whale-cli",
|
|
337
|
+
serviceVersion: "2.1.0",
|
|
338
|
+
model: modelId,
|
|
339
|
+
agentId,
|
|
340
|
+
agentName: name || `subagent-${subagent_type}`,
|
|
341
|
+
// Inherit parent's trace to keep hierarchy intact
|
|
342
|
+
traceId: parentTraceContext?.traceId || generateTraceId(),
|
|
343
|
+
spanId: subagentSpanId,
|
|
344
|
+
parentSpanId: parentTraceContext?.spanId, // Parent's spanId becomes our parentSpanId
|
|
345
|
+
conversationId: parentTraceContext?.conversationId,
|
|
346
|
+
turnNumber: parentTraceContext?.turnNumber, // Inherit parent turn, don't increment
|
|
347
|
+
userId: parentTraceContext?.userId,
|
|
348
|
+
userEmail: parentTraceContext?.userEmail,
|
|
349
|
+
traceFlags: 1,
|
|
350
|
+
};
|
|
351
|
+
try {
|
|
352
|
+
for (let turn = 0; turn < effectiveMaxTurns; turn++) {
|
|
353
|
+
// Emit progress and yield before API call to keep UI responsive
|
|
354
|
+
emitSubagentProgress(subagent_type, agentId, `turn ${turn + 1}: calling API...`, turn + 1);
|
|
355
|
+
await yieldForRender(); // Give Ink time to render the progress update
|
|
356
|
+
// Claude Code parity: thinking is DISABLED for all subagents.
|
|
357
|
+
// Subagents don't get extended thinking — all output tokens go to actual content.
|
|
358
|
+
// max_tokens = 32K matching Claude Code's hardcoded subagent default.
|
|
359
|
+
const apiStart = Date.now();
|
|
360
|
+
let response;
|
|
361
|
+
let lastError = null;
|
|
362
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
363
|
+
try {
|
|
364
|
+
response = await callAgentAPI({
|
|
365
|
+
modelId,
|
|
366
|
+
contextProfile: "subagent",
|
|
367
|
+
systemPrompt: fullSystemPrompt,
|
|
368
|
+
messages: state.messages,
|
|
369
|
+
tools,
|
|
370
|
+
thinkingEnabled: false,
|
|
371
|
+
maxOutputTokens: SUBAGENT_MAX_OUTPUT_TOKENS,
|
|
372
|
+
cacheLastTool: true,
|
|
373
|
+
});
|
|
374
|
+
lastError = null;
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
lastError = err;
|
|
379
|
+
if (attempt < 2 && (err.status === 429 || err.status === 500 || err.status === 529)) {
|
|
380
|
+
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
throw err;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (lastError)
|
|
387
|
+
throw lastError;
|
|
388
|
+
const apiDuration = Date.now() - apiStart;
|
|
389
|
+
// Yield after API call
|
|
390
|
+
await yieldToEventLoop();
|
|
391
|
+
totalIn += response.usage.input_tokens;
|
|
392
|
+
totalOut += response.usage.output_tokens;
|
|
393
|
+
// Log API call as child span
|
|
394
|
+
const apiCostUsd = estimateCostUsd(response.usage.input_tokens, response.usage.output_tokens, modelId);
|
|
395
|
+
logSpan({
|
|
396
|
+
action: "claude_api_request",
|
|
397
|
+
durationMs: apiDuration,
|
|
398
|
+
context: {
|
|
399
|
+
...turnCtx,
|
|
400
|
+
spanId: generateSpanId(), // New span for this API call
|
|
401
|
+
parentSpanId: turnCtx.spanId, // Parent is the subagent
|
|
402
|
+
inputTokens: response.usage.input_tokens,
|
|
403
|
+
outputTokens: response.usage.output_tokens,
|
|
404
|
+
totalCost: apiCostUsd,
|
|
405
|
+
model: modelId,
|
|
406
|
+
},
|
|
407
|
+
details: {
|
|
408
|
+
"gen_ai.request.model": modelId,
|
|
409
|
+
"gen_ai.usage.input_tokens": response.usage.input_tokens,
|
|
410
|
+
"gen_ai.usage.output_tokens": response.usage.output_tokens,
|
|
411
|
+
"gen_ai.usage.cost": apiCostUsd,
|
|
412
|
+
agent_id: agentId,
|
|
413
|
+
agent_type: subagent_type,
|
|
414
|
+
turn: turn + 1,
|
|
415
|
+
stop_reason: response.stop_reason,
|
|
416
|
+
is_subagent_api: true,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
// Yield to event loop to keep UI responsive
|
|
420
|
+
await yieldToEventLoop();
|
|
421
|
+
// Extract text and tool use using shared helpers
|
|
422
|
+
const textBlocks = extractTextBlocks(response.content);
|
|
423
|
+
const toolBlocks = extractToolUseBlocks(response.content);
|
|
424
|
+
if (textBlocks.length) {
|
|
425
|
+
finalText = getResponseText(response.content);
|
|
426
|
+
}
|
|
427
|
+
// No tool calls — we're done
|
|
428
|
+
if (toolBlocks.length === 0 || response.stop_reason === "end_turn") {
|
|
429
|
+
emitSubagentProgress(subagent_type, agentId, `done (${turn + 1} turn${turn > 0 ? "s" : ""})`, turn + 1);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
// Emit tool usage progress
|
|
433
|
+
const toolNames = toolBlocks.map(b => b.name).join(", ");
|
|
434
|
+
emitSubagentProgress(subagent_type, agentId, `using: ${toolNames}`, turn + 1);
|
|
435
|
+
// Execute tools using shared executeToolBlocks
|
|
436
|
+
const { toolResults, toolsUsed: turnToolsUsed } = await executeToolBlocks({
|
|
437
|
+
toolBlocks,
|
|
438
|
+
loopDetector,
|
|
439
|
+
callbacks: {
|
|
440
|
+
onToolStart: (toolName, input) => {
|
|
441
|
+
emitSubagentProgress(subagent_type, agentId, `running ${toolName}`, turn + 1, toolName);
|
|
442
|
+
emitter.emitSubagentToolStart(agentId, toolName, input);
|
|
443
|
+
},
|
|
444
|
+
onToolEnd: (toolName, success, durationMs) => {
|
|
445
|
+
emitter.emitSubagentToolEnd(agentId, toolName, success, durationMs);
|
|
446
|
+
// Log tool call as child span of the subagent
|
|
447
|
+
const tu = toolBlocks.find(b => b.name === toolName);
|
|
448
|
+
logSpan({
|
|
449
|
+
action: `tool.${toolName}`,
|
|
450
|
+
durationMs,
|
|
451
|
+
context: {
|
|
452
|
+
...turnCtx,
|
|
453
|
+
spanId: generateSpanId(),
|
|
454
|
+
parentSpanId: turnCtx.spanId,
|
|
455
|
+
},
|
|
456
|
+
error: success ? undefined : "(see tool result)",
|
|
457
|
+
details: {
|
|
458
|
+
tool_type: "subagent_tool",
|
|
459
|
+
tool_input: tu?.input,
|
|
460
|
+
agent_id: agentId,
|
|
461
|
+
agent_type: subagent_type,
|
|
462
|
+
is_subagent_tool: true,
|
|
463
|
+
iteration: turn,
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
// Track tool usage
|
|
470
|
+
for (const tn of turnToolsUsed) {
|
|
471
|
+
if (!state.toolsUsed.includes(tn)) {
|
|
472
|
+
state.toolsUsed.push(tn);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Append assistant response + tool results
|
|
476
|
+
state.messages.push({
|
|
477
|
+
role: "assistant",
|
|
478
|
+
content: response.content,
|
|
479
|
+
});
|
|
480
|
+
state.messages.push({
|
|
481
|
+
role: "user",
|
|
482
|
+
content: toolResults,
|
|
483
|
+
});
|
|
484
|
+
// Yield to event loop after tool execution
|
|
485
|
+
await yieldToEventLoop();
|
|
486
|
+
}
|
|
487
|
+
// Save state for potential resume
|
|
488
|
+
state.updatedAt = new Date().toISOString();
|
|
489
|
+
saveAgentState(state);
|
|
490
|
+
// Log the subagent's final response as a chat.assistant_response span
|
|
491
|
+
// This ensures the full output is visible in the telemetry waterfall
|
|
492
|
+
const subFinalCost = estimateCostUsd(totalIn, totalOut, modelId);
|
|
493
|
+
if (finalText) {
|
|
494
|
+
logSpan({
|
|
495
|
+
action: "chat.assistant_response",
|
|
496
|
+
durationMs: Date.now() - startTime,
|
|
497
|
+
context: {
|
|
498
|
+
...turnCtx,
|
|
499
|
+
spanId: generateSpanId(),
|
|
500
|
+
parentSpanId: turnCtx.spanId,
|
|
501
|
+
inputTokens: totalIn,
|
|
502
|
+
outputTokens: totalOut,
|
|
503
|
+
totalCost: subFinalCost,
|
|
504
|
+
model: modelId,
|
|
505
|
+
},
|
|
506
|
+
details: {
|
|
507
|
+
response: finalText,
|
|
508
|
+
agent_id: agentId,
|
|
509
|
+
agent_name: name || `subagent-${subagent_type}`,
|
|
510
|
+
agent_type: subagent_type,
|
|
511
|
+
is_subagent: true,
|
|
512
|
+
"gen_ai.request.model": modelId,
|
|
513
|
+
"gen_ai.usage.input_tokens": totalIn,
|
|
514
|
+
"gen_ai.usage.output_tokens": totalOut,
|
|
515
|
+
"gen_ai.usage.cost": subFinalCost,
|
|
516
|
+
model: modelId,
|
|
517
|
+
input_tokens: totalIn,
|
|
518
|
+
output_tokens: totalOut,
|
|
519
|
+
turn_count: Math.floor(state.messages.length / 2),
|
|
520
|
+
tool_calls: state.toolsUsed.length,
|
|
521
|
+
tool_names: state.toolsUsed,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
// Log telemetry with rich metadata for trace visualization
|
|
526
|
+
const subagentTotalCost = estimateCostUsd(totalIn, totalOut, modelId);
|
|
527
|
+
logSpan({
|
|
528
|
+
action: `subagent.${subagent_type}`,
|
|
529
|
+
durationMs: Date.now() - new Date(state.createdAt).getTime(),
|
|
530
|
+
context: {
|
|
531
|
+
...turnCtx,
|
|
532
|
+
inputTokens: totalIn,
|
|
533
|
+
outputTokens: totalOut,
|
|
534
|
+
totalCost: subagentTotalCost,
|
|
535
|
+
},
|
|
536
|
+
details: {
|
|
537
|
+
// Core identification
|
|
538
|
+
agent_id: agentId,
|
|
539
|
+
agent_type: subagent_type,
|
|
540
|
+
model: modelId,
|
|
541
|
+
// Cost for SwiftUI display
|
|
542
|
+
"gen_ai.request.model": modelId,
|
|
543
|
+
"gen_ai.usage.input_tokens": totalIn,
|
|
544
|
+
"gen_ai.usage.output_tokens": totalOut,
|
|
545
|
+
"gen_ai.usage.cost": subagentTotalCost,
|
|
546
|
+
// Execution metrics
|
|
547
|
+
turns: state.messages.length / 2,
|
|
548
|
+
tools_used: state.toolsUsed,
|
|
549
|
+
tool_count: state.toolsUsed.length,
|
|
550
|
+
// Hierarchy info for visualization
|
|
551
|
+
is_subagent: true,
|
|
552
|
+
parent_trace_id: parentTraceContext?.traceId,
|
|
553
|
+
parent_span_id: parentTraceContext?.spanId,
|
|
554
|
+
// Rich display metadata
|
|
555
|
+
display_name: `${subagent_type.charAt(0).toUpperCase() + subagent_type.slice(1)} Agent`,
|
|
556
|
+
display_icon: getAgentIcon(subagent_type),
|
|
557
|
+
display_color: getAgentColor(subagent_type),
|
|
558
|
+
// Summary for trace feed (2000 chars for meaningful preview)
|
|
559
|
+
summary: finalText.slice(0, 2000) + (finalText.length > 2000 ? "..." : ""),
|
|
560
|
+
prompt_preview: prompt.slice(0, 200) + (prompt.length > 200 ? "..." : ""),
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
// Emit subagent done event
|
|
564
|
+
emitter.emitSubagentDone(agentId, subagent_type, true, finalText || "(No output from agent)", { input: totalIn, output: totalOut }, state.toolsUsed, Date.now() - startTime);
|
|
565
|
+
return {
|
|
566
|
+
success: true,
|
|
567
|
+
output: finalText || "(No output from agent)",
|
|
568
|
+
agentId,
|
|
569
|
+
tokensUsed: { input: totalIn, output: totalOut },
|
|
570
|
+
toolsUsed: state.toolsUsed,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
catch (err) {
|
|
574
|
+
// Emit subagent error event
|
|
575
|
+
emitter.emitSubagentDone(agentId, subagent_type, false, `Agent error: ${err.message || err}`, { input: totalIn, output: totalOut }, state.toolsUsed, Date.now() - startTime);
|
|
576
|
+
return {
|
|
577
|
+
success: false,
|
|
578
|
+
output: `Agent error: ${err.message || err}`,
|
|
579
|
+
agentId,
|
|
580
|
+
tokensUsed: { input: totalIn, output: totalOut },
|
|
581
|
+
toolsUsed: state.toolsUsed,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// ============================================================================
|
|
586
|
+
// BACKGROUND AGENT EXECUTION
|
|
587
|
+
// ============================================================================
|
|
588
|
+
export async function runSubagentBackground(options) {
|
|
589
|
+
const agentId = options.resume || generateAgentId();
|
|
590
|
+
const outputFile = join(tmpdir(), `whale-agent-${agentId}.output`);
|
|
591
|
+
// Write initial status
|
|
592
|
+
writeFileSync(outputFile, `Agent ${agentId} started (${options.subagent_type})\n`, "utf-8");
|
|
593
|
+
// Import background process tracker (dynamic to avoid circular deps)
|
|
594
|
+
const bgModule = await import("./background-processes.js");
|
|
595
|
+
bgModule.registerBackgroundAgent(agentId, options.subagent_type, outputFile);
|
|
596
|
+
// Start agent in detached async — don't await
|
|
597
|
+
runSubagent({ ...options, resume: undefined }).then(result => {
|
|
598
|
+
appendFileSync(outputFile, `\n---DONE---\n${JSON.stringify({ success: result.success, agentId: result.agentId, output: result.output })}\n`, "utf-8");
|
|
599
|
+
import("./background-processes.js").then(m => m.markAgentDone(agentId, result.success));
|
|
600
|
+
}).catch(err => {
|
|
601
|
+
appendFileSync(outputFile, `\n---ERROR---\n${err.message}\n`, "utf-8");
|
|
602
|
+
import("./background-processes.js").then(m => m.markAgentDone(agentId, false));
|
|
603
|
+
});
|
|
604
|
+
return { agentId, outputFile };
|
|
605
|
+
}
|
|
606
|
+
// ============================================================================
|
|
607
|
+
// TOOL DEFINITION — for integration with main agent loop
|
|
608
|
+
// ============================================================================
|
|
609
|
+
// Following Claude Code pattern: discrete, focused tasks with clear completion criteria
|
|
610
|
+
export const TASK_TOOL_DEFINITION = {
|
|
611
|
+
name: "task",
|
|
612
|
+
description: `Launch a subagent in isolated context. Subagents have full tool access and batch multiple tool calls per turn (glob + grep + read_file in parallel). Launch multiple task calls in one response for parallel execution.
|
|
613
|
+
|
|
614
|
+
Use for any work requiring 3+ tool calls or touching multiple parts of the codebase.
|
|
615
|
+
|
|
616
|
+
Agent types:
|
|
617
|
+
- explore: Find specific files/code (2-4 turns, uses batch glob+grep)
|
|
618
|
+
- plan: Create implementation plan (3-5 turns)
|
|
619
|
+
- general-purpose: Complete a specific task (4-8 turns, full tool access)
|
|
620
|
+
- research: Look up specific info (2-4 turns)
|
|
621
|
+
|
|
622
|
+
Each agent completes autonomously and returns results.`,
|
|
623
|
+
input_schema: {
|
|
624
|
+
type: "object",
|
|
625
|
+
properties: {
|
|
626
|
+
prompt: {
|
|
627
|
+
type: "string",
|
|
628
|
+
description: "Specific task with clear completion criteria. Include: what to find/do, where to look, what to return.",
|
|
629
|
+
},
|
|
630
|
+
subagent_type: {
|
|
631
|
+
type: "string",
|
|
632
|
+
enum: ["explore", "plan", "general-purpose", "research"],
|
|
633
|
+
description: "Agent type: explore=find, plan=design, general-purpose=do, research=lookup.",
|
|
634
|
+
},
|
|
635
|
+
model: {
|
|
636
|
+
type: "string",
|
|
637
|
+
enum: ["sonnet", "opus", "haiku"],
|
|
638
|
+
description: "Optional model override. If not specified, inherits from parent. Prefer haiku for quick, straightforward tasks to minimize cost and latency.",
|
|
639
|
+
},
|
|
640
|
+
resume: {
|
|
641
|
+
type: "string",
|
|
642
|
+
description: "Agent ID to resume (rarely needed).",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
required: ["prompt", "subagent_type"],
|
|
646
|
+
},
|
|
647
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Prompt Builder — assembles the full system prompt for the agent loop
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agent-loop.ts for single-responsibility.
|
|
5
|
+
* All consumers should import from agent-loop.ts (re-export facade).
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildSystemPrompt(hasServerTools: boolean, effort?: "low" | "medium" | "high"): Promise<string>;
|