whale-code 6.4.0 → 6.5.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/bin/swagmanager-mcp.js +7 -0
- package/dist/cli/app.js +30 -2
- package/dist/cli/chat/ChatApp.d.ts +4 -4
- package/dist/cli/chat/ChatApp.js +114 -44
- package/dist/cli/chat/ChatInput.d.ts +13 -6
- package/dist/cli/chat/ChatInput.js +433 -89
- package/dist/cli/chat/MemoryManager.d.ts +15 -0
- package/dist/cli/chat/MemoryManager.js +61 -0
- package/dist/cli/chat/MessageList.d.ts +8 -0
- package/dist/cli/chat/MessageList.js +1 -1
- package/dist/cli/chat/NodeManager.d.ts +30 -0
- package/dist/cli/chat/NodeManager.js +89 -0
- package/dist/cli/chat/NodeSelector.d.ts +19 -0
- package/dist/cli/chat/NodeSelector.js +37 -0
- package/dist/cli/chat/PlanApproval.d.ts +17 -0
- package/dist/cli/chat/PlanApproval.js +82 -0
- package/dist/cli/chat/SessionManager.d.ts +16 -0
- package/dist/cli/chat/SessionManager.js +43 -0
- package/dist/cli/chat/SlashMenu.d.ts +38 -0
- package/dist/cli/chat/SlashMenu.js +208 -0
- package/dist/cli/chat/StatusBar.d.ts +16 -0
- package/dist/cli/chat/StatusBar.js +22 -0
- package/dist/cli/chat/ThemeSelector.d.ts +14 -0
- package/dist/cli/chat/ThemeSelector.js +29 -0
- package/dist/cli/chat/ToolIndicator.d.ts +8 -0
- package/dist/cli/chat/ToolIndicator.js +33 -9
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
- package/dist/cli/commands/config-cmd.js +4 -25
- package/dist/cli/commands/db.d.ts +13 -0
- package/dist/cli/commands/db.js +243 -0
- package/dist/cli/commands/doctor.js +6 -9
- package/dist/cli/commands/mcp.js +1 -20
- package/dist/cli/services/agent-events.d.ts +22 -1
- package/dist/cli/services/agent-events.js +9 -0
- package/dist/cli/services/agent-loop.js +66 -2
- package/dist/cli/services/agent-worker-base.js +21 -6
- package/dist/cli/services/api-retry.d.ts +25 -0
- package/dist/cli/services/api-retry.js +91 -0
- package/dist/cli/services/auth-service.d.ts +1 -1
- package/dist/cli/services/auth-service.js +40 -19
- package/dist/cli/services/background-processes.js +26 -2
- package/dist/cli/services/config-store.d.ts +13 -1
- package/dist/cli/services/config-store.js +116 -13
- package/dist/cli/services/format-server-response.js +12 -6
- package/dist/cli/services/ink-resize-fix.d.ts +18 -0
- package/dist/cli/services/ink-resize-fix.js +66 -0
- package/dist/cli/services/interactive-tools.d.ts +14 -0
- package/dist/cli/services/interactive-tools.js +47 -2
- package/dist/cli/services/keybinding-manager.js +1 -1
- package/dist/cli/services/local-tools.js +35 -2
- package/dist/cli/services/server-tools.js +175 -3
- package/dist/cli/services/subagent.js +15 -3
- package/dist/cli/services/system-prompt.js +5 -3
- package/dist/cli/services/task-decomposer.d.ts +35 -0
- package/dist/cli/services/task-decomposer.js +199 -0
- package/dist/cli/services/team-lead.d.ts +18 -0
- package/dist/cli/services/team-lead.js +80 -0
- package/dist/cli/services/teammate.js +5 -5
- package/dist/cli/services/telemetry.d.ts +8 -2
- package/dist/cli/services/telemetry.js +116 -92
- package/dist/cli/services/tools/agent-tools.d.ts +1 -0
- package/dist/cli/services/tools/agent-tools.js +50 -4
- package/dist/cli/services/tools/file-ops.d.ts +2 -0
- package/dist/cli/services/tools/file-ops.js +71 -19
- package/dist/cli/services/tools/shell-exec.js +22 -12
- package/dist/cli/shared/Theme.d.ts +1 -2
- package/dist/cli/shared/Theme.js +1 -1
- package/dist/cli/shared/WhaleBanner.d.ts +4 -1
- package/dist/cli/shared/WhaleBanner.js +12 -8
- package/dist/cli/shared/markdown.d.ts +5 -4
- package/dist/cli/shared/markdown.js +376 -334
- package/dist/cli/shared/theme-manager.d.ts +27 -0
- package/dist/cli/shared/theme-manager.js +178 -0
- package/dist/cli/shared/theme-presets.d.ts +16 -0
- package/dist/cli/shared/theme-presets.js +265 -0
- package/dist/index.js +0 -51
- package/dist/node/adapters/imessage.d.ts +10 -0
- package/dist/node/adapters/imessage.js +45 -6
- package/dist/node/cli.js +459 -8
- package/dist/node/config.d.ts +17 -0
- package/dist/node/gateway-client.d.ts +55 -0
- package/dist/node/gateway-client.js +201 -0
- package/dist/node/portal/clipboard.d.ts +28 -0
- package/dist/node/portal/clipboard.js +183 -0
- package/dist/node/portal/discovery.d.ts +29 -0
- package/dist/node/portal/discovery.js +61 -0
- package/dist/node/portal/forward.d.ts +30 -0
- package/dist/node/portal/forward.js +90 -0
- package/dist/node/portal/index.d.ts +47 -0
- package/dist/node/portal/index.js +250 -0
- package/dist/node/portal/multiplexer.d.ts +48 -0
- package/dist/node/portal/multiplexer.js +207 -0
- package/dist/node/portal/permissions.d.ts +36 -0
- package/dist/node/portal/permissions.js +131 -0
- package/dist/node/portal/protocol.d.ts +140 -0
- package/dist/node/portal/protocol.js +193 -0
- package/dist/node/portal/screen.d.ts +18 -0
- package/dist/node/portal/screen.js +93 -0
- package/dist/node/portal/session.d.ts +68 -0
- package/dist/node/portal/session.js +127 -0
- package/dist/node/portal/shell.d.ts +26 -0
- package/dist/node/portal/shell.js +142 -0
- package/dist/node/portal/stream.d.ts +43 -0
- package/dist/node/portal/stream.js +90 -0
- package/dist/node/portal/transfer.d.ts +33 -0
- package/dist/node/portal/transfer.js +231 -0
- package/dist/node/portal/ui.d.ts +16 -0
- package/dist/node/portal/ui.js +148 -0
- package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
- package/dist/node/remote-desktop/compile-helper.js +73 -0
- package/dist/node/remote-desktop/index.d.ts +67 -0
- package/dist/node/remote-desktop/index.js +220 -0
- package/dist/node/remote-desktop/protocol.d.ts +96 -0
- package/dist/node/remote-desktop/protocol.js +67 -0
- package/dist/node/runtime.d.ts +8 -1
- package/dist/node/runtime.js +117 -9
- package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
- package/dist/server/handlers/__test-utils__/test-db.js +128 -0
- package/dist/server/handlers/api-keys.js +26 -2
- package/dist/server/handlers/browser.d.ts +0 -4
- package/dist/server/handlers/browser.js +0 -46
- package/dist/server/handlers/catalog.js +37 -14
- package/dist/server/handlers/clickhouse.d.ts +10 -0
- package/dist/server/handlers/clickhouse.js +215 -0
- package/dist/server/handlers/comms.d.ts +308 -4
- package/dist/server/handlers/comms.js +444 -11
- package/dist/server/handlers/creations.js +1 -1
- package/dist/server/handlers/crm.d.ts +54 -8
- package/dist/server/handlers/crm.js +353 -68
- package/dist/server/handlers/embeddings.js +3 -3
- package/dist/server/handlers/enrichment.js +39 -55
- package/dist/server/handlers/inventory.js +1 -1
- package/dist/server/handlers/kali.d.ts +9 -1
- package/dist/server/handlers/kali.js +50 -1
- package/dist/server/handlers/media.d.ts +8 -0
- package/dist/server/handlers/media.js +902 -0
- package/dist/server/handlers/meta-ads.js +6 -3
- package/dist/server/handlers/nodes.d.ts +2 -0
- package/dist/server/handlers/nodes.js +331 -40
- package/dist/server/handlers/operations.d.ts +4 -6
- package/dist/server/handlers/operations.js +99 -38
- package/dist/server/handlers/platform.js +224 -107
- package/dist/server/handlers/remove-bg.d.ts +6 -0
- package/dist/server/handlers/remove-bg.js +96 -0
- package/dist/server/handlers/storefront.d.ts +6 -0
- package/dist/server/handlers/storefront.js +477 -0
- package/dist/server/handlers/supply-chain.js +21 -3
- package/dist/server/handlers/workflow-steps.js +87 -31
- package/dist/server/handlers/workflows.js +4 -1
- package/dist/server/index.js +334 -88
- package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
- package/dist/server/lib/clickhouse-buffer.js +175 -0
- package/dist/server/lib/clickhouse-client.d.ts +112 -0
- package/dist/server/lib/clickhouse-client.js +141 -0
- package/dist/server/lib/coa-renderer.d.ts +91 -0
- package/dist/server/lib/coa-renderer.js +411 -0
- package/dist/server/lib/compaction-service.js +45 -1
- package/dist/server/lib/pdf-renderer.d.ts +143 -0
- package/dist/server/lib/pdf-renderer.js +867 -0
- package/dist/server/lib/react-pdf-layout.d.ts +40 -0
- package/dist/server/lib/react-pdf-layout.js +437 -0
- package/dist/server/lib/server-agent-loop.d.ts +2 -0
- package/dist/server/lib/server-agent-loop.js +61 -15
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +7 -4
- package/dist/server/lib/supabase-client.js +51 -3
- package/dist/server/lib/template-resolver.js +14 -4
- package/dist/server/lib/utils.js +15 -0
- package/dist/server/local-agent-gateway.d.ts +44 -0
- package/dist/server/local-agent-gateway.js +389 -49
- package/dist/server/providers/anthropic.js +12 -2
- package/dist/server/providers/gemini.js +17 -2
- package/dist/server/proxy-handlers.js +151 -0
- package/dist/server/tool-router.d.ts +2 -2
- package/dist/server/tool-router.js +25 -35
- package/dist/shared/agent-core.d.ts +5 -2
- package/dist/shared/agent-core.js +30 -4
- package/dist/shared/api-client.js +54 -3
- package/dist/shared/sse-parser.d.ts +1 -1
- package/dist/shared/sse-parser.js +5 -2
- package/dist/shared/tool-dispatch.js +1 -1
- package/package.json +16 -10
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
- package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
package/bin/swagmanager-mcp.js
CHANGED
|
@@ -111,6 +111,7 @@ function showHelp() {
|
|
|
111
111
|
console.log(` whale doctor${d} Run diagnostics${r}`);
|
|
112
112
|
console.log(` whale init${d} Generate .whale/CLAUDE.md for project${r}`);
|
|
113
113
|
console.log(` whale config${d} View/set configuration${r}`);
|
|
114
|
+
console.log(` whale db${d} Local Supabase management${r}`);
|
|
114
115
|
console.log(` whale serve${d} Local agent WebSocket server${r}`);
|
|
115
116
|
console.log(` whale agent${d} Start local security agent${r}`);
|
|
116
117
|
console.log();
|
|
@@ -305,6 +306,12 @@ switch (command) {
|
|
|
305
306
|
break;
|
|
306
307
|
}
|
|
307
308
|
|
|
309
|
+
case "db": {
|
|
310
|
+
const { runDbCommand } = await import(join(distDir, "cli", "commands", "db.js"));
|
|
311
|
+
await runDbCommand(positionals.slice(1));
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
308
315
|
case "agent": {
|
|
309
316
|
// Forward remaining args to local-agent CLI
|
|
310
317
|
// Rebuild process.argv so the agent sees: [node, script, subcommand, ...flags]
|
package/dist/cli/app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { render } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
import { initErrorLogger, setErrorLoggerUser, captureError } from "./services/error-logger.js";
|
|
4
5
|
import { loadConfig } from "./services/config-store.js";
|
|
5
6
|
export async function renderLogin() {
|
|
@@ -32,8 +33,35 @@ export async function renderChat(options) {
|
|
|
32
33
|
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
33
34
|
captureError({ error: err, severity: "error", tags: { source: "unhandledRejection" } });
|
|
34
35
|
});
|
|
35
|
-
|
|
36
|
-
await
|
|
36
|
+
// Auto-login if not authenticated — seamless browser OAuth
|
|
37
|
+
const { isLoggedIn } = await import("./services/auth-service.js");
|
|
38
|
+
if (!isLoggedIn()) {
|
|
39
|
+
const { signInWithBrowser } = await import("./services/browser-auth.js");
|
|
40
|
+
const brand = chalk.hex("#6366F1");
|
|
41
|
+
const dim = chalk.hex("#64748B");
|
|
42
|
+
const green = chalk.hex("#30D158");
|
|
43
|
+
console.log();
|
|
44
|
+
console.log(` ${brand.bold("◆")} Opening browser to sign in...`);
|
|
45
|
+
const result = await signInWithBrowser(undefined, {
|
|
46
|
+
onBrowserOpening: (url) => {
|
|
47
|
+
console.log(` ${dim(url)}`);
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(` ${dim("Waiting for authentication...")}`);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
console.error(` ${chalk.red("✗")} ${result.error}`);
|
|
54
|
+
console.error(` ${dim("Run")} whale login ${dim("to try again.")}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
console.log(` ${green("✓")} Signed in as ${result.config?.email || "unknown"}`);
|
|
58
|
+
if (result.config?.store_name) {
|
|
59
|
+
console.log(` ${dim("Store:")} ${result.config.store_name}`);
|
|
60
|
+
}
|
|
61
|
+
console.log();
|
|
62
|
+
}
|
|
63
|
+
// Clear screen — fresh start
|
|
64
|
+
process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
|
|
37
65
|
// Apply options before starting chat
|
|
38
66
|
if (options?.model || options?.permissionMode) {
|
|
39
67
|
const agentLoop = await import("./services/agent-loop.js");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ChatApp — whale code CLI
|
|
3
3
|
*
|
|
4
|
-
* Uses Ink's <Static> for completed messages — written to stdout once,
|
|
5
|
-
* never re-rendered. Only the active area (
|
|
6
|
-
* is
|
|
7
|
-
*
|
|
4
|
+
* Uses Ink's <Static> for ALL completed messages — written to stdout once,
|
|
5
|
+
* never re-rendered. Only the active area (running tools, streaming text,
|
|
6
|
+
* thinking spinner, input) is in Ink's render loop.
|
|
7
|
+
* This prevents screen overwriting and allows scrolling during generation.
|
|
8
8
|
*/
|
|
9
9
|
export declare function ChatApp(): import("react/jsx-runtime").JSX.Element;
|
package/dist/cli/chat/ChatApp.js
CHANGED
|
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
/**
|
|
3
3
|
* ChatApp — whale code CLI
|
|
4
4
|
*
|
|
5
|
-
* Uses Ink's <Static> for completed messages — written to stdout once,
|
|
6
|
-
* never re-rendered. Only the active area (
|
|
7
|
-
* is
|
|
8
|
-
*
|
|
5
|
+
* Uses Ink's <Static> for ALL completed messages — written to stdout once,
|
|
6
|
+
* never re-rendered. Only the active area (running tools, streaming text,
|
|
7
|
+
* thinking spinner, input) is in Ink's render loop.
|
|
8
|
+
* This prevents screen overwriting and allows scrolling during generation.
|
|
9
9
|
*/
|
|
10
10
|
import { useState, useEffect, useRef, useMemo, useCallback } from "react";
|
|
11
11
|
import { Box, Text, Static, useApp, useInput } from "ink";
|
|
@@ -16,12 +16,23 @@ import { ToolIndicator } from "./ToolIndicator.js";
|
|
|
16
16
|
import { SubagentPanel } from "./SubagentPanel.js";
|
|
17
17
|
import { TeamPanel } from "./TeamPanel.js";
|
|
18
18
|
import { StreamingText } from "./StreamingText.js";
|
|
19
|
+
import { MarkdownText } from "./MarkdownText.js";
|
|
19
20
|
import { ChatInput } from "./ChatInput.js";
|
|
21
|
+
import { StatusBar } from "./StatusBar.js";
|
|
20
22
|
import { StoreSelector } from "./StoreSelector.js";
|
|
23
|
+
import { NodeSelector } from "./NodeSelector.js";
|
|
24
|
+
import { SessionManager } from "./SessionManager.js";
|
|
25
|
+
import { MemoryManager } from "./MemoryManager.js";
|
|
26
|
+
import { NodeManager } from "./NodeManager.js";
|
|
21
27
|
import { ModelSelector } from "./ModelSelector.js";
|
|
28
|
+
import { ThemeSelector } from "./ThemeSelector.js";
|
|
22
29
|
import { RewindViewer, RewindOutcome } from "./RewindViewer.js";
|
|
30
|
+
import { PlanApproval } from "./PlanApproval.js";
|
|
23
31
|
import { RewindManager } from "../services/rewind.js";
|
|
32
|
+
import { interactiveEvents, resolvePlanApproval } from "../services/interactive-tools.js";
|
|
24
33
|
import { colors, symbols } from "../shared/Theme.js";
|
|
34
|
+
import { initTheme, switchTheme } from "../shared/theme-manager.js";
|
|
35
|
+
import { WhaleBanner } from "../shared/WhaleBanner.js";
|
|
25
36
|
import { loadKeybindings, matchesBinding } from "../services/keybinding-manager.js";
|
|
26
37
|
import { loadConfig, updateConfig } from "../services/config-store.js";
|
|
27
38
|
import { createRequire } from "module";
|
|
@@ -30,22 +41,6 @@ import { dirname, join } from "path";
|
|
|
30
41
|
// Extracted hooks
|
|
31
42
|
import { useSlashCommands } from "./hooks/useSlashCommands.js";
|
|
32
43
|
import { useAgentLoop } from "./hooks/useAgentLoop.js";
|
|
33
|
-
// Thinking verbs — rotate randomly each render (Claude Code parity)
|
|
34
|
-
const THINKING_VERBS = [
|
|
35
|
-
"thinking…",
|
|
36
|
-
"reasoning…",
|
|
37
|
-
"considering…",
|
|
38
|
-
"analyzing…",
|
|
39
|
-
"evaluating…",
|
|
40
|
-
"pondering…",
|
|
41
|
-
"processing…",
|
|
42
|
-
"reflecting…",
|
|
43
|
-
"examining…",
|
|
44
|
-
"working…",
|
|
45
|
-
];
|
|
46
|
-
function randomVerb() {
|
|
47
|
-
return THINKING_VERBS[Math.floor(Math.random() * THINKING_VERBS.length)];
|
|
48
|
-
}
|
|
49
44
|
const PKG_NAME = "whale-code";
|
|
50
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
51
46
|
const __dirname = dirname(__filename);
|
|
@@ -69,7 +64,16 @@ export function ChatApp() {
|
|
|
69
64
|
const [serverToolsAvailable, setServerToolsAvailable] = useState(0);
|
|
70
65
|
const [storeSelectMode, setStoreSelectMode] = useState(false);
|
|
71
66
|
const [storeList, setStoreList] = useState([]);
|
|
67
|
+
const [nodeSelectMode, setNodeSelectMode] = useState(false);
|
|
68
|
+
const [nodeList, setNodeList] = useState([]);
|
|
69
|
+
const [sessionManagerMode, setSessionManagerMode] = useState(false);
|
|
70
|
+
const [sessionList, setSessionList] = useState([]);
|
|
71
|
+
const [memoryManagerMode, setMemoryManagerMode] = useState(false);
|
|
72
|
+
const [memoryList, setMemoryList] = useState([]);
|
|
73
|
+
const [nodeManagerMode, setNodeManagerMode] = useState(false);
|
|
74
|
+
const [nodeManagerStatus, setNodeManagerStatus] = useState({ registered: false, running: false });
|
|
72
75
|
const [modelSelectMode, setModelSelectMode] = useState(false);
|
|
76
|
+
const [themeSelectMode, setThemeSelectMode] = useState(false);
|
|
73
77
|
const [currentModel, setCurrentModel] = useState(getModelShortName());
|
|
74
78
|
const [sessionId, setSessionId] = useState(null);
|
|
75
79
|
const [thinkingEnabled, setThinkingEnabled] = useState(() => {
|
|
@@ -80,34 +84,57 @@ export function ChatApp() {
|
|
|
80
84
|
return true;
|
|
81
85
|
}
|
|
82
86
|
});
|
|
83
|
-
// Stream activity indicator — show spinner when no new content for 800ms+
|
|
84
|
-
const [streamStale, setStreamStale] = useState(false);
|
|
85
87
|
const lastContentUpdateRef = useRef(Date.now());
|
|
86
|
-
//
|
|
88
|
+
// Unified streaming activity — verb cycling + elapsed timer (replaces separate thinking timer + StatusBar verb)
|
|
89
|
+
const FUN_VERBS = useMemo(() => [
|
|
90
|
+
"thinking", "pondering", "brewing", "cooking", "rowing", "juggling",
|
|
91
|
+
"weaving", "crafting", "forging", "sketching", "conjuring", "assembling",
|
|
92
|
+
"sculpting", "composing", "mixing", "polishing", "building", "wiring",
|
|
93
|
+
"spinning", "mapping", "plotting", "dreaming", "hurdling", "launching",
|
|
94
|
+
"surfing", "flying", "grabbing", "lifting", "twisting", "rolling",
|
|
95
|
+
], []);
|
|
96
|
+
const [streamElapsed, setStreamElapsed] = useState(0);
|
|
97
|
+
const [verbIndex, setVerbIndex] = useState(0);
|
|
98
|
+
const streamStartRef = useRef(null);
|
|
87
99
|
useEffect(() => {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
if (isStreaming) {
|
|
101
|
+
if (!streamStartRef.current)
|
|
102
|
+
streamStartRef.current = Date.now();
|
|
103
|
+
setStreamElapsed(0);
|
|
104
|
+
setVerbIndex(0);
|
|
105
|
+
const elapsedInterval = setInterval(() => {
|
|
106
|
+
if (streamStartRef.current) {
|
|
107
|
+
setStreamElapsed(Math.floor((Date.now() - streamStartRef.current) / 1000));
|
|
108
|
+
}
|
|
109
|
+
}, 1000);
|
|
110
|
+
const verbInterval = setInterval(() => {
|
|
111
|
+
setVerbIndex(i => (i + 1) % FUN_VERBS.length);
|
|
112
|
+
}, 4000);
|
|
113
|
+
return () => { clearInterval(elapsedInterval); clearInterval(verbInterval); };
|
|
91
114
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}, [isStreaming]);
|
|
115
|
+
else {
|
|
116
|
+
streamStartRef.current = null;
|
|
117
|
+
setStreamElapsed(0);
|
|
118
|
+
setVerbIndex(0);
|
|
119
|
+
}
|
|
120
|
+
}, [isStreaming, FUN_VERBS]);
|
|
98
121
|
// Rewind state
|
|
99
122
|
const [showRewind, setShowRewind] = useState(false);
|
|
123
|
+
// Plan approval state
|
|
124
|
+
const [planApproval, setPlanApproval] = useState(null);
|
|
100
125
|
const rewindManagerRef = useRef(new RewindManager());
|
|
101
126
|
const turnIndexRef = useRef(0);
|
|
102
127
|
// Refs
|
|
103
128
|
const conversationRef = useRef([]);
|
|
104
129
|
const abortRef = useRef(null);
|
|
105
130
|
const accTextRef = useRef("");
|
|
106
|
-
const
|
|
131
|
+
const thinkingChunksRef = useRef(0);
|
|
132
|
+
const thinkingDoneRef = useRef(false);
|
|
107
133
|
// No timer cleanup needed — unified flush timer is managed inside useAgentLoop,
|
|
108
134
|
// stale polling interval is cleaned up by its own effect.
|
|
109
135
|
// ── Init ──
|
|
110
136
|
useEffect(() => {
|
|
137
|
+
initTheme();
|
|
111
138
|
const check = canUseAgent();
|
|
112
139
|
if (!check.ready) {
|
|
113
140
|
setError(check.reason || "Run 'whale login' to authenticate.");
|
|
@@ -120,8 +147,20 @@ export function ChatApp() {
|
|
|
120
147
|
getServerToolCount().then((count) => setServerToolsAvailable(count));
|
|
121
148
|
mcpClientManager.connectAll().catch(() => { });
|
|
122
149
|
}
|
|
150
|
+
// Listen for plan approval requests — push plan content to Static for natural scrolling
|
|
151
|
+
const handlePlanExited = (data) => {
|
|
152
|
+
// Push plan content into messages so it renders in the <Static> area
|
|
153
|
+
const content = data.planContent || "(empty plan)";
|
|
154
|
+
setMessages(prev => [...prev, {
|
|
155
|
+
role: "assistant",
|
|
156
|
+
text: ` ${symbols.arrowRight} Plan Review — ${data.planFile}\n\n${content}`,
|
|
157
|
+
}]);
|
|
158
|
+
setPlanApproval({ planContent: data.planContent || "", planFile: data.planFile });
|
|
159
|
+
};
|
|
160
|
+
interactiveEvents.on("planModeExited", handlePlanExited);
|
|
123
161
|
return () => {
|
|
124
162
|
mcpClientManager.disconnectAll().catch(() => { });
|
|
163
|
+
interactiveEvents.off("planModeExited", handlePlanExited);
|
|
125
164
|
};
|
|
126
165
|
}, []);
|
|
127
166
|
// ── Keys (configurable via ~/.swagmanager/keybindings.json) ──
|
|
@@ -153,24 +192,36 @@ export function ChatApp() {
|
|
|
153
192
|
});
|
|
154
193
|
}
|
|
155
194
|
});
|
|
195
|
+
// ESC abort is now handled by ChatInput's onInterrupt prop — stdin stays active
|
|
196
|
+
// even when disabled, so ESC always reaches the handler.
|
|
156
197
|
// ── Extracted hooks ──
|
|
157
|
-
const { handleCommand, handleStoreSelect, handleStoreCancel } = useSlashCommands({
|
|
198
|
+
const { handleCommand, handleStoreSelect, handleStoreCancel, handleNodeSelect, handleNodeCancel, handleSessionSelect, handleMemoryDelete, handleNodeToggle, handleNodeRegister, handleNodeClose, } = useSlashCommands({
|
|
158
199
|
exit, toolsExpanded, serverToolsAvailable, sessionId, thinkingEnabled,
|
|
159
200
|
conversationRef,
|
|
160
201
|
setMessages, setStreamingText, setActiveTools, setTeamState,
|
|
161
202
|
setStoreList, setStoreSelectMode, setModelSelectMode, setCurrentModel,
|
|
162
203
|
setSessionId, setThinkingEnabled, setUserLabel, setServerToolsAvailable,
|
|
163
204
|
setShowRewind,
|
|
205
|
+
setNodeSelectMode, setNodeList,
|
|
206
|
+
setSessionManagerMode, setSessionList,
|
|
207
|
+
setMemoryManagerMode, setMemoryList,
|
|
208
|
+
setThemeSelectMode,
|
|
209
|
+
setNodeManagerMode, setNodeManagerStatus,
|
|
164
210
|
rewindCheckpointCount: rewindManagerRef.current.getCheckpointCount(),
|
|
165
211
|
PKG_NAME, PKG_VERSION,
|
|
166
212
|
});
|
|
167
213
|
const { handleSend } = useAgentLoop({
|
|
168
214
|
isStreaming, thinkingEnabled, conversationRef, abortRef,
|
|
169
|
-
accTextRef, lastContentUpdateRef,
|
|
215
|
+
accTextRef, lastContentUpdateRef, thinkingChunksRef, thinkingDoneRef,
|
|
170
216
|
rewindManagerRef, turnIndexRef,
|
|
171
217
|
setMessages, setStreamingText, setIsStreaming, setActiveTools,
|
|
172
218
|
setSubagentActivity, setCompletedSubagents, setTeamState,
|
|
173
219
|
});
|
|
220
|
+
// ── Plan approval handler ──
|
|
221
|
+
const handlePlanDecision = useCallback((decision) => {
|
|
222
|
+
setPlanApproval(null);
|
|
223
|
+
resolvePlanApproval(decision);
|
|
224
|
+
}, []);
|
|
174
225
|
// ── Rewind handler ──
|
|
175
226
|
const handleRewind = useCallback((checkpointIndex, outcome) => {
|
|
176
227
|
const rm = rewindManagerRef.current;
|
|
@@ -235,27 +286,39 @@ export function ChatApp() {
|
|
|
235
286
|
// ── Render ──
|
|
236
287
|
const termWidth = process.stdout.columns || 80;
|
|
237
288
|
const contentWidth = Math.max(20, termWidth - 2);
|
|
238
|
-
|
|
289
|
+
// ALL completed messages go to Static — written to stdout once, never re-rendered.
|
|
290
|
+
// This keeps the dynamic area small (only active tools + streaming + input),
|
|
291
|
+
// preventing the constant screen overwriting that blocks scrolling.
|
|
292
|
+
const staticItems = useMemo(() => {
|
|
239
293
|
const items = [{ id: "header", type: "header" }];
|
|
240
|
-
|
|
241
|
-
for (let i = 0; i < cutoff; i++) {
|
|
294
|
+
for (let i = 0; i < messages.length; i++) {
|
|
242
295
|
items.push({ id: `msg-${i}`, type: "message", msg: messages[i], index: i });
|
|
243
296
|
}
|
|
244
|
-
|
|
245
|
-
return { staticItems: items, dynamicMessages: tail };
|
|
297
|
+
return items;
|
|
246
298
|
}, [messages]);
|
|
247
299
|
if (error) {
|
|
248
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(
|
|
300
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(WhaleBanner, {}), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.error, children: [" ", error] })] }));
|
|
249
301
|
}
|
|
250
302
|
if (!ready) {
|
|
251
303
|
return (_jsx(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: _jsx(Text, { color: colors.tertiary, children: "loading..." }) }));
|
|
252
304
|
}
|
|
305
|
+
// Plan approval no longer full-screen — plan content is pushed to Static (scrollable),
|
|
306
|
+
// PlanApproval renders as an overlay above the persistent input.
|
|
253
307
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(Static, { items: staticItems, children: (item) => {
|
|
254
308
|
if (item.type === "header") {
|
|
255
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: [
|
|
309
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(WhaleBanner, {}), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", userLabel ? _jsxs(Text, { color: colors.dim, children: [userLabel, " "] }) : null, _jsx(Text, { color: colors.dim, children: currentModel }), thinkingEnabled ? _jsx(Text, { color: colors.warning, children: " thinking" }) : null, getPermissionMode() !== "default" && (_jsxs(Text, { color: getPermissionMode() === "yolo" ? colors.error : colors.info, children: [" ", getPermissionMode()] })), serverToolsAvailable > 0 ? (_jsxs(Text, { color: colors.tertiary, children: [" ", symbols.dot, " ", serverToolsAvailable, " tools"] })) : null] }), _jsx(Text, { children: " " })] }, item.id));
|
|
256
310
|
}
|
|
257
311
|
return _jsx(CompletedMessage, { msg: item.msg, index: item.index, toolsExpanded: toolsExpanded }, item.id);
|
|
258
|
-
} }),
|
|
312
|
+
} }), teamState ? (_jsx(TeamPanel, { team: teamState })) : (_jsxs(_Fragment, { children: [isStreaming && (_jsxs(Box, { marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: colors.dim, children: [" ", FUN_VERBS[verbIndex]] }), streamElapsed > 0 && (_jsxs(Text, { color: colors.quaternary, children: [" ", streamElapsed < 60 ? `${streamElapsed}s` : `${Math.floor(streamElapsed / 60)}:${(streamElapsed % 60).toString().padStart(2, "0")}`] }))] })), activeTools.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: activeTools.map((tc, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(ToolIndicator, { id: `live-${tc.name}-${i}`, name: tc.name, status: tc.status, result: tc.result, input: tc.input, expanded: toolsExpanded, progress: tc.progress }), tc.name === "task" && tc.status === "running" && (subagentActivity.size > 0 || completedSubagents.length > 0) && (_jsx(SubagentPanel, { running: subagentActivity, completed: completedSubagents }))] }, `live-${tc.name}-${i}`))) })), toolsExpanded && !isStreaming && (() => {
|
|
313
|
+
// Find collapsed tool results from the last assistant turn
|
|
314
|
+
const lastAssistant = [...messages].reverse().find(m => m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0);
|
|
315
|
+
if (!lastAssistant?.toolCalls)
|
|
316
|
+
return null;
|
|
317
|
+
const collapsed = lastAssistant.toolCalls.filter(tc => tc.result && tc.status === "success" && tc.result.split("\n").length > 12);
|
|
318
|
+
if (collapsed.length === 0)
|
|
319
|
+
return null;
|
|
320
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: colors.tertiary, children: ["─".repeat(Math.min(60, (process.stdout.columns || 80) - 6)), " ^O close"] }), collapsed.map((tc, i) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.info, bold: true, children: tc.name }), _jsx(MarkdownText, { text: tc.result })] }, i))), _jsx(Text, { color: colors.tertiary, children: "─".repeat(Math.min(60, (process.stdout.columns || 80) - 6)) })] }));
|
|
321
|
+
})(), streamingText && (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(StreamingText, { text: streamingText }) }))] })), showRewind && (_jsx(RewindViewer, { checkpoints: rewindManagerRef.current.getCheckpoints(), onRewind: handleRewind, onCancel: () => setShowRewind(false) })), sessionManagerMode && (_jsx(SessionManager, { sessions: sessionList, onSelect: handleSessionSelect, onCancel: () => setSessionManagerMode(false) })), memoryManagerMode && (_jsx(MemoryManager, { memories: memoryList, onDelete: handleMemoryDelete, onCancel: () => setMemoryManagerMode(false) })), nodeManagerMode && (_jsx(NodeManager, { initialStatus: nodeManagerStatus, onToggle: handleNodeToggle, onRegister: handleNodeRegister, onClose: handleNodeClose })), storeSelectMode && (_jsx(StoreSelector, { stores: storeList, currentStoreId: loadConfig().store_id || "", onSelect: handleStoreSelect, onCancel: handleStoreCancel })), nodeSelectMode && (_jsx(NodeSelector, { nodes: nodeList, onSelect: handleNodeSelect, onCancel: handleNodeCancel })), modelSelectMode && (_jsx(ModelSelector, { currentModel: currentModel, onSelect: (model) => {
|
|
259
322
|
setModelSelectMode(false);
|
|
260
323
|
setModel(model.value);
|
|
261
324
|
setCurrentModel(model.value);
|
|
@@ -263,5 +326,12 @@ export function ChatApp() {
|
|
|
263
326
|
role: "assistant",
|
|
264
327
|
text: ` ${symbols.check} Model: ${model.label} (${model.modelId})`,
|
|
265
328
|
}]);
|
|
266
|
-
}, onCancel: () => setModelSelectMode(false) }))
|
|
329
|
+
}, onCancel: () => setModelSelectMode(false) })), themeSelectMode && (_jsx(ThemeSelector, { onSelect: (preset) => {
|
|
330
|
+
setThemeSelectMode(false);
|
|
331
|
+
switchTheme(preset.id);
|
|
332
|
+
setMessages((prev) => [...prev, {
|
|
333
|
+
role: "assistant",
|
|
334
|
+
text: ` ${symbols.check} Theme: ${preset.label}`,
|
|
335
|
+
}]);
|
|
336
|
+
}, onCancel: () => setThemeSelectMode(false) })), planApproval && (_jsx(PlanApproval, { planFile: planApproval.planFile, onDecision: handlePlanDecision })), _jsx(ChatInput, { onSubmit: handleSend, onCommand: handleCommand, disabled: isStreaming || !!planApproval, agentName: "whale code", onInterrupt: () => abortRef.current?.abort() }), _jsx(StatusBar, { permissionMode: getPermissionMode(), model: currentModel, thinkingEnabled: thinkingEnabled, serverTools: serverToolsAvailable, isStreaming: isStreaming, toolsExpanded: toolsExpanded })] }));
|
|
267
337
|
}
|
|
@@ -24,16 +24,23 @@ export interface FileAttachment {
|
|
|
24
24
|
path: string;
|
|
25
25
|
name: string;
|
|
26
26
|
}
|
|
27
|
-
export interface
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
export interface ContextAttachment {
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
title: string;
|
|
31
|
+
subtitle?: string;
|
|
32
|
+
icon: string;
|
|
33
|
+
sourceApp: string;
|
|
34
|
+
data: Record<string, unknown>;
|
|
35
|
+
timestamp: string;
|
|
30
36
|
}
|
|
31
|
-
export
|
|
37
|
+
export type { SlashCommand } from "./SlashMenu.js";
|
|
38
|
+
export declare const SLASH_COMMANDS: import("./SlashMenu.js").SlashCommand[];
|
|
32
39
|
interface ChatInputProps {
|
|
33
40
|
onSubmit: (message: string, images?: ImageAttachment[]) => void;
|
|
34
41
|
onCommand: (command: string) => void;
|
|
35
42
|
disabled: boolean;
|
|
36
43
|
agentName?: string;
|
|
44
|
+
onInterrupt?: () => void;
|
|
37
45
|
}
|
|
38
|
-
export declare function ChatInput({ onSubmit, onCommand, disabled, agentName }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
-
export {};
|
|
46
|
+
export declare function ChatInput({ onSubmit, onCommand, disabled, agentName, onInterrupt }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|