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