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,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Manager — connect to external MCP servers
|
|
3
|
+
*
|
|
4
|
+
* Reads mcpServers from ~/.swagmanager/config.json, spawns/connects each,
|
|
5
|
+
* and exposes their tools with mcp__{server}__{tool} naming convention.
|
|
6
|
+
* Non-fatal: if a server fails to connect, it's skipped with a warning.
|
|
7
|
+
*/
|
|
8
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
9
|
+
declare class McpClientManager {
|
|
10
|
+
private servers;
|
|
11
|
+
private connectPromise;
|
|
12
|
+
/**
|
|
13
|
+
* Connect to all configured MCP servers.
|
|
14
|
+
* Non-blocking: failures are logged but don't prevent startup.
|
|
15
|
+
*/
|
|
16
|
+
connectAll(): Promise<void>;
|
|
17
|
+
private _connectAll;
|
|
18
|
+
private connectServer;
|
|
19
|
+
/**
|
|
20
|
+
* Get all tools from all connected MCP servers.
|
|
21
|
+
*/
|
|
22
|
+
getTools(): Anthropic.Tool[];
|
|
23
|
+
/**
|
|
24
|
+
* Call a tool on a connected MCP server.
|
|
25
|
+
* Tool name format: mcp__{serverName}__{toolName}
|
|
26
|
+
*/
|
|
27
|
+
callTool(fullToolName: string, args: Record<string, unknown>): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
output: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a tool name is an MCP tool.
|
|
33
|
+
*/
|
|
34
|
+
isMcpTool(name: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Get status info about connected servers.
|
|
37
|
+
*/
|
|
38
|
+
getStatus(): {
|
|
39
|
+
name: string;
|
|
40
|
+
toolCount: number;
|
|
41
|
+
}[];
|
|
42
|
+
/**
|
|
43
|
+
* Disconnect all servers gracefully.
|
|
44
|
+
*/
|
|
45
|
+
disconnectAll(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export declare const mcpClientManager: McpClientManager;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Manager — connect to external MCP servers
|
|
3
|
+
*
|
|
4
|
+
* Reads mcpServers from ~/.swagmanager/config.json, spawns/connects each,
|
|
5
|
+
* and exposes their tools with mcp__{server}__{tool} naming convention.
|
|
6
|
+
* Non-fatal: if a server fails to connect, it's skipped with a warning.
|
|
7
|
+
*/
|
|
8
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
10
|
+
import { loadConfig } from "./config-store.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// MCP CLIENT MANAGER
|
|
13
|
+
// ============================================================================
|
|
14
|
+
class McpClientManager {
|
|
15
|
+
servers = new Map();
|
|
16
|
+
connectPromise = null;
|
|
17
|
+
/**
|
|
18
|
+
* Connect to all configured MCP servers.
|
|
19
|
+
* Non-blocking: failures are logged but don't prevent startup.
|
|
20
|
+
*/
|
|
21
|
+
async connectAll() {
|
|
22
|
+
if (this.connectPromise)
|
|
23
|
+
return this.connectPromise;
|
|
24
|
+
this.connectPromise = this._connectAll();
|
|
25
|
+
return this.connectPromise;
|
|
26
|
+
}
|
|
27
|
+
async _connectAll() {
|
|
28
|
+
const config = loadConfig();
|
|
29
|
+
const mcpServers = (config.mcpServers || {});
|
|
30
|
+
const serverNames = Object.keys(mcpServers);
|
|
31
|
+
if (serverNames.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
const results = await Promise.allSettled(serverNames.map(name => this.connectServer(name, mcpServers[name])));
|
|
34
|
+
for (let i = 0; i < results.length; i++) {
|
|
35
|
+
if (results[i].status === "rejected") {
|
|
36
|
+
const err = results[i].reason;
|
|
37
|
+
process.stderr.write(`[mcp] Failed to connect to "${serverNames[i]}": ${err?.message || err}\n`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async connectServer(name, config) {
|
|
42
|
+
const client = new Client({
|
|
43
|
+
name: "whale-code",
|
|
44
|
+
version: "5.1.0",
|
|
45
|
+
});
|
|
46
|
+
let transport;
|
|
47
|
+
if (config.transport === "http" && config.url) {
|
|
48
|
+
// Dynamically import HTTP transport — handles both new StreamableHTTP and legacy SSE
|
|
49
|
+
try {
|
|
50
|
+
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
51
|
+
transport = new StreamableHTTPClientTransport(new URL(config.url));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Fallback to SSE for older SDK versions
|
|
55
|
+
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
56
|
+
transport = new SSEClientTransport(new URL(config.url));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (config.command) {
|
|
60
|
+
transport = new StdioClientTransport({
|
|
61
|
+
command: config.command,
|
|
62
|
+
args: config.args || [],
|
|
63
|
+
env: {
|
|
64
|
+
...process.env,
|
|
65
|
+
...(config.env || {}),
|
|
66
|
+
},
|
|
67
|
+
// Pipe stderr to avoid corrupting our UI
|
|
68
|
+
stderr: "pipe",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`Invalid config: no command or url`);
|
|
73
|
+
}
|
|
74
|
+
// Connect with a 10s timeout
|
|
75
|
+
const connectTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Connection timeout (10s)")), 10_000));
|
|
76
|
+
await Promise.race([client.connect(transport), connectTimeout]);
|
|
77
|
+
// Discover tools
|
|
78
|
+
const toolsResult = await client.listTools();
|
|
79
|
+
const tools = (toolsResult.tools || []).map(t => ({
|
|
80
|
+
name: `mcp__${name}__${t.name}`,
|
|
81
|
+
description: `[${name}] ${t.description || t.name}`,
|
|
82
|
+
input_schema: (t.inputSchema || { type: "object", properties: {} }),
|
|
83
|
+
}));
|
|
84
|
+
this.servers.set(name, { name, client, tools });
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get all tools from all connected MCP servers.
|
|
88
|
+
*/
|
|
89
|
+
getTools() {
|
|
90
|
+
const allTools = [];
|
|
91
|
+
for (const server of this.servers.values()) {
|
|
92
|
+
allTools.push(...server.tools);
|
|
93
|
+
}
|
|
94
|
+
return allTools;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Call a tool on a connected MCP server.
|
|
98
|
+
* Tool name format: mcp__{serverName}__{toolName}
|
|
99
|
+
*/
|
|
100
|
+
async callTool(fullToolName, args) {
|
|
101
|
+
// Parse mcp__{server}__{tool}
|
|
102
|
+
const parts = fullToolName.split("__");
|
|
103
|
+
if (parts.length < 3 || parts[0] !== "mcp") {
|
|
104
|
+
return { success: false, output: `Invalid MCP tool name: ${fullToolName}` };
|
|
105
|
+
}
|
|
106
|
+
const serverName = parts[1];
|
|
107
|
+
const toolName = parts.slice(2).join("__"); // Handle tool names with __ in them
|
|
108
|
+
const server = this.servers.get(serverName);
|
|
109
|
+
if (!server) {
|
|
110
|
+
return { success: false, output: `MCP server "${serverName}" not connected` };
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const result = await server.client.callTool({ name: toolName, arguments: args });
|
|
114
|
+
// Extract text content from the result
|
|
115
|
+
const content = result.content;
|
|
116
|
+
if (Array.isArray(content)) {
|
|
117
|
+
const texts = content
|
|
118
|
+
.filter((c) => c.type === "text")
|
|
119
|
+
.map((c) => c.text);
|
|
120
|
+
return { success: !result.isError, output: texts.join("\n") || JSON.stringify(content) };
|
|
121
|
+
}
|
|
122
|
+
return { success: !result.isError, output: String(content) };
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return { success: false, output: `MCP tool error: ${err.message || err}` };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if a tool name is an MCP tool.
|
|
130
|
+
*/
|
|
131
|
+
isMcpTool(name) {
|
|
132
|
+
return name.startsWith("mcp__");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get status info about connected servers.
|
|
136
|
+
*/
|
|
137
|
+
getStatus() {
|
|
138
|
+
return Array.from(this.servers.values()).map(s => ({
|
|
139
|
+
name: s.name,
|
|
140
|
+
toolCount: s.tools.length,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Disconnect all servers gracefully.
|
|
145
|
+
*/
|
|
146
|
+
async disconnectAll() {
|
|
147
|
+
const closePromises = [];
|
|
148
|
+
for (const server of this.servers.values()) {
|
|
149
|
+
closePromises.push(server.client.close().catch(() => { }));
|
|
150
|
+
}
|
|
151
|
+
await Promise.allSettled(closePromises);
|
|
152
|
+
this.servers.clear();
|
|
153
|
+
this.connectPromise = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Singleton instance
|
|
157
|
+
export const mcpClientManager = new McpClientManager();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Manager — persistent /remember and /forget across sessions
|
|
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 loadMemory(): string;
|
|
8
|
+
export declare function addMemory(fact: string): {
|
|
9
|
+
success: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function removeMemory(pattern: string): {
|
|
13
|
+
success: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function listMemories(): string[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Manager — persistent /remember and /forget across sessions
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agent-loop.ts for single-responsibility.
|
|
5
|
+
* All consumers should import from agent-loop.ts (re-export facade).
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
const MEMORY_DIR = join(homedir(), ".swagmanager", "memory");
|
|
11
|
+
const MEMORY_FILE = join(MEMORY_DIR, "MEMORY.md");
|
|
12
|
+
function ensureMemoryDir() {
|
|
13
|
+
if (!existsSync(MEMORY_DIR))
|
|
14
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
export function loadMemory() {
|
|
17
|
+
if (!existsSync(MEMORY_FILE))
|
|
18
|
+
return "";
|
|
19
|
+
try {
|
|
20
|
+
return readFileSync(MEMORY_FILE, "utf-8").trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function addMemory(fact) {
|
|
27
|
+
ensureMemoryDir();
|
|
28
|
+
const existing = loadMemory();
|
|
29
|
+
const entry = `- ${fact}`;
|
|
30
|
+
// Check for duplicate
|
|
31
|
+
if (existing.includes(fact)) {
|
|
32
|
+
return { success: false, message: "Already remembered." };
|
|
33
|
+
}
|
|
34
|
+
const updated = existing ? existing + "\n" + entry : entry;
|
|
35
|
+
writeFileSync(MEMORY_FILE, updated + "\n", "utf-8");
|
|
36
|
+
return { success: true, message: `Remembered: ${fact}` };
|
|
37
|
+
}
|
|
38
|
+
export function removeMemory(pattern) {
|
|
39
|
+
if (!existsSync(MEMORY_FILE))
|
|
40
|
+
return { success: false, message: "No memories stored." };
|
|
41
|
+
const content = readFileSync(MEMORY_FILE, "utf-8");
|
|
42
|
+
const lines = content.split("\n");
|
|
43
|
+
const lower = pattern.toLowerCase();
|
|
44
|
+
const filtered = lines.filter(line => !line.toLowerCase().includes(lower));
|
|
45
|
+
if (filtered.length === lines.length) {
|
|
46
|
+
return { success: false, message: `No memory matching "${pattern}" found.` };
|
|
47
|
+
}
|
|
48
|
+
const removed = lines.length - filtered.length;
|
|
49
|
+
writeFileSync(MEMORY_FILE, filtered.join("\n"), "utf-8");
|
|
50
|
+
return { success: true, message: `Forgot ${removed} memor${removed === 1 ? "y" : "ies"} matching "${pattern}".` };
|
|
51
|
+
}
|
|
52
|
+
export function listMemories() {
|
|
53
|
+
const content = loadMemory();
|
|
54
|
+
if (!content)
|
|
55
|
+
return [];
|
|
56
|
+
return content.split("\n").filter(l => l.trim().startsWith("- ")).map(l => l.replace(/^- /, "").trim());
|
|
57
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Manager — CLI model selection and cost tracking
|
|
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 setModel(name: string, persist?: boolean): {
|
|
8
|
+
success: boolean;
|
|
9
|
+
model: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Internal: set model by full ID (used for restoring after fallback) */
|
|
13
|
+
export declare function setModelById(modelId: string): void;
|
|
14
|
+
export declare function getModel(): string;
|
|
15
|
+
export declare function getModelShortName(): string;
|
|
16
|
+
/** Check if the current model is set to auto-routing */
|
|
17
|
+
export declare function isAutoRouting(): boolean;
|
|
18
|
+
export declare function estimateCostUsd(inputTokens: number, outputTokens: number, model?: string, thinkingTokens?: number, cacheReadTokens?: number, cacheCreationTokens?: number): number;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Manager — CLI model selection and cost tracking
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agent-loop.ts for single-responsibility.
|
|
5
|
+
* All consumers should import from agent-loop.ts (re-export facade).
|
|
6
|
+
*/
|
|
7
|
+
import { MODEL_MAP } from "../../shared/constants.js";
|
|
8
|
+
import { estimateCostUsd as coreEstimateCostUsd } from "../../shared/agent-core.js";
|
|
9
|
+
import { loadConfig, updateConfig } from "./config-store.js";
|
|
10
|
+
/** CLI-only: current model ID for the active session */
|
|
11
|
+
let activeModel = "claude-opus-4-6";
|
|
12
|
+
// Load persisted default model on import
|
|
13
|
+
try {
|
|
14
|
+
const cfg = loadConfig();
|
|
15
|
+
if (cfg.default_model) {
|
|
16
|
+
const id = MODEL_MAP[cfg.default_model.toLowerCase()];
|
|
17
|
+
if (id)
|
|
18
|
+
activeModel = id;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch { /* use default */ }
|
|
22
|
+
/** Reverse map: full model ID → short key */
|
|
23
|
+
const REVERSE_MAP = {};
|
|
24
|
+
for (const [key, id] of Object.entries(MODEL_MAP)) {
|
|
25
|
+
REVERSE_MAP[id] = key;
|
|
26
|
+
}
|
|
27
|
+
export function setModel(name, persist = true) {
|
|
28
|
+
// Try direct key match first (e.g. "opus", "bedrock-sonnet", "gemini-pro")
|
|
29
|
+
const directId = MODEL_MAP[name.toLowerCase()];
|
|
30
|
+
if (directId) {
|
|
31
|
+
activeModel = directId;
|
|
32
|
+
if (persist)
|
|
33
|
+
try {
|
|
34
|
+
updateConfig({ default_model: name.toLowerCase() });
|
|
35
|
+
}
|
|
36
|
+
catch { /* best effort */ }
|
|
37
|
+
return { success: true, model: activeModel };
|
|
38
|
+
}
|
|
39
|
+
// Legacy: strip "claude-" prefix for backward compat (e.g. "claude-opus" → "opus")
|
|
40
|
+
const legacy = name.toLowerCase().replace(/^claude-?/, "").replace(/-.*/, "");
|
|
41
|
+
const legacyId = MODEL_MAP[legacy];
|
|
42
|
+
if (legacyId) {
|
|
43
|
+
activeModel = legacyId;
|
|
44
|
+
if (persist)
|
|
45
|
+
try {
|
|
46
|
+
updateConfig({ default_model: legacy });
|
|
47
|
+
}
|
|
48
|
+
catch { /* best effort */ }
|
|
49
|
+
return { success: true, model: activeModel };
|
|
50
|
+
}
|
|
51
|
+
return { success: false, model: activeModel, error: `Unknown model: "${name}". Valid: ${Object.keys(MODEL_MAP).join(", ")}` };
|
|
52
|
+
}
|
|
53
|
+
/** Internal: set model by full ID (used for restoring after fallback) */
|
|
54
|
+
export function setModelById(modelId) {
|
|
55
|
+
activeModel = modelId;
|
|
56
|
+
}
|
|
57
|
+
export function getModel() {
|
|
58
|
+
return activeModel;
|
|
59
|
+
}
|
|
60
|
+
export function getModelShortName() {
|
|
61
|
+
if (activeModel === "auto")
|
|
62
|
+
return "auto";
|
|
63
|
+
return REVERSE_MAP[activeModel] || "sonnet";
|
|
64
|
+
}
|
|
65
|
+
/** Check if the current model is set to auto-routing */
|
|
66
|
+
export function isAutoRouting() {
|
|
67
|
+
return activeModel === "auto";
|
|
68
|
+
}
|
|
69
|
+
export function estimateCostUsd(inputTokens, outputTokens, model, thinkingTokens = 0, cacheReadTokens = 0, cacheCreationTokens = 0) {
|
|
70
|
+
return coreEstimateCostUsd(inputTokens, outputTokens, model || activeModel, thinkingTokens, cacheReadTokens, cacheCreationTokens);
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Router — auto-routing via Haiku classifier.
|
|
3
|
+
*
|
|
4
|
+
* When the user selects "auto" as their model, each user message is classified
|
|
5
|
+
* by Haiku to determine the optimal model (haiku/sonnet/opus) based on task
|
|
6
|
+
* complexity. Matches Gemini CLI's auto-routing feature.
|
|
7
|
+
*/
|
|
8
|
+
export type RoutedModel = "haiku" | "sonnet" | "opus";
|
|
9
|
+
export interface RouteResult {
|
|
10
|
+
model: RoutedModel;
|
|
11
|
+
reasoning: string;
|
|
12
|
+
confidence: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Classify a user message to determine the best model.
|
|
16
|
+
* Uses Haiku via server proxy for fast, cheap classification.
|
|
17
|
+
*/
|
|
18
|
+
export declare function classifyAndRoute(userMessage: string, serverUrl: string, authToken: string): Promise<RouteResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if auto-routing is enabled for this model string.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isAutoModel(model: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve "auto" to an actual model ID based on classification.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveAutoModel(userMessage: string, serverUrl: string, authToken: string): Promise<string>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Router — auto-routing via Haiku classifier.
|
|
3
|
+
*
|
|
4
|
+
* When the user selects "auto" as their model, each user message is classified
|
|
5
|
+
* by Haiku to determine the optimal model (haiku/sonnet/opus) based on task
|
|
6
|
+
* complexity. Matches Gemini CLI's auto-routing feature.
|
|
7
|
+
*/
|
|
8
|
+
import { MODELS } from "../../shared/constants.js";
|
|
9
|
+
import { callServerProxy } from "../../shared/api-client.js";
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ============================================================================
|
|
13
|
+
const CLASSIFIER_MODEL = MODELS.HAIKU;
|
|
14
|
+
const CLASSIFIER_PROMPT = `Classify this user request by complexity. Respond with ONLY a JSON object.
|
|
15
|
+
|
|
16
|
+
Categories:
|
|
17
|
+
- "haiku": Simple lookups, single-step tasks, quick questions, status checks, simple edits (1-2 files)
|
|
18
|
+
- "sonnet": Multi-step tasks, code refactoring, moderate analysis, 3-5 file changes, debugging
|
|
19
|
+
- "opus": Complex architecture, large refactors, planning, creative problem solving, 5+ files, ambiguous requirements
|
|
20
|
+
|
|
21
|
+
User request: {message}
|
|
22
|
+
|
|
23
|
+
Respond: {"model": "haiku"|"sonnet"|"opus", "reasoning": "brief reason", "confidence": 0.0-1.0}`;
|
|
24
|
+
const MODEL_ID_MAP = {
|
|
25
|
+
haiku: MODELS.HAIKU,
|
|
26
|
+
sonnet: MODELS.SONNET,
|
|
27
|
+
opus: MODELS.OPUS,
|
|
28
|
+
};
|
|
29
|
+
const DEFAULT_ROUTE = {
|
|
30
|
+
model: "sonnet",
|
|
31
|
+
reasoning: "default fallback",
|
|
32
|
+
confidence: 0.5,
|
|
33
|
+
};
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// CLASSIFIER
|
|
36
|
+
// ============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Classify a user message to determine the best model.
|
|
39
|
+
* Uses Haiku via server proxy for fast, cheap classification.
|
|
40
|
+
*/
|
|
41
|
+
export async function classifyAndRoute(userMessage, serverUrl, authToken) {
|
|
42
|
+
try {
|
|
43
|
+
const prompt = CLASSIFIER_PROMPT.replace("{message}", userMessage);
|
|
44
|
+
const apiConfig = {
|
|
45
|
+
betas: [],
|
|
46
|
+
contextManagement: { edits: [] },
|
|
47
|
+
maxTokens: 256,
|
|
48
|
+
};
|
|
49
|
+
const stream = await callServerProxy({
|
|
50
|
+
proxyUrl: `${serverUrl}/proxy`,
|
|
51
|
+
token: authToken,
|
|
52
|
+
model: CLASSIFIER_MODEL,
|
|
53
|
+
system: [{ type: "text", text: "You are a task complexity classifier. Respond with only valid JSON." }],
|
|
54
|
+
messages: [{ role: "user", content: prompt }],
|
|
55
|
+
tools: [],
|
|
56
|
+
apiConfig,
|
|
57
|
+
timeoutMs: 5000,
|
|
58
|
+
});
|
|
59
|
+
// Collect the full text response from the SSE stream
|
|
60
|
+
const text = await collectText(stream);
|
|
61
|
+
return parseClassifierResponse(text);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// On any error (network, timeout, parse), fall back to sonnet
|
|
65
|
+
return DEFAULT_ROUTE;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if auto-routing is enabled for this model string.
|
|
70
|
+
*/
|
|
71
|
+
export function isAutoModel(model) {
|
|
72
|
+
return model === "auto";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve "auto" to an actual model ID based on classification.
|
|
76
|
+
*/
|
|
77
|
+
export async function resolveAutoModel(userMessage, serverUrl, authToken) {
|
|
78
|
+
const result = await classifyAndRoute(userMessage, serverUrl, authToken);
|
|
79
|
+
return MODEL_ID_MAP[result.model];
|
|
80
|
+
}
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// INTERNAL HELPERS
|
|
83
|
+
// ============================================================================
|
|
84
|
+
/**
|
|
85
|
+
* Collect all text from an SSE stream body.
|
|
86
|
+
* Lightweight extraction — only reads text_delta events.
|
|
87
|
+
*/
|
|
88
|
+
async function collectText(body) {
|
|
89
|
+
const reader = body.getReader();
|
|
90
|
+
const decoder = new TextDecoder();
|
|
91
|
+
let buffer = "";
|
|
92
|
+
let text = "";
|
|
93
|
+
try {
|
|
94
|
+
while (true) {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done)
|
|
97
|
+
break;
|
|
98
|
+
buffer += decoder.decode(value, { stream: true });
|
|
99
|
+
const lines = buffer.split("\n");
|
|
100
|
+
buffer = lines.pop() || "";
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (!trimmed || !trimmed.startsWith("data: "))
|
|
104
|
+
continue;
|
|
105
|
+
const payload = trimmed.slice(6);
|
|
106
|
+
if (payload === "[DONE]")
|
|
107
|
+
return text;
|
|
108
|
+
try {
|
|
109
|
+
const event = JSON.parse(payload);
|
|
110
|
+
if (event.type === "content_block_delta" && event.delta?.type === "text_delta") {
|
|
111
|
+
text += event.delta.text;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch { /* skip malformed JSON */ }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
reader.releaseLock();
|
|
120
|
+
}
|
|
121
|
+
return text;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse the classifier's JSON response into a RouteResult.
|
|
125
|
+
* Falls back to sonnet on any parse failure or low confidence.
|
|
126
|
+
*/
|
|
127
|
+
function parseClassifierResponse(text) {
|
|
128
|
+
try {
|
|
129
|
+
// Extract JSON from response (handle markdown fences or extra text)
|
|
130
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
131
|
+
if (!jsonMatch)
|
|
132
|
+
return DEFAULT_ROUTE;
|
|
133
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
134
|
+
const model = parsed.model;
|
|
135
|
+
if (model !== "haiku" && model !== "sonnet" && model !== "opus") {
|
|
136
|
+
return DEFAULT_ROUTE;
|
|
137
|
+
}
|
|
138
|
+
const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
|
|
139
|
+
const reasoning = typeof parsed.reasoning === "string" ? parsed.reasoning : "";
|
|
140
|
+
// Low confidence defaults to sonnet (safe middle ground)
|
|
141
|
+
if (confidence < 0.3) {
|
|
142
|
+
return { model: "sonnet", reasoning: reasoning || "low confidence fallback", confidence };
|
|
143
|
+
}
|
|
144
|
+
return { model: model, reasoning, confidence };
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return DEFAULT_ROUTE;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Modes — control tool access levels
|
|
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 type PermissionMode = "default" | "plan" | "yolo";
|
|
8
|
+
export declare function setPermissionMode(mode: PermissionMode): {
|
|
9
|
+
success: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function getPermissionMode(): PermissionMode;
|
|
13
|
+
export declare function isToolAllowedByPermission(toolName: string): boolean;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Modes — control tool access levels
|
|
3
|
+
*
|
|
4
|
+
* Extracted from agent-loop.ts for single-responsibility.
|
|
5
|
+
* All consumers should import from agent-loop.ts (re-export facade).
|
|
6
|
+
*/
|
|
7
|
+
import { loadConfig, updateConfig } from "./config-store.js";
|
|
8
|
+
/** CLI-only: current permission mode */
|
|
9
|
+
let activePermissionMode = "default";
|
|
10
|
+
// Load persisted permission mode on import
|
|
11
|
+
try {
|
|
12
|
+
const cfg = loadConfig();
|
|
13
|
+
if (cfg.permission_mode && ["default", "plan", "yolo"].includes(cfg.permission_mode)) {
|
|
14
|
+
activePermissionMode = cfg.permission_mode;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { /* use default */ }
|
|
18
|
+
// Tools allowed in plan mode
|
|
19
|
+
const PLAN_MODE_TOOLS = new Set([
|
|
20
|
+
"read_file", "list_directory", "search_files", "search_content",
|
|
21
|
+
"glob", "grep", "web_fetch", "web_search", "task", "task_output",
|
|
22
|
+
"bash_output", "list_shells", "tasks", "config", "ask_user",
|
|
23
|
+
// Interactive tools (read-only by nature)
|
|
24
|
+
"ask_user_question", "enter_plan_mode", "exit_plan_mode",
|
|
25
|
+
]);
|
|
26
|
+
export function setPermissionMode(mode) {
|
|
27
|
+
activePermissionMode = mode;
|
|
28
|
+
try {
|
|
29
|
+
updateConfig({ permission_mode: mode });
|
|
30
|
+
}
|
|
31
|
+
catch { /* best effort */ }
|
|
32
|
+
return { success: true, message: `Permission mode: ${mode}` };
|
|
33
|
+
}
|
|
34
|
+
export function getPermissionMode() {
|
|
35
|
+
return activePermissionMode;
|
|
36
|
+
}
|
|
37
|
+
export function isToolAllowedByPermission(toolName) {
|
|
38
|
+
switch (activePermissionMode) {
|
|
39
|
+
case "yolo": return true;
|
|
40
|
+
case "plan": return PLAN_MODE_TOOLS.has(toolName);
|
|
41
|
+
case "default": return true; // Default allows all — UI can prompt for confirmation
|
|
42
|
+
}
|
|
43
|
+
}
|