whale-code 6.4.0 → 6.5.1
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 +51 -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 +65 -8
- 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 +7 -6
- 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 +85 -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 +46 -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 +36 -17
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +9 -6
- 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 +25 -2
- package/dist/shared/agent-core.js +66 -5
- 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 +15 -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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SlashMenu — Two-level categorized slash command menu
|
|
4
|
+
*
|
|
5
|
+
* Pure rendering component: zero internal state, zero keyboard handling.
|
|
6
|
+
* ChatInput owns all state and passes props.
|
|
7
|
+
*
|
|
8
|
+
* Level 1: Category list (6 rows)
|
|
9
|
+
* Level 2: Commands within a category (with breadcrumb header)
|
|
10
|
+
* Filter: Flat filtered list (existing behavior)
|
|
11
|
+
*/
|
|
12
|
+
import { Box, Text } from "ink";
|
|
13
|
+
import { colors } from "../shared/Theme.js";
|
|
14
|
+
// ── Command Definitions (6 categories) ──
|
|
15
|
+
export const CATEGORIES = [
|
|
16
|
+
{
|
|
17
|
+
label: "Session",
|
|
18
|
+
color: colors.brand,
|
|
19
|
+
commands: [
|
|
20
|
+
{ name: "/clear", description: "Clear conversation" },
|
|
21
|
+
{ name: "/save", description: "Save session to disk" },
|
|
22
|
+
{ name: "/sessions", description: "List saved sessions" },
|
|
23
|
+
{ name: "/resume", description: "Resume a saved session" },
|
|
24
|
+
{ name: "/compact", description: "Compress conversation context" },
|
|
25
|
+
{ name: "/rewind", description: "Rewind conversation to earlier point" },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: "Config",
|
|
30
|
+
color: colors.warning,
|
|
31
|
+
commands: [
|
|
32
|
+
{ name: "/model", description: "Switch model (Anthropic/Bedrock/Gemini)" },
|
|
33
|
+
{ name: "/mode", description: "Permission mode (default/plan/yolo)" },
|
|
34
|
+
{ name: "/thinking", description: "Toggle extended thinking" },
|
|
35
|
+
{ name: "/store", description: "Switch active store" },
|
|
36
|
+
{ name: "/theme", description: "Switch color theme" },
|
|
37
|
+
{ name: "/init", description: "Generate project config (.whale/CLAUDE.md)" },
|
|
38
|
+
{ name: "/update", description: "Check for updates & install" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: "Info",
|
|
43
|
+
color: colors.info,
|
|
44
|
+
commands: [
|
|
45
|
+
{ name: "/help", description: "Show available commands" },
|
|
46
|
+
{ name: "/status", description: "Show session info" },
|
|
47
|
+
{ name: "/tools", description: "List all tools" },
|
|
48
|
+
{ name: "/mcp", description: "Server connection status" },
|
|
49
|
+
{ name: "/agents", description: "List available agent types" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: "Memory",
|
|
54
|
+
color: colors.purple,
|
|
55
|
+
commands: [
|
|
56
|
+
{ name: "/remember", description: "Remember a fact across sessions" },
|
|
57
|
+
{ name: "/forget", description: "Forget a remembered fact" },
|
|
58
|
+
{ name: "/memory", description: "List all remembered facts" },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: "Nodes",
|
|
63
|
+
color: colors.success,
|
|
64
|
+
commands: [
|
|
65
|
+
{ name: "/nodes", description: "Connected whale-nodes" },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: "Account",
|
|
70
|
+
color: colors.tertiary,
|
|
71
|
+
commands: [
|
|
72
|
+
{ name: "/logout", description: "Log out and exit" },
|
|
73
|
+
{ name: "/exit", description: "Exit" },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
/** Flat array of all commands (for external consumers like /help) */
|
|
78
|
+
export const SLASH_COMMANDS = CATEGORIES.flatMap(c => c.commands);
|
|
79
|
+
// ── Category helpers ──
|
|
80
|
+
export function getCategoryCount() {
|
|
81
|
+
return CATEGORIES.length;
|
|
82
|
+
}
|
|
83
|
+
export function getCategoryCommandCount(catIndex) {
|
|
84
|
+
return CATEGORIES[catIndex]?.commands.length ?? 0;
|
|
85
|
+
}
|
|
86
|
+
export function getCategoryLabel(catIndex) {
|
|
87
|
+
return CATEGORIES[catIndex]?.label ?? "";
|
|
88
|
+
}
|
|
89
|
+
export function getCategoryColor(catIndex) {
|
|
90
|
+
return CATEGORIES[catIndex]?.color ?? colors.tertiary;
|
|
91
|
+
}
|
|
92
|
+
export function getCategoryCommand(catIndex, cmdIndex) {
|
|
93
|
+
return CATEGORIES[catIndex]?.commands[cmdIndex] ?? null;
|
|
94
|
+
}
|
|
95
|
+
// ── Windowed rendering constants ──
|
|
96
|
+
const MAX_VISIBLE = 12;
|
|
97
|
+
const SCROLL_PADDING = 2;
|
|
98
|
+
// ── Flat list builder (memoized on filter) ──
|
|
99
|
+
function buildFlatList(filter) {
|
|
100
|
+
const q = filter.toLowerCase();
|
|
101
|
+
const items = [];
|
|
102
|
+
for (const cat of CATEGORIES) {
|
|
103
|
+
const matched = q
|
|
104
|
+
? cat.commands.filter(c => c.name.includes(q) || c.description.toLowerCase().includes(q))
|
|
105
|
+
: cat.commands;
|
|
106
|
+
if (matched.length === 0)
|
|
107
|
+
continue;
|
|
108
|
+
items.push({ kind: "header", label: cat.label, color: cat.color });
|
|
109
|
+
for (const cmd of matched) {
|
|
110
|
+
items.push({ kind: "command", command: cmd, color: cat.color });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return items;
|
|
114
|
+
}
|
|
115
|
+
/** Count of selectable (command) items in a filtered list */
|
|
116
|
+
export function getFilteredSelectableCount(filter) {
|
|
117
|
+
const q = filter.toLowerCase();
|
|
118
|
+
let count = 0;
|
|
119
|
+
for (const cat of CATEGORIES) {
|
|
120
|
+
const matched = q
|
|
121
|
+
? cat.commands.filter(c => c.name.includes(q) || c.description.toLowerCase().includes(q))
|
|
122
|
+
: cat.commands;
|
|
123
|
+
count += matched.length;
|
|
124
|
+
}
|
|
125
|
+
return count;
|
|
126
|
+
}
|
|
127
|
+
/** Get the command at a given selectable index (skipping headers) */
|
|
128
|
+
export function getCommandAtIndex(filter, selectableIndex) {
|
|
129
|
+
const flat = buildFlatList(filter);
|
|
130
|
+
let si = 0;
|
|
131
|
+
for (const item of flat) {
|
|
132
|
+
if (item.kind === "command") {
|
|
133
|
+
if (si === selectableIndex)
|
|
134
|
+
return item.command;
|
|
135
|
+
si++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
export function SlashMenu({ filter, selectedIndex, selectedCategory }) {
|
|
141
|
+
// ── Level 1: Category list ──
|
|
142
|
+
if (selectedCategory === null && filter === "") {
|
|
143
|
+
return (_jsx(Box, { flexDirection: "column", children: CATEGORIES.map((cat, i) => {
|
|
144
|
+
const isSelected = i === selectedIndex;
|
|
145
|
+
const count = cat.commands.length;
|
|
146
|
+
const countLabel = count === 1 ? "1 command" : `${count} commands`;
|
|
147
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? cat.color : colors.quaternary, children: isSelected ? "\u203A " : " " }), _jsx(Text, { color: isSelected ? cat.color : colors.secondary, bold: isSelected, children: cat.label }), _jsxs(Text, { color: colors.tertiary, children: [" ", countLabel] })] }, cat.label));
|
|
148
|
+
}) }));
|
|
149
|
+
}
|
|
150
|
+
// ── Level 2: Commands within a category ──
|
|
151
|
+
if (selectedCategory !== null && filter === "") {
|
|
152
|
+
const cat = CATEGORIES[selectedCategory];
|
|
153
|
+
if (!cat)
|
|
154
|
+
return null;
|
|
155
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { color: cat.color, children: ` \u2039 ${cat.label}` }) }), cat.commands.map((cmd, i) => {
|
|
156
|
+
const isSelected = i === selectedIndex;
|
|
157
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? cat.color : colors.quaternary, children: isSelected ? "\u203A " : " " }), _jsx(Text, { color: isSelected ? cat.color : colors.secondary, bold: isSelected, children: cmd.name }), _jsxs(Text, { color: colors.tertiary, children: [" ", cmd.description] })] }, cmd.name));
|
|
158
|
+
})] }));
|
|
159
|
+
}
|
|
160
|
+
// ── Filter mode: flat filtered list (existing behavior) ──
|
|
161
|
+
const flatList = buildFlatList(filter);
|
|
162
|
+
// Map selectedIndex (selectable-only) → flat-list index
|
|
163
|
+
let selectedFlatIndex = -1;
|
|
164
|
+
{
|
|
165
|
+
let si = 0;
|
|
166
|
+
for (let i = 0; i < flatList.length; i++) {
|
|
167
|
+
if (flatList[i].kind === "command") {
|
|
168
|
+
if (si === selectedIndex) {
|
|
169
|
+
selectedFlatIndex = i;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
si++;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Calculate visible window
|
|
177
|
+
let windowStart = 0;
|
|
178
|
+
let windowEnd = flatList.length;
|
|
179
|
+
if (flatList.length > MAX_VISIBLE) {
|
|
180
|
+
windowStart = Math.max(0, selectedFlatIndex - SCROLL_PADDING);
|
|
181
|
+
windowEnd = windowStart + MAX_VISIBLE;
|
|
182
|
+
if (windowEnd > flatList.length) {
|
|
183
|
+
windowEnd = flatList.length;
|
|
184
|
+
windowStart = Math.max(0, windowEnd - MAX_VISIBLE);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const visibleItems = flatList.slice(windowStart, windowEnd);
|
|
188
|
+
const hasOverflowAbove = windowStart > 0;
|
|
189
|
+
const hasOverflowBelow = windowEnd < flatList.length;
|
|
190
|
+
// No matching commands
|
|
191
|
+
if (flatList.length === 0) {
|
|
192
|
+
return _jsx(Text, { color: colors.tertiary, children: " no matching commands" });
|
|
193
|
+
}
|
|
194
|
+
// Track selectable index as we render
|
|
195
|
+
let selectableCounter = 0;
|
|
196
|
+
for (let i = 0; i < windowStart; i++) {
|
|
197
|
+
if (flatList[i].kind === "command")
|
|
198
|
+
selectableCounter++;
|
|
199
|
+
}
|
|
200
|
+
return (_jsxs(Box, { flexDirection: "column", children: [hasOverflowAbove && (_jsxs(Text, { color: colors.quaternary, children: [" ", " \u2191 more"] })), visibleItems.map((item) => {
|
|
201
|
+
if (item.kind === "header") {
|
|
202
|
+
return (_jsx(Box, { children: _jsx(Text, { color: item.color, dimColor: true, children: ` ${item.label}` }) }, `h-${item.label}`));
|
|
203
|
+
}
|
|
204
|
+
const thisSelectableIdx = selectableCounter++;
|
|
205
|
+
const isSelected = thisSelectableIdx === selectedIndex;
|
|
206
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? item.color : colors.quaternary, children: isSelected ? "\u203A " : " " }), _jsx(Text, { color: isSelected ? item.color : colors.secondary, bold: isSelected, children: item.command.name }), _jsxs(Text, { color: colors.tertiary, children: [" ", item.command.description] })] }, item.command.name));
|
|
207
|
+
}), hasOverflowBelow && (_jsxs(Text, { color: colors.quaternary, children: [" ", " \u2193 more"] }))] }));
|
|
208
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar — persistent status line below the chat input
|
|
3
|
+
*
|
|
4
|
+
* Shows: permission mode · model · thinking on/off · tool count · streaming elapsed
|
|
5
|
+
* Single line, dimmed colors, dot-separated items.
|
|
6
|
+
*/
|
|
7
|
+
interface StatusBarProps {
|
|
8
|
+
permissionMode: string;
|
|
9
|
+
model: string;
|
|
10
|
+
thinkingEnabled: boolean;
|
|
11
|
+
serverTools: number;
|
|
12
|
+
isStreaming: boolean;
|
|
13
|
+
toolsExpanded?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function StatusBar({ permissionMode, model, thinkingEnabled, serverTools, isStreaming, toolsExpanded, }: StatusBarProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* StatusBar — persistent status line below the chat input
|
|
4
|
+
*
|
|
5
|
+
* Shows: permission mode · model · thinking on/off · tool count · streaming elapsed
|
|
6
|
+
* Single line, dimmed colors, dot-separated items.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Text } from "ink";
|
|
9
|
+
import { colors, symbols } from "../shared/Theme.js";
|
|
10
|
+
// ── Helpers ──
|
|
11
|
+
function modeColor(mode) {
|
|
12
|
+
switch (mode) {
|
|
13
|
+
case "yolo": return colors.error;
|
|
14
|
+
case "plan": return colors.info;
|
|
15
|
+
default: return colors.quaternary;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// ── Component ──
|
|
19
|
+
export function StatusBar({ permissionMode, model, thinkingEnabled, serverTools, isStreaming, toolsExpanded, }) {
|
|
20
|
+
const sep = ` ${symbols.dot} `;
|
|
21
|
+
return (_jsxs(Box, { marginLeft: 2, overflow: "hidden", children: [_jsx(Text, { color: modeColor(permissionMode), wrap: "truncate", children: permissionMode }), _jsxs(Text, { color: colors.quaternary, wrap: "truncate", children: [sep, model, sep, "thinking ", thinkingEnabled ? "on" : "off", serverTools > 0 ? `${sep}${serverTools} tools` : ""] }), toolsExpanded && !isStreaming && (_jsxs(Text, { color: colors.info, wrap: "truncate", children: [sep, "expanded ^O close"] }))] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThemeSelector — inline theme picker for chat
|
|
3
|
+
*
|
|
4
|
+
* Renders a SelectInput with available color themes.
|
|
5
|
+
* Shows colored preview dots (brand/success/error/purple) for each theme.
|
|
6
|
+
* Current theme marked with dot. Esc to cancel.
|
|
7
|
+
*/
|
|
8
|
+
import { type ThemePreset } from "../shared/theme-manager.js";
|
|
9
|
+
interface ThemeSelectorProps {
|
|
10
|
+
onSelect: (preset: ThemePreset) => void;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function ThemeSelector({ onSelect, onCancel }: ThemeSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
4
|
+
import { colors, symbols } from "../shared/Theme.js";
|
|
5
|
+
import { getThemePresets, getCurrentThemeId } from "../shared/theme-manager.js";
|
|
6
|
+
export function ThemeSelector({ onSelect, onCancel }) {
|
|
7
|
+
useInput((_input, key) => {
|
|
8
|
+
if (key.escape)
|
|
9
|
+
onCancel();
|
|
10
|
+
});
|
|
11
|
+
const presets = getThemePresets();
|
|
12
|
+
const currentId = getCurrentThemeId();
|
|
13
|
+
const items = presets.map((p) => ({
|
|
14
|
+
label: p.label,
|
|
15
|
+
value: p.id,
|
|
16
|
+
}));
|
|
17
|
+
const handleSelect = (item) => {
|
|
18
|
+
const preset = presets.find((p) => p.id === item.value);
|
|
19
|
+
if (preset)
|
|
20
|
+
onSelect(preset);
|
|
21
|
+
};
|
|
22
|
+
const initialIdx = Math.max(0, items.findIndex((i) => i.value === currentId));
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: " Select theme:" }), _jsx(Box, { height: 1 }), _jsx(SelectInput, { items: items, initialIndex: initialIdx, onSelect: handleSelect, indicatorComponent: ({ isSelected }) => (_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] })), itemComponent: ({ isSelected, label, value }) => {
|
|
24
|
+
const preset = presets.find((p) => p.id === value);
|
|
25
|
+
const isCurrent = value === currentId;
|
|
26
|
+
const chrome = preset?.chrome;
|
|
27
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.brand : colors.text, bold: isSelected, children: label }), chrome && (_jsxs(Text, { children: [" ", _jsx(Text, { color: chrome.brand, children: "\u25CF" }), _jsx(Text, { color: chrome.success, children: "\u25CF" }), _jsx(Text, { color: chrome.error, children: "\u25CF" }), _jsx(Text, { color: chrome.purple, children: "\u25CF" })] })), isCurrent && _jsxs(Text, { color: colors.success, children: [" ", symbols.dot, " current"] })] }));
|
|
28
|
+
} }), _jsx(Text, { color: colors.quaternary, children: " esc to cancel" })] }));
|
|
29
|
+
}
|
|
@@ -17,6 +17,14 @@ interface ToolIndicatorProps {
|
|
|
17
17
|
expanded?: boolean;
|
|
18
18
|
/** When > 1, shows a "× N" badge and hides result (collapsed duplicate group) */
|
|
19
19
|
count?: number;
|
|
20
|
+
progress?: {
|
|
21
|
+
phase?: string;
|
|
22
|
+
elapsed_s: number;
|
|
23
|
+
stdout_lines: number;
|
|
24
|
+
stderr_lines: number;
|
|
25
|
+
stdout_bytes: number;
|
|
26
|
+
last_line?: string;
|
|
27
|
+
};
|
|
20
28
|
}
|
|
21
29
|
/** Shorten an absolute path for display: strip cwd, collapse home, truncate */
|
|
22
30
|
export declare function shortenPath(fullPath: string, maxLen?: number): string;
|
|
@@ -19,7 +19,10 @@ import os from "os";
|
|
|
19
19
|
// CONSTANTS
|
|
20
20
|
// ============================================================================
|
|
21
21
|
const AUTO_EXPAND_THRESHOLD = 12;
|
|
22
|
+
const SERVER_AUTO_EXPAND_THRESHOLD = 30; // Server tools (tables) get more room before collapsing
|
|
22
23
|
const PREVIEW_LINES = 6;
|
|
24
|
+
const SERVER_PREVIEW_LINES = 20; // Tables need ~header + separator + 17 data rows
|
|
25
|
+
const MINI_PREVIEW_LINES = 3;
|
|
23
26
|
// ============================================================================
|
|
24
27
|
// HELPERS
|
|
25
28
|
// ============================================================================
|
|
@@ -239,6 +242,16 @@ function formatSearchResult(result) {
|
|
|
239
242
|
return line;
|
|
240
243
|
}).join("\n");
|
|
241
244
|
}
|
|
245
|
+
function formatElapsed(s) {
|
|
246
|
+
const m = Math.floor(s / 60);
|
|
247
|
+
const sec = Math.floor(s % 60);
|
|
248
|
+
return m > 0 ? `${m}m${sec}s` : `${sec}s`;
|
|
249
|
+
}
|
|
250
|
+
function progressBar(elapsed, maxSeconds = 300) {
|
|
251
|
+
const pct = Math.min(elapsed / maxSeconds, 1);
|
|
252
|
+
const filled = Math.round(pct * 20);
|
|
253
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
254
|
+
}
|
|
242
255
|
const PLAIN_TEXT_TOOLS = new Set(["enter_plan_mode", "exit_plan_mode", "ask_user_question", "skill"]);
|
|
243
256
|
function detectLang(toolName, input) {
|
|
244
257
|
if (toolName === "edit_file" || toolName === "multi_edit")
|
|
@@ -281,7 +294,7 @@ function wrapInFence(content, lang, subtitle) {
|
|
|
281
294
|
// ============================================================================
|
|
282
295
|
// COMPONENT
|
|
283
296
|
// ============================================================================
|
|
284
|
-
export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name, status, result, input, durationMs, expanded = false, count }) {
|
|
297
|
+
export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name, status, result, input, durationMs, expanded = false, count, progress }) {
|
|
285
298
|
const context = useMemo(() => formatContext(name, input), [name, input]);
|
|
286
299
|
const lineCount = useMemo(() => result ? result.split("\n").length : 0, [result]);
|
|
287
300
|
// Elapsed time counter for running tools
|
|
@@ -354,8 +367,7 @@ export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name,
|
|
|
354
367
|
if (category === "search") {
|
|
355
368
|
const lines = result.split("\n").filter(l => l.trim());
|
|
356
369
|
const files = new Set(lines.map(l => l.split(":")[0]).filter(f => f.includes("/") || f.includes(".")));
|
|
357
|
-
|
|
358
|
-
return { type: "search", matches: lines.length, files: files.size };
|
|
370
|
+
return { type: "search", matches: lines.length, files: files.size };
|
|
359
371
|
}
|
|
360
372
|
if (category === "command") {
|
|
361
373
|
const lines = result.split("\n").filter(l => l.trim());
|
|
@@ -389,6 +401,10 @@ export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name,
|
|
|
389
401
|
}, [status, result]);
|
|
390
402
|
// ── RUNNING ──
|
|
391
403
|
if (status === "running") {
|
|
404
|
+
// Kali with structured progress — richer display
|
|
405
|
+
if (name === "kali" && progress) {
|
|
406
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: catStyle.color, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: catStyle.color, children: [" ", catStyle.icon] }), _jsxs(Text, { color: catStyle.color, bold: true, children: [" ", getDisplayName(name)] }), context ? _jsxs(Text, { color: colors.dim, children: [" ", context] }) : null, progress.phase && _jsxs(Text, { color: colors.info, children: [" ", progress.phase] }), _jsxs(Text, { color: progress.elapsed_s >= 60 ? colors.warning : colors.dim, children: [" ", formatElapsed(progress.elapsed_s)] })] }), _jsxs(Box, { marginLeft: 4, children: [_jsx(Text, { color: colors.tertiary, children: progressBar(progress.elapsed_s) }), _jsxs(Text, { color: colors.dim, children: [" ", progress.stdout_lines, " lines"] }), progress.stderr_lines > 0 && _jsxs(Text, { color: colors.warning, children: [" ", progress.stderr_lines, " err"] })] }), progress.last_line && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: colors.tertiary, wrap: "truncate", children: progress.last_line }) }))] }));
|
|
407
|
+
}
|
|
392
408
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: catStyle.color, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: catStyle.color, children: [" ", catStyle.icon] }), _jsxs(Text, { color: catStyle.color, bold: true, children: [" ", getDisplayName(name)] }), context ? _jsxs(Text, { color: colors.dim, children: [" ", context] }) : null, elapsed >= 2 && _jsxs(Text, { color: elapsed >= 30 ? colors.warning : colors.dim, children: [" ", formatDuration(elapsed * 1000)] })] }), liveLines.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 4, children: liveLines.map((line, i) => (_jsx(Text, { color: colors.tertiary, wrap: "truncate", children: line }, i))) }))] }));
|
|
393
409
|
}
|
|
394
410
|
// ── ERROR ──
|
|
@@ -397,10 +413,13 @@ export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name,
|
|
|
397
413
|
}
|
|
398
414
|
// ── SUCCESS ──
|
|
399
415
|
const hasResult = !!(result && result.trim());
|
|
400
|
-
const
|
|
416
|
+
const isServer = category === "server" || category === "agent";
|
|
417
|
+
const autoExpandLimit = isServer ? SERVER_AUTO_EXPAND_THRESHOLD : AUTO_EXPAND_THRESHOLD;
|
|
418
|
+
const isShort = lineCount <= autoExpandLimit;
|
|
401
419
|
// Read/write results stay collapsed (just header line) — Claude Code parity.
|
|
402
420
|
// Edit/search/command results auto-expand if short enough.
|
|
403
421
|
// Writes with diffs auto-expand like edits.
|
|
422
|
+
// Server/agent results get a higher threshold — tables need more room.
|
|
404
423
|
const hasDiff = lang === "diff";
|
|
405
424
|
const collapseByDefault = category === "read" || (category === "write" && !hasDiff) || category === "directory";
|
|
406
425
|
// Interactive tools (plan mode) always expand to show their content
|
|
@@ -419,11 +438,15 @@ export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name,
|
|
|
419
438
|
}, [isGrouped, result]);
|
|
420
439
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: catStyle.color, children: [" ", catStyle.icon] }), _jsxs(Text, { color: catStyle.color, bold: true, children: [" ", getDisplayName(name)] }), context ? _jsxs(Text, { color: colors.dim, children: [" ", context] }) : null, durationMs !== undefined && (durationMs > 3000
|
|
421
440
|
? _jsxs(Text, { color: colors.warning, children: [" ", formatDuration(durationMs)] })
|
|
422
|
-
: _jsxs(Text, { color: colors.dim, children: [" ", formatDuration(durationMs)] })), summary?.type === "edit" && category === "edit" && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.success, children: [" +", summary.added] }), _jsxs(Text, { color: colors.error, children: [" -", summary.removed] })] })), summary?.type === "search" && (_jsxs(Text, { color: colors.dim, children: [" ", summary.matches, " match", summary.matches !== 1 ? "es" : "", summary.files > 0 ? ` in ${summary.files} file${summary.files !== 1 ? "s" : ""}` : ""] })), summary && "label" in summary && summary.type !== "server" && (_jsxs(Text, { color: colors.dim, children: [" ", summary.label] })), summary?.type === "server" && (_jsxs(Text, { color: colors.info, children: [" ", summary.label] })), !summary && hasResult && !showFull && _jsxs(Text, { color: colors.dim, children: [" ", lineCount, " lines"] }), (count ?? 0) > 1 && _jsxs(Text, { color: colors.dim, dimColor: true, children: [" \u00D7 ", count] })] }), isGrouped && groupSummaryLine && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: colors.tertiary, wrap: "truncate", children: groupSummaryLine }) })), category === "write" && hasDiff && summary?.type === "edit" && (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { color: colors.tertiary, children: "\u2514 Added " }), _jsx(Text, { color: colors.success, children: summary.added }), _jsx(Text, { color: colors.tertiary, children: " lines, removed " }), _jsx(Text, { color: colors.error, children: summary.removed }), _jsx(Text, { color: colors.tertiary, children: " lines" })] })), showFull && (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(MarkdownText, { text: category === "agent"
|
|
441
|
+
: _jsxs(Text, { color: colors.dim, children: [" ", formatDuration(durationMs)] })), summary?.type === "edit" && category === "edit" && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.success, children: [" +", summary.added] }), _jsxs(Text, { color: colors.error, children: [" -", summary.removed] })] })), summary?.type === "search" && (_jsxs(Text, { color: colors.dim, children: [" ", summary.matches, " match", summary.matches !== 1 ? "es" : "", summary.files > 0 ? ` in ${summary.files} file${summary.files !== 1 ? "s" : ""}` : ""] })), summary && "label" in summary && summary.type !== "server" && (_jsxs(Text, { color: colors.dim, children: [" ", summary.label] })), summary?.type === "server" && (_jsxs(Text, { color: colors.info, children: [" ", summary.label] })), !summary && hasResult && !showFull && _jsxs(Text, { color: colors.dim, children: [" ", lineCount, " lines"] }), !hasResult && status === "success" && _jsx(Text, { color: colors.quaternary, children: " (no output)" }), hasResult && !showFull && collapseByDefault && !isGrouped && _jsx(Text, { color: colors.quaternary, children: " ^O" }), (count ?? 0) > 1 && _jsxs(Text, { color: colors.dim, dimColor: true, children: [" \u00D7 ", count] })] }), isGrouped && groupSummaryLine && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: colors.tertiary, wrap: "truncate", children: groupSummaryLine }) })), category === "write" && hasDiff && summary?.type === "edit" && (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { color: colors.tertiary, children: "\u2514 Added " }), _jsx(Text, { color: colors.success, children: summary.added }), _jsx(Text, { color: colors.tertiary, children: " lines, removed " }), _jsx(Text, { color: colors.error, children: summary.removed }), _jsx(Text, { color: colors.tertiary, children: " lines" })] })), showFull && (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(MarkdownText, { text: category === "agent"
|
|
423
442
|
? result
|
|
424
|
-
: wrapInFence(category === "search" ? formatSearchResult(result) : result, lang, filePath) }) })), hasResult && !showFull &&
|
|
425
|
-
|
|
426
|
-
|
|
443
|
+
: wrapInFence(category === "search" ? formatSearchResult(result) : result, lang, filePath) }) })), hasResult && !showFull && collapseByDefault && !isGrouped && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(MarkdownText, { text: wrapInFence(result.split("\n").slice(0, MINI_PREVIEW_LINES).join("\n"), lang, filePath) }), lineCount > MINI_PREVIEW_LINES && _jsxs(Text, { color: colors.quaternary, children: [" \u2514 +", lineCount - MINI_PREVIEW_LINES, " lines ", _jsx(Text, { color: colors.tertiary, children: "^O expand" })] })] })), hasResult && !showFull && !collapseByDefault && !isGrouped && (() => {
|
|
444
|
+
const previewN = isServer ? SERVER_PREVIEW_LINES : PREVIEW_LINES;
|
|
445
|
+
const remaining = lineCount - previewN;
|
|
446
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(MarkdownText, { text: category === "agent"
|
|
447
|
+
? result.split("\n").slice(0, previewN).join("\n")
|
|
448
|
+
: wrapInFence((category === "search" ? formatSearchResult(result) : result).split("\n").slice(0, previewN).join("\n"), lang, filePath) }), remaining > 0 && _jsxs(Text, { color: colors.quaternary, children: [" \u2514 +", remaining, " lines ", _jsx(Text, { color: colors.tertiary, children: "^O expand" })] })] }));
|
|
449
|
+
})()] }));
|
|
427
450
|
}, (prev, next) => {
|
|
428
451
|
// Custom comparator: skip deep-comparing input object reference
|
|
429
452
|
return prev.id === next.id
|
|
@@ -432,5 +455,6 @@ export const ToolIndicator = React.memo(function ToolIndicator({ id: _id, name,
|
|
|
432
455
|
&& prev.result === next.result
|
|
433
456
|
&& prev.durationMs === next.durationMs
|
|
434
457
|
&& prev.name === next.name
|
|
435
|
-
&& prev.count === next.count
|
|
458
|
+
&& prev.count === next.count
|
|
459
|
+
&& prev.progress === next.progress;
|
|
436
460
|
});
|
|
@@ -15,7 +15,8 @@ export interface AgentLoopDeps {
|
|
|
15
15
|
abortRef: React.MutableRefObject<AbortController | null>;
|
|
16
16
|
accTextRef: React.MutableRefObject<string>;
|
|
17
17
|
lastContentUpdateRef: React.MutableRefObject<number>;
|
|
18
|
-
|
|
18
|
+
thinkingChunksRef: React.MutableRefObject<number>;
|
|
19
|
+
thinkingDoneRef: React.MutableRefObject<boolean>;
|
|
19
20
|
rewindManagerRef: React.MutableRefObject<RewindManager>;
|
|
20
21
|
turnIndexRef: React.MutableRefObject<number>;
|
|
21
22
|
setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;
|
|
@@ -7,15 +7,8 @@ import { useCallback, useRef } from "react";
|
|
|
7
7
|
import { runAgentLoop } from "../../services/agent-loop.js";
|
|
8
8
|
import { AgentEventEmitter } from "../../services/agent-events.js";
|
|
9
9
|
import { listBackups } from "../../services/file-history.js";
|
|
10
|
-
function randomVerb() {
|
|
11
|
-
const THINKING_VERBS = [
|
|
12
|
-
"thinking\u2026", "reasoning\u2026", "considering\u2026", "analyzing\u2026", "evaluating\u2026",
|
|
13
|
-
"pondering\u2026", "processing\u2026", "reflecting\u2026", "examining\u2026", "working\u2026",
|
|
14
|
-
];
|
|
15
|
-
return THINKING_VERBS[Math.floor(Math.random() * THINKING_VERBS.length)];
|
|
16
|
-
}
|
|
17
10
|
export function useAgentLoop(deps) {
|
|
18
|
-
const { isStreaming, thinkingEnabled, conversationRef, abortRef, accTextRef, lastContentUpdateRef,
|
|
11
|
+
const { isStreaming, thinkingEnabled, conversationRef, abortRef, accTextRef, lastContentUpdateRef, thinkingChunksRef, thinkingDoneRef, rewindManagerRef, turnIndexRef, setMessages, setStreamingText, setIsStreaming, setActiveTools, setSubagentActivity, setCompletedSubagents, setTeamState, } = deps;
|
|
19
12
|
// Single ref for the unified flush timer — survives across handleSend calls for cleanup
|
|
20
13
|
const flushTimerRef = useRef(null);
|
|
21
14
|
const handleSend = useCallback(async (userMessage, images) => {
|
|
@@ -32,7 +25,8 @@ export function useAgentLoop(deps) {
|
|
|
32
25
|
setCompletedSubagents([]);
|
|
33
26
|
setTeamState(null);
|
|
34
27
|
setIsStreaming(true);
|
|
35
|
-
|
|
28
|
+
thinkingChunksRef.current = 0;
|
|
29
|
+
thinkingDoneRef.current = false;
|
|
36
30
|
const abort = new AbortController();
|
|
37
31
|
abortRef.current = abort;
|
|
38
32
|
accTextRef.current = "";
|
|
@@ -101,7 +95,15 @@ export function useAgentLoop(deps) {
|
|
|
101
95
|
const teammateStatus = new Map();
|
|
102
96
|
const unsub = emitter.onEvent((event) => {
|
|
103
97
|
switch (event.type) {
|
|
98
|
+
case "thinking":
|
|
99
|
+
thinkingChunksRef.current = event.chunkCount;
|
|
100
|
+
markDirty("text");
|
|
101
|
+
break;
|
|
104
102
|
case "text":
|
|
103
|
+
if (thinkingChunksRef.current > 0 && !thinkingDoneRef.current) {
|
|
104
|
+
thinkingDoneRef.current = true;
|
|
105
|
+
markDirty("text");
|
|
106
|
+
}
|
|
105
107
|
accTextRef.current += event.text;
|
|
106
108
|
markDirty("text");
|
|
107
109
|
break;
|
|
@@ -115,6 +117,14 @@ export function useAgentLoop(deps) {
|
|
|
115
117
|
}
|
|
116
118
|
break;
|
|
117
119
|
}
|
|
120
|
+
case "tool_progress": {
|
|
121
|
+
const idx = toolCalls.findIndex(t => t.name === event.toolName && t.status === "running");
|
|
122
|
+
if (idx >= 0) {
|
|
123
|
+
toolCalls[idx].progress = event.progress;
|
|
124
|
+
markDirty("tools");
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
118
128
|
case "team_start":
|
|
119
129
|
teamName = event.name;
|
|
120
130
|
teamTotal = event.taskCount;
|
|
@@ -293,14 +303,9 @@ export function useAgentLoop(deps) {
|
|
|
293
303
|
}
|
|
294
304
|
}
|
|
295
305
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const updated = { ...last, toolCalls: [...last.toolCalls, completedTool] };
|
|
300
|
-
return [...prev.slice(0, -1), updated];
|
|
301
|
-
}
|
|
302
|
-
return [...prev, { role: "assistant", text: "", toolCalls: [completedTool] }];
|
|
303
|
-
});
|
|
306
|
+
// Each tool result is its own message — goes to Static immediately.
|
|
307
|
+
// (Don't batch into the last message — Static can't re-render updated items.)
|
|
308
|
+
setMessages(prev => [...prev, { role: "assistant", text: "", toolCalls: [completedTool] }]);
|
|
304
309
|
setActiveTools([...toolCalls]);
|
|
305
310
|
accTextRef.current = "";
|
|
306
311
|
setStreamingText("");
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
7
7
|
import { type StoreInfo } from "../../services/auth-service.js";
|
|
8
8
|
import type { ChatMessage } from "../MessageList.js";
|
|
9
|
+
import type { NodeInfo } from "../NodeSelector.js";
|
|
10
|
+
import type { NodeStatus, NodeActionResult } from "../NodeManager.js";
|
|
11
|
+
import type { SessionMeta } from "../../services/session-persistence.js";
|
|
9
12
|
export interface SlashCommandDeps {
|
|
10
13
|
exit: () => void;
|
|
11
14
|
toolsExpanded: boolean;
|
|
@@ -27,6 +30,15 @@ export interface SlashCommandDeps {
|
|
|
27
30
|
setServerToolsAvailable: React.Dispatch<React.SetStateAction<number>>;
|
|
28
31
|
setShowRewind: React.Dispatch<React.SetStateAction<boolean>>;
|
|
29
32
|
rewindCheckpointCount: number;
|
|
33
|
+
setNodeSelectMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
34
|
+
setNodeList: React.Dispatch<React.SetStateAction<NodeInfo[]>>;
|
|
35
|
+
setSessionManagerMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
36
|
+
setSessionList: React.Dispatch<React.SetStateAction<SessionMeta[]>>;
|
|
37
|
+
setMemoryManagerMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
38
|
+
setMemoryList: React.Dispatch<React.SetStateAction<string[]>>;
|
|
39
|
+
setThemeSelectMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
40
|
+
setNodeManagerMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
41
|
+
setNodeManagerStatus: React.Dispatch<React.SetStateAction<NodeStatus>>;
|
|
30
42
|
PKG_NAME: string;
|
|
31
43
|
PKG_VERSION: string;
|
|
32
44
|
}
|
|
@@ -34,4 +46,11 @@ export declare function useSlashCommands(deps: SlashCommandDeps): {
|
|
|
34
46
|
handleCommand: (command: string) => Promise<void>;
|
|
35
47
|
handleStoreSelect: (store: StoreInfo) => void;
|
|
36
48
|
handleStoreCancel: () => void;
|
|
49
|
+
handleNodeSelect: (node: NodeInfo) => void;
|
|
50
|
+
handleNodeCancel: () => void;
|
|
51
|
+
handleSessionSelect: (session: SessionMeta) => void;
|
|
52
|
+
handleMemoryDelete: (index: number) => void;
|
|
53
|
+
handleNodeToggle: () => Promise<NodeActionResult>;
|
|
54
|
+
handleNodeRegister: () => Promise<NodeActionResult>;
|
|
55
|
+
handleNodeClose: (resultMessage?: string) => void;
|
|
37
56
|
};
|