wave-code 1.0.5 → 1.0.7
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/dist/components/AgentsManager.d.ts +7 -0
- package/dist/components/AgentsManager.js +109 -0
- package/dist/components/ConfirmationSelector.js +17 -3
- package/dist/components/InputBox.js +7 -21
- package/dist/components/LoginCommand.js +31 -2
- package/dist/components/MarketplaceAddForm.js +16 -2
- package/dist/constants/commands.js +6 -0
- package/dist/contexts/useChat.d.ts +2 -1
- package/dist/contexts/useChat.js +96 -21
- package/dist/hooks/useInputManager.d.ts +2 -0
- package/dist/hooks/useInputManager.js +8 -0
- package/dist/managers/inputHandlers.js +3 -0
- package/dist/managers/inputReducer.d.ts +4 -0
- package/dist/managers/inputReducer.js +8 -0
- package/dist/reducers/agentsManagerReducer.d.ts +26 -0
- package/dist/reducers/agentsManagerReducer.js +54 -0
- package/dist/stdio/agentBridge.d.ts +4 -0
- package/dist/stdio/agentBridge.js +45 -10
- package/dist/stdio/protocol.d.ts +1 -1
- package/dist/utils/rewindCheckpoints.d.ts +2 -2
- package/dist/utils/rewindCheckpoints.js +4 -2
- package/dist/utils/usageSummary.d.ts +0 -4
- package/dist/utils/usageSummary.js +1 -34
- package/package.json +2 -2
- package/src/components/AgentsManager.tsx +290 -0
- package/src/components/ConfirmationSelector.tsx +18 -3
- package/src/components/InputBox.tsx +54 -45
- package/src/components/LoginCommand.tsx +35 -2
- package/src/components/MarketplaceAddForm.tsx +17 -2
- package/src/constants/commands.ts +6 -0
- package/src/contexts/useChat.tsx +159 -72
- package/src/hooks/useInputManager.ts +8 -0
- package/src/managers/inputHandlers.ts +2 -0
- package/src/managers/inputReducer.ts +10 -0
- package/src/reducers/agentsManagerReducer.ts +91 -0
- package/src/stdio/agentBridge.ts +55 -9
- package/src/stdio/protocol.ts +2 -0
- package/src/utils/rewindCheckpoints.ts +3 -2
- package/src/utils/usageSummary.ts +2 -46
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { SubagentConfiguration } from "wave-agent-sdk";
|
|
3
|
+
export interface AgentsManagerProps {
|
|
4
|
+
onCancel: () => void;
|
|
5
|
+
agentDefinitions: SubagentConfiguration[];
|
|
6
|
+
}
|
|
7
|
+
export declare const AgentsManager: React.FC<AgentsManagerProps>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useReducer } from "react";
|
|
3
|
+
import { Box, Text, useInput, useStdout } from "ink";
|
|
4
|
+
import { Markdown } from "./Markdown.js";
|
|
5
|
+
import { agentsManagerReducer, } from "../reducers/agentsManagerReducer.js";
|
|
6
|
+
const SCOPE_LABELS = {
|
|
7
|
+
builtin: "Built-in agents",
|
|
8
|
+
user: "User agents",
|
|
9
|
+
project: "Project agents",
|
|
10
|
+
plugin: "Plugin agents",
|
|
11
|
+
};
|
|
12
|
+
const SCOPE_ORDER = [
|
|
13
|
+
"builtin",
|
|
14
|
+
"user",
|
|
15
|
+
"project",
|
|
16
|
+
"plugin",
|
|
17
|
+
];
|
|
18
|
+
const initialState = {
|
|
19
|
+
selectedIndex: 0,
|
|
20
|
+
viewMode: "list",
|
|
21
|
+
pendingEffect: null,
|
|
22
|
+
};
|
|
23
|
+
export const AgentsManager = ({ onCancel, agentDefinitions, }) => {
|
|
24
|
+
const [state, dispatch] = useReducer(agentsManagerReducer, initialState);
|
|
25
|
+
const { stdout } = useStdout();
|
|
26
|
+
// Handle pending effects
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!state.pendingEffect)
|
|
29
|
+
return;
|
|
30
|
+
const effect = state.pendingEffect;
|
|
31
|
+
dispatch({ type: "CLEAR_PENDING_EFFECT" });
|
|
32
|
+
if (effect.type === "CANCEL") {
|
|
33
|
+
onCancel();
|
|
34
|
+
}
|
|
35
|
+
}, [state.pendingEffect, onCancel]);
|
|
36
|
+
// Flatten definitions (grouped by scope) into one navigable list. Headers
|
|
37
|
+
// and the empty-state line are non-selectable.
|
|
38
|
+
const entries = useMemo(() => {
|
|
39
|
+
const result = [];
|
|
40
|
+
let selectableCount = 0;
|
|
41
|
+
result.push({ kind: "header", label: "AGENTS", selectableIndex: -1 });
|
|
42
|
+
let definitionCount = 0;
|
|
43
|
+
for (const scope of SCOPE_ORDER) {
|
|
44
|
+
const defs = agentDefinitions
|
|
45
|
+
.filter((d) => d.scope === scope)
|
|
46
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
47
|
+
if (defs.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
result.push({
|
|
50
|
+
kind: "header",
|
|
51
|
+
label: SCOPE_LABELS[scope],
|
|
52
|
+
scope,
|
|
53
|
+
selectableIndex: -1,
|
|
54
|
+
});
|
|
55
|
+
for (const def of defs) {
|
|
56
|
+
result.push({
|
|
57
|
+
kind: "definition",
|
|
58
|
+
label: def.name,
|
|
59
|
+
model: def.model,
|
|
60
|
+
sub: def.description,
|
|
61
|
+
scope: def.scope,
|
|
62
|
+
selectableIndex: selectableCount++,
|
|
63
|
+
definition: def,
|
|
64
|
+
});
|
|
65
|
+
definitionCount++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (definitionCount === 0) {
|
|
69
|
+
result.push({
|
|
70
|
+
kind: "empty",
|
|
71
|
+
label: "No agents available",
|
|
72
|
+
selectableIndex: -1,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}, [agentDefinitions]);
|
|
77
|
+
const itemCount = entries.filter((e) => e.selectableIndex >= 0).length;
|
|
78
|
+
// Window slice: center the selected item within the visible area, clamping
|
|
79
|
+
// to the terminal's available rows (reusable pattern from
|
|
80
|
+
// BackgroundTaskManager).
|
|
81
|
+
const availableRows = stdout?.rows ?? 24;
|
|
82
|
+
const maxVisible = Math.max(3, Math.min(15, availableRows - 12));
|
|
83
|
+
const selectedFlatIndex = entries.findIndex((e) => e.selectableIndex === state.selectedIndex);
|
|
84
|
+
const startIndex = Math.max(0, Math.min(selectedFlatIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
|
|
85
|
+
const visibleEntries = entries.slice(startIndex, startIndex + maxVisible);
|
|
86
|
+
useInput((input, key) => {
|
|
87
|
+
dispatch({ type: "HANDLE_KEY", input, key, itemCount });
|
|
88
|
+
});
|
|
89
|
+
const selectedEntry = entries.find((e) => e.selectableIndex === state.selectedIndex);
|
|
90
|
+
// Detail view — body renders fully expanded with no height limit, no
|
|
91
|
+
// clipping and no scrolling (aligned with Claude Code's AgentDetail).
|
|
92
|
+
if (state.viewMode === "detail" && selectedEntry) {
|
|
93
|
+
const def = selectedEntry.definition;
|
|
94
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: "cyan", bold: true, children: ["Agent: ", selectedEntry.label] }) }), _jsxs(Box, { flexDirection: "column", gap: 1, children: [def?.description && (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Description:" }), " ", def.description] }) })), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Model:" }), " ", def?.model || "default (not explicitly configured)"] }) }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "Scope:" }), " ", def ? SCOPE_LABELS[def.scope] : ""] }) }), def?.tools && def.tools.length > 0 && (_jsx(Box, { children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: "blue", children: "Tools:" }), " ", def.tools.join(", ")] }) })), def?.filePath && (_jsx(Box, { children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: "blue", children: "File:" }), " ", def.filePath] }) }))] }), def && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "blue", bold: true, children: "System Prompt:" }), _jsx(Box, { marginLeft: 2, marginRight: 2, children: _jsx(Markdown, { children: def.systemPrompt }) })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Esc or Enter to go back" }) })] }));
|
|
95
|
+
}
|
|
96
|
+
if (itemCount === 0) {
|
|
97
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "Agents" }), _jsx(Text, { children: "No agents available" }), _jsx(Text, { dimColor: true, children: "Press Escape to close" })] }));
|
|
98
|
+
}
|
|
99
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderBottom: false, borderLeft: false, borderRight: false, paddingTop: 1, gap: 1, children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "Agents" }) }), _jsx(Text, { dimColor: true, children: "Select an agent to view details" }), _jsx(Box, { flexDirection: "column", children: visibleEntries.map((entry, index) => {
|
|
100
|
+
const isSelected = entry.selectableIndex === state.selectedIndex;
|
|
101
|
+
if (entry.kind === "header") {
|
|
102
|
+
return (_jsx(Text, { dimColor: true, bold: true, children: entry.label }, `${entry.kind}-${entry.label}-${index}`));
|
|
103
|
+
}
|
|
104
|
+
if (entry.kind === "empty") {
|
|
105
|
+
return (_jsx(Text, { dimColor: true, children: entry.label }, `empty-${index}`));
|
|
106
|
+
}
|
|
107
|
+
return (_jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, wrap: "truncate-end", children: [isSelected ? "▶ " : " ", entry.selectableIndex + 1, ". ", entry.label, entry.model ? (_jsxs(Text, { color: isSelected ? "black" : "gray", children: [" ", "\u00B7 ", entry.model] })) : null, entry.sub ? ` · ${entry.sub}` : ""] }, `${entry.kind}-${entry.selectableIndex}`));
|
|
108
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to select \u00B7 Enter to view details \u00B7 Esc to close" }) })] }));
|
|
109
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useReducer } from "react";
|
|
2
|
+
import { useEffect, useReducer, useRef } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
|
|
5
5
|
import { confirmationReducer } from "../reducers/confirmationReducer.js";
|
|
6
6
|
import { questionReducer } from "../reducers/questionReducer.js";
|
|
7
|
+
import { createBracketedPasteDetector } from "../utils/bracketedPaste.js";
|
|
7
8
|
const getHeaderColor = (header) => {
|
|
8
9
|
const colors = ["red", "green", "blue", "magenta", "cyan"];
|
|
9
10
|
let hash = 0;
|
|
@@ -64,15 +65,28 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
64
65
|
}
|
|
65
66
|
return "Yes, and auto-accept edits";
|
|
66
67
|
};
|
|
68
|
+
const pasteDetectorRef = useRef(createBracketedPasteDetector());
|
|
67
69
|
useInput((input, key) => {
|
|
68
70
|
if (key.escape) {
|
|
69
71
|
onCancel();
|
|
70
72
|
return;
|
|
71
73
|
}
|
|
74
|
+
const result = pasteDetectorRef.current.process(input);
|
|
75
|
+
if (result.kind === "consume") {
|
|
76
|
+
// Content of an in-flight bracketed paste: hold it, never submit.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let cleanInput;
|
|
80
|
+
if (result.kind === "paste") {
|
|
81
|
+
cleanInput = (result.leadingInput ?? "") + result.text;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
cleanInput = result.input;
|
|
85
|
+
}
|
|
72
86
|
if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
73
87
|
questionDispatch({
|
|
74
88
|
type: "HANDLE_KEY",
|
|
75
|
-
input,
|
|
89
|
+
input: cleanInput,
|
|
76
90
|
key,
|
|
77
91
|
questions,
|
|
78
92
|
});
|
|
@@ -80,7 +94,7 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
80
94
|
else {
|
|
81
95
|
dispatch({
|
|
82
96
|
type: "HANDLE_KEY",
|
|
83
|
-
input,
|
|
97
|
+
input: cleanInput,
|
|
84
98
|
key,
|
|
85
99
|
toolName,
|
|
86
100
|
toolInput,
|
|
@@ -7,6 +7,7 @@ import { CommandSelector } from "./CommandSelector.js";
|
|
|
7
7
|
import { HistorySearch } from "./HistorySearch.js";
|
|
8
8
|
import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
|
|
9
9
|
import { McpManager } from "./McpManager.js";
|
|
10
|
+
import { AgentsManager } from "./AgentsManager.js";
|
|
10
11
|
import { RewindCommand } from "./RewindCommand.js";
|
|
11
12
|
import { HelpView } from "./HelpView.js";
|
|
12
13
|
import { StatusCommand } from "./StatusCommand.js";
|
|
@@ -22,7 +23,7 @@ import { useChat } from "../contexts/useChat.js";
|
|
|
22
23
|
export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
|
|
23
24
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
|
|
24
25
|
export const InputBox = ({ isLoading, isCommandRunning, isCompacting, sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 200000, showLoginHint = false, }) => {
|
|
25
|
-
const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, clearMessages, compact, addDir, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, setIsBtwActive, } = useChat();
|
|
26
|
+
const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, clearMessages, compact, addDir, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, setIsBtwActive, agentDefinitions, } = useChat();
|
|
26
27
|
// Ref to hold setInputText so queue callbacks can access it before useInputManager returns
|
|
27
28
|
const setInputTextRef = useRef(() => { });
|
|
28
29
|
const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
|
|
@@ -45,7 +46,7 @@ export const InputBox = ({ isLoading, isCommandRunning, isCompacting, sendMessag
|
|
|
45
46
|
// History search
|
|
46
47
|
showHistorySearch, historySearchQuery,
|
|
47
48
|
// Task/MCP Manager
|
|
48
|
-
showBackgroundTaskManager, showMcpManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, showWorkflowManager, setShowBackgroundTaskManager, setShowMcpManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector, setShowWorkflowManager,
|
|
49
|
+
showBackgroundTaskManager, showMcpManager, showAgentsManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, showWorkflowManager, setShowBackgroundTaskManager, setShowMcpManager, setShowAgentsManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector, setShowWorkflowManager,
|
|
49
50
|
// Permission mode
|
|
50
51
|
permissionMode, setPermissionMode,
|
|
51
52
|
// BTW state
|
|
@@ -101,6 +102,7 @@ export const InputBox = ({ isLoading, isCommandRunning, isCompacting, sendMessag
|
|
|
101
102
|
showModelSelector ||
|
|
102
103
|
showBackgroundTaskManager ||
|
|
103
104
|
showMcpManager ||
|
|
105
|
+
showAgentsManager ||
|
|
104
106
|
showWorkflowManager) {
|
|
105
107
|
return;
|
|
106
108
|
}
|
|
@@ -132,32 +134,16 @@ export const InputBox = ({ isLoading, isCommandRunning, isCompacting, sendMessag
|
|
|
132
134
|
}
|
|
133
135
|
await handleRewindSelect(index);
|
|
134
136
|
};
|
|
135
|
-
|
|
136
|
-
return (_jsx(RewindCommand, { messages: messages, onSelect: handleRewindSelectWithClose, onCancel: handleRewindCancel, getFullMessageThread: getFullMessageThread }));
|
|
137
|
-
}
|
|
138
|
-
if (showHelp) {
|
|
139
|
-
return (_jsx(HelpView, { onCancel: () => setShowHelp(false), commands: slashCommands }));
|
|
140
|
-
}
|
|
141
|
-
if (showStatusCommand) {
|
|
142
|
-
return _jsx(StatusCommand, { onCancel: () => setShowStatusCommand(false) });
|
|
143
|
-
}
|
|
144
|
-
if (showLoginCommand) {
|
|
145
|
-
return _jsx(LoginCommand, { onCancel: () => setShowLoginCommand(false) });
|
|
146
|
-
}
|
|
147
|
-
if (showPluginManager) {
|
|
148
|
-
return (_jsx(PluginManagerShell, { onCancel: () => setShowPluginManager(false), onPluginInstalled: recreateAgent }));
|
|
149
|
-
}
|
|
150
|
-
if (showModelSelector) {
|
|
151
|
-
return (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel }));
|
|
152
|
-
}
|
|
153
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), btwState.question || btwState.answer
|
|
137
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showAgentsManager && (_jsx(AgentsManager, { onCancel: () => setShowAgentsManager(false), agentDefinitions: agentDefinitions })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), showRewindManager && (_jsx(RewindCommand, { messages: messages, onSelect: handleRewindSelectWithClose, onCancel: handleRewindCancel, getFullMessageThread: getFullMessageThread })), showHelp && (_jsx(HelpView, { onCancel: () => setShowHelp(false), commands: slashCommands })), showStatusCommand && (_jsx(StatusCommand, { onCancel: () => setShowStatusCommand(false) })), showLoginCommand && (_jsx(LoginCommand, { onCancel: () => setShowLoginCommand(false) })), showPluginManager && (_jsx(PluginManagerShell, { onCancel: () => setShowPluginManager(false), onPluginInstalled: recreateAgent })), showModelSelector && (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel })), btwState.question || btwState.answer
|
|
154
138
|
? null
|
|
155
139
|
: showBackgroundTaskManager ||
|
|
156
140
|
showMcpManager ||
|
|
141
|
+
showAgentsManager ||
|
|
157
142
|
showRewindManager ||
|
|
158
143
|
showHelp ||
|
|
159
144
|
showStatusCommand ||
|
|
160
145
|
showLoginCommand ||
|
|
161
146
|
showPluginManager ||
|
|
147
|
+
showModelSelector ||
|
|
162
148
|
showWorkflowManager || (_jsxs(Box, { flexDirection: "column", children: [escClearPending && _jsx(Text, { color: "gray", children: "\u518D\u6B21\u6309 Esc \u6E05\u7A7A\u8F93\u5165" }), _jsx(Box, { borderStyle: "single", borderColor: "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsxs(Box, { justifyContent: "space-between", children: [_jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand }), _jsx(Notifications, { latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens, showLoginHint: showLoginHint })] })] }))] }));
|
|
163
149
|
};
|
|
@@ -4,6 +4,7 @@ import { Box, Text, useInput } from "ink";
|
|
|
4
4
|
import { execFile } from "child_process";
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
import { authService } from "wave-agent-sdk";
|
|
7
|
+
import { createBracketedPasteDetector } from "../utils/bracketedPaste.js";
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
function openBrowser(url) {
|
|
9
10
|
const platform = process.platform;
|
|
@@ -26,6 +27,11 @@ export const LoginCommand = ({ onCancel }) => {
|
|
|
26
27
|
const [tokenInput, setTokenInput] = useState("");
|
|
27
28
|
const isLoadingRef = useRef(isLoading);
|
|
28
29
|
isLoadingRef.current = isLoading;
|
|
30
|
+
// Detects and strips bracketed paste markers (\x1b[200~ ... \x1b[201~)
|
|
31
|
+
// that terminals wrap pasted text in. Unlike the main InputBox pipeline,
|
|
32
|
+
// this overlay's token input goes through raw ink useInput, which only
|
|
33
|
+
// strips ONE leading ESC, leaving the markers (e.g. "[200~") in the input.
|
|
34
|
+
const pasteDetectorRef = useRef(createBracketedPasteDetector());
|
|
29
35
|
// Resolve/reject refs for the token promise
|
|
30
36
|
const tokenResolveRef = useRef(null);
|
|
31
37
|
const tokenRejectRef = useRef(null);
|
|
@@ -64,9 +70,31 @@ export const LoginCommand = ({ onCancel }) => {
|
|
|
64
70
|
setTokenInput((prev) => prev.slice(0, -1));
|
|
65
71
|
return;
|
|
66
72
|
}
|
|
67
|
-
// Regular character input (single or pasted multi-char)
|
|
73
|
+
// Regular character input (single or pasted multi-char). Run through the
|
|
74
|
+
// bracketed paste detector: a pasted token arrives wrapped in
|
|
75
|
+
// \x1b[200~ ... \x1b[201~ markers (possibly split across chunks), which
|
|
76
|
+
// must be stripped instead of being appended to the token.
|
|
68
77
|
if (input && !key.ctrl && !key.meta && !key.return && input.length > 0) {
|
|
69
|
-
|
|
78
|
+
const result = pasteDetectorRef.current.process(input);
|
|
79
|
+
if (result.kind === "consume") {
|
|
80
|
+
// In-flight bracketed paste content: hold it; the final chunk
|
|
81
|
+
// delivers the complete text.
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (result.kind === "paste") {
|
|
85
|
+
// Tokens never contain carriage returns — drop \r (CRLF terminals
|
|
86
|
+
// send \r, not \n, in pasted text).
|
|
87
|
+
const leading = result.leadingInput?.replace(/\r/g, "");
|
|
88
|
+
if (leading) {
|
|
89
|
+
setTokenInput((prev) => prev + leading);
|
|
90
|
+
}
|
|
91
|
+
const text = result.text.replace(/\r/g, "");
|
|
92
|
+
if (text) {
|
|
93
|
+
setTokenInput((prev) => prev + text);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setTokenInput((prev) => prev + result.input);
|
|
70
98
|
}
|
|
71
99
|
});
|
|
72
100
|
const handleEnter = async () => {
|
|
@@ -84,6 +112,7 @@ export const LoginCommand = ({ onCancel }) => {
|
|
|
84
112
|
setError("");
|
|
85
113
|
setAuthUrl("");
|
|
86
114
|
setTokenInput("");
|
|
115
|
+
pasteDetectorRef.current.reset();
|
|
87
116
|
setMessage("Starting authentication...");
|
|
88
117
|
// Promise that resolves when user presses Enter with token input
|
|
89
118
|
const readToken = () => new Promise((resolve, reject) => {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useReducer, useEffect } from "react";
|
|
2
|
+
import { useReducer, useEffect, useRef } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
5
5
|
import { marketplaceAddFormReducer, SCOPES, } from "../reducers/marketplaceAddFormReducer.js";
|
|
6
|
+
import { createBracketedPasteDetector } from "../utils/bracketedPaste.js";
|
|
6
7
|
export const MarketplaceAddForm = () => {
|
|
7
8
|
const { state: ctxState, actions } = usePluginManagerContext();
|
|
8
9
|
const [state, dispatch] = useReducer(marketplaceAddFormReducer, {
|
|
@@ -23,11 +24,24 @@ export const MarketplaceAddForm = () => {
|
|
|
23
24
|
}
|
|
24
25
|
dispatch({ type: "CLEAR_PENDING_ACTION" });
|
|
25
26
|
}, [state.pendingAction, actions]);
|
|
27
|
+
const pasteDetectorRef = useRef(createBracketedPasteDetector());
|
|
26
28
|
useInput((input, key) => {
|
|
29
|
+
const result = pasteDetectorRef.current.process(input);
|
|
30
|
+
if (result.kind === "consume") {
|
|
31
|
+
// Content of an in-flight bracketed paste: hold it, never submit.
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let cleanInput;
|
|
35
|
+
if (result.kind === "paste") {
|
|
36
|
+
cleanInput = (result.leadingInput ?? "") + result.text;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
cleanInput = result.input;
|
|
40
|
+
}
|
|
27
41
|
dispatch({
|
|
28
42
|
type: "HANDLE_KEY",
|
|
29
43
|
key,
|
|
30
|
-
input,
|
|
44
|
+
input: cleanInput,
|
|
31
45
|
isLoading: ctxState.isLoading,
|
|
32
46
|
});
|
|
33
47
|
});
|
|
@@ -11,6 +11,12 @@ export const AVAILABLE_COMMANDS = [
|
|
|
11
11
|
description: "View and manage MCP servers",
|
|
12
12
|
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
id: "agents",
|
|
16
|
+
name: "agents",
|
|
17
|
+
description: "List available agents and active subagents",
|
|
18
|
+
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
19
|
+
},
|
|
14
20
|
{
|
|
15
21
|
id: "rewind",
|
|
16
22
|
name: "rewind",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type { Message, McpServerStatus, BackgroundTask, Task, SlashCommand, PermissionDecision, PermissionMode, QueuedMessage, WorkflowRun } from "wave-agent-sdk";
|
|
2
|
+
import type { Message, McpServerStatus, BackgroundTask, Task, SlashCommand, SubagentConfiguration, PermissionDecision, PermissionMode, QueuedMessage, WorkflowRun } from "wave-agent-sdk";
|
|
3
3
|
import { BaseAppProps } from "../types.js";
|
|
4
4
|
export interface ChatContextType {
|
|
5
5
|
messages: Message[];
|
|
@@ -46,6 +46,7 @@ export interface ChatContextType {
|
|
|
46
46
|
stopBackgroundTask: (taskId: string) => boolean;
|
|
47
47
|
slashCommands: SlashCommand[];
|
|
48
48
|
hasSlashCommand: (commandId: string) => boolean;
|
|
49
|
+
agentDefinitions: SubagentConfiguration[];
|
|
49
50
|
permissionMode: PermissionMode;
|
|
50
51
|
setPermissionMode: (mode: PermissionMode) => void;
|
|
51
52
|
isConfirmationVisible: boolean;
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -15,6 +15,66 @@ export const useChat = () => {
|
|
|
15
15
|
}
|
|
16
16
|
return context;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Window-concat throttle for pure-delta streaming updates: chunks arriving
|
|
20
|
+
* within the cooldown window are merged so no delta is lost (a dropped delta
|
|
21
|
+
* would permanently lose content, unlike the accumulated-payload throttle it
|
|
22
|
+
* replaces). Leading edge fires immediately; the trailing edge carries only
|
|
23
|
+
* chunks that arrived within the window. `end` flushes any pending deltas
|
|
24
|
+
* first, then forwards the end signal right away.
|
|
25
|
+
*/
|
|
26
|
+
function createStreamingWindowThrottle(fn, wait) {
|
|
27
|
+
let timer = null;
|
|
28
|
+
let pending = null;
|
|
29
|
+
const fire = (stage) => {
|
|
30
|
+
if (pending) {
|
|
31
|
+
fn({ ...pending, stage });
|
|
32
|
+
pending = null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const throttled = (params) => {
|
|
36
|
+
if (params.stage === "end") {
|
|
37
|
+
// Flush any deltas still pending inside the cooldown window first
|
|
38
|
+
if (timer) {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
timer = null;
|
|
41
|
+
}
|
|
42
|
+
fire("streaming");
|
|
43
|
+
fn(params);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (pending) {
|
|
47
|
+
pending.chunk += params.chunk;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
pending = { messageId: params.messageId, chunk: params.chunk };
|
|
51
|
+
}
|
|
52
|
+
if (!timer) {
|
|
53
|
+
// Leading edge: fire the current delta immediately, then reset pending so
|
|
54
|
+
// the trailing edge only carries chunks arriving within this window
|
|
55
|
+
fire("streaming");
|
|
56
|
+
timer = setTimeout(() => {
|
|
57
|
+
timer = null;
|
|
58
|
+
fire("streaming");
|
|
59
|
+
}, wait);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
throttled.cancel = () => {
|
|
63
|
+
if (timer) {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
timer = null;
|
|
66
|
+
}
|
|
67
|
+
pending = null;
|
|
68
|
+
};
|
|
69
|
+
throttled.flush = () => {
|
|
70
|
+
if (timer) {
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
timer = null;
|
|
73
|
+
}
|
|
74
|
+
fire("streaming");
|
|
75
|
+
};
|
|
76
|
+
return throttled;
|
|
77
|
+
}
|
|
18
78
|
export const ChatProvider = ({ children, bypassPermissions, permissionMode: initialPermissionMode, pluginDirs, additionalDirectories, tools, allowedTools, disallowedTools, workdir, worktreeSession, originalCwd, version, model, mcpServers, }) => {
|
|
19
79
|
const { restoreSessionId, continueLastSession } = useAppConfig();
|
|
20
80
|
const { stdout } = useStdout();
|
|
@@ -26,11 +86,13 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
26
86
|
const [messages, setMessages] = useState([]);
|
|
27
87
|
const [latestTotalTokens, setLatestTotalTokens] = useState(0);
|
|
28
88
|
const [maxInputTokens, setMaxInputTokens] = useState(200000);
|
|
29
|
-
// Throttled incremental streaming updaters — 500ms
|
|
30
|
-
// as the pre-incremental throttledSetMessages.
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
89
|
+
// Throttled incremental streaming updaters — 500ms window-concat, the same
|
|
90
|
+
// interval as the pre-incremental throttledSetMessages. Chunks are pure
|
|
91
|
+
// deltas: within-window chunks are merged so none is dropped, and
|
|
92
|
+
// `stage === "end"` flushes pending deltas + applies the end signal
|
|
93
|
+
// immediately so completion results are never delayed.
|
|
94
|
+
const throttledContentUpdate = useMemo(() => createStreamingWindowThrottle((params) => {
|
|
95
|
+
const { messageId, chunk, stage } = params;
|
|
34
96
|
setMessages((prev) => prev.map((m) => {
|
|
35
97
|
if (m.id !== messageId)
|
|
36
98
|
return m;
|
|
@@ -38,22 +100,23 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
38
100
|
if (textBlockIndex === -1) {
|
|
39
101
|
return {
|
|
40
102
|
...m,
|
|
41
|
-
blocks: [
|
|
42
|
-
...m.blocks,
|
|
43
|
-
{ type: "text", content: accumulated, stage },
|
|
44
|
-
],
|
|
103
|
+
blocks: [...m.blocks, { type: "text", content: chunk, stage }],
|
|
45
104
|
};
|
|
46
105
|
}
|
|
47
106
|
return {
|
|
48
107
|
...m,
|
|
49
108
|
blocks: m.blocks.map((b, idx) => idx === textBlockIndex && b.type === "text"
|
|
50
|
-
? {
|
|
109
|
+
? {
|
|
110
|
+
...b,
|
|
111
|
+
content: (b.content || "") + chunk,
|
|
112
|
+
stage,
|
|
113
|
+
}
|
|
51
114
|
: b),
|
|
52
115
|
};
|
|
53
116
|
}));
|
|
54
117
|
}, 500), []);
|
|
55
|
-
const throttledReasoningUpdate = useMemo(() =>
|
|
56
|
-
const { messageId,
|
|
118
|
+
const throttledReasoningUpdate = useMemo(() => createStreamingWindowThrottle((params) => {
|
|
119
|
+
const { messageId, chunk, stage } = params;
|
|
57
120
|
setMessages((prev) => prev.map((m) => {
|
|
58
121
|
if (m.id !== messageId)
|
|
59
122
|
return m;
|
|
@@ -63,14 +126,18 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
63
126
|
...m,
|
|
64
127
|
blocks: [
|
|
65
128
|
...m.blocks,
|
|
66
|
-
{ type: "reasoning", content:
|
|
129
|
+
{ type: "reasoning", content: chunk, stage },
|
|
67
130
|
],
|
|
68
131
|
};
|
|
69
132
|
}
|
|
70
133
|
return {
|
|
71
134
|
...m,
|
|
72
135
|
blocks: m.blocks.map((b, idx) => idx === reasoningBlockIndex && b.type === "reasoning"
|
|
73
|
-
? {
|
|
136
|
+
? {
|
|
137
|
+
...b,
|
|
138
|
+
content: (b.content || "") + chunk,
|
|
139
|
+
stage,
|
|
140
|
+
}
|
|
74
141
|
: b),
|
|
75
142
|
};
|
|
76
143
|
}));
|
|
@@ -137,6 +204,8 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
137
204
|
const [tasks, setTasks] = useState([]);
|
|
138
205
|
// Command state
|
|
139
206
|
const [slashCommands, setSlashCommands] = useState([]);
|
|
207
|
+
// Agent definitions (for /agents overlay)
|
|
208
|
+
const [agentDefinitions, setAgentDefinitions] = useState([]);
|
|
140
209
|
// Permission state
|
|
141
210
|
const [permissionMode, setPermissionModeState] = useState(initialPermissionMode ||
|
|
142
211
|
(bypassPermissions ? "bypassPermissions" : "default"));
|
|
@@ -211,15 +280,11 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
211
280
|
if (isExpandedRef.current)
|
|
212
281
|
return;
|
|
213
282
|
throttledContentUpdate(params);
|
|
214
|
-
if (params.stage === "end")
|
|
215
|
-
throttledContentUpdate.flush();
|
|
216
283
|
},
|
|
217
284
|
onAssistantReasoningUpdated: (params) => {
|
|
218
285
|
if (isExpandedRef.current)
|
|
219
286
|
return;
|
|
220
287
|
throttledReasoningUpdate(params);
|
|
221
|
-
if (params.stage === "end")
|
|
222
|
-
throttledReasoningUpdate.flush();
|
|
223
288
|
},
|
|
224
289
|
onToolBlockUpdated: (params) => {
|
|
225
290
|
if (isExpandedRef.current)
|
|
@@ -292,14 +357,20 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
292
357
|
}
|
|
293
358
|
: m));
|
|
294
359
|
},
|
|
295
|
-
onCompleteBangMessage: (command, exitCode, messageId) => {
|
|
360
|
+
onCompleteBangMessage: (command, exitCode, messageId, output) => {
|
|
296
361
|
if (isExpandedRef.current)
|
|
297
362
|
return;
|
|
298
363
|
setMessages((prev) => prev.map((m) => m.id === messageId
|
|
299
364
|
? {
|
|
300
365
|
...m,
|
|
301
366
|
blocks: m.blocks.map((b, idx) => idx === m.blocks.length - 1 && b.type === "bang"
|
|
302
|
-
? {
|
|
367
|
+
? {
|
|
368
|
+
...b,
|
|
369
|
+
command,
|
|
370
|
+
exitCode,
|
|
371
|
+
stage: "end",
|
|
372
|
+
...(output !== undefined ? { output } : {}),
|
|
373
|
+
}
|
|
303
374
|
: b),
|
|
304
375
|
}
|
|
305
376
|
: m));
|
|
@@ -418,6 +489,9 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
418
489
|
// Get initial commands
|
|
419
490
|
const agentSlashCommands = agent.getSlashCommands?.() || [];
|
|
420
491
|
setSlashCommands(agentSlashCommands);
|
|
492
|
+
// Get initial agent definitions
|
|
493
|
+
const initialAgentDefinitions = agent.getSubagentConfigurations?.() || [];
|
|
494
|
+
setAgentDefinitions(initialAgentDefinitions);
|
|
421
495
|
}
|
|
422
496
|
catch (error) {
|
|
423
497
|
console.error("Failed to initialize AI manager:", error);
|
|
@@ -437,7 +511,6 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
437
511
|
originalCwd,
|
|
438
512
|
model,
|
|
439
513
|
initialPermissionMode,
|
|
440
|
-
refreshMessages,
|
|
441
514
|
throttledContentUpdate,
|
|
442
515
|
throttledReasoningUpdate,
|
|
443
516
|
throttledToolBlockUpdate,
|
|
@@ -458,6 +531,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
458
531
|
setMessages([]);
|
|
459
532
|
setMcpServerStatuses([]);
|
|
460
533
|
setSlashCommands([]);
|
|
534
|
+
setAgentDefinitions([]);
|
|
461
535
|
setSessionId("");
|
|
462
536
|
setIsLoading(false);
|
|
463
537
|
setLatestTotalTokens(0);
|
|
@@ -779,6 +853,7 @@ export const ChatProvider = ({ children, bypassPermissions, permissionMode: init
|
|
|
779
853
|
stopBackgroundTask,
|
|
780
854
|
slashCommands,
|
|
781
855
|
hasSlashCommand,
|
|
856
|
+
agentDefinitions,
|
|
782
857
|
permissionMode,
|
|
783
858
|
setPermissionMode,
|
|
784
859
|
isConfirmationVisible,
|
|
@@ -16,6 +16,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
|
|
|
16
16
|
historySearchQuery: string;
|
|
17
17
|
showBackgroundTaskManager: boolean;
|
|
18
18
|
showMcpManager: boolean;
|
|
19
|
+
showAgentsManager: boolean;
|
|
19
20
|
showRewindManager: boolean;
|
|
20
21
|
showHelp: boolean;
|
|
21
22
|
showStatusCommand: boolean;
|
|
@@ -49,6 +50,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
|
|
|
49
50
|
processSelectorInput: (char: string) => void;
|
|
50
51
|
setShowBackgroundTaskManager: (show: boolean) => void;
|
|
51
52
|
setShowMcpManager: (show: boolean) => void;
|
|
53
|
+
setShowAgentsManager: (show: boolean) => void;
|
|
52
54
|
setShowRewindManager: (show: boolean) => void;
|
|
53
55
|
setShowHelp: (show: boolean) => void;
|
|
54
56
|
setShowStatusCommand: (show: boolean) => void;
|
|
@@ -174,6 +174,9 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
174
174
|
else if (command === "mcp") {
|
|
175
175
|
dispatch({ type: "SET_SHOW_MCP_MANAGER", payload: true });
|
|
176
176
|
}
|
|
177
|
+
else if (command === "agents") {
|
|
178
|
+
dispatch({ type: "SET_SHOW_AGENTS_MANAGER", payload: true });
|
|
179
|
+
}
|
|
177
180
|
else if (command === "rewind") {
|
|
178
181
|
dispatch({ type: "SET_SHOW_REWIND_MANAGER", payload: true });
|
|
179
182
|
}
|
|
@@ -378,6 +381,9 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
378
381
|
const setShowMcpManager = useCallback((show) => {
|
|
379
382
|
dispatch({ type: "SET_SHOW_MCP_MANAGER", payload: show });
|
|
380
383
|
}, []);
|
|
384
|
+
const setShowAgentsManager = useCallback((show) => {
|
|
385
|
+
dispatch({ type: "SET_SHOW_AGENTS_MANAGER", payload: show });
|
|
386
|
+
}, []);
|
|
381
387
|
const setShowRewindManager = useCallback((show) => {
|
|
382
388
|
dispatch({ type: "SET_SHOW_REWIND_MANAGER", payload: show });
|
|
383
389
|
}, []);
|
|
@@ -506,6 +512,7 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
506
512
|
historySearchQuery: state.historySearchQuery,
|
|
507
513
|
showBackgroundTaskManager: state.showBackgroundTaskManager,
|
|
508
514
|
showMcpManager: state.showMcpManager,
|
|
515
|
+
showAgentsManager: state.showAgentsManager,
|
|
509
516
|
showRewindManager: state.showRewindManager,
|
|
510
517
|
showHelp: state.showHelp,
|
|
511
518
|
showStatusCommand: state.showStatusCommand,
|
|
@@ -545,6 +552,7 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
545
552
|
// Bash/MCP Manager
|
|
546
553
|
setShowBackgroundTaskManager,
|
|
547
554
|
setShowMcpManager,
|
|
555
|
+
setShowAgentsManager,
|
|
548
556
|
setShowRewindManager,
|
|
549
557
|
setShowHelp,
|
|
550
558
|
setShowStatusCommand,
|
|
@@ -264,6 +264,9 @@ export const handleCommandSelect = (state, dispatch, callbacks, command) => {
|
|
|
264
264
|
else if (command === "mcp") {
|
|
265
265
|
dispatch({ type: "SET_SHOW_MCP_MANAGER", payload: true });
|
|
266
266
|
}
|
|
267
|
+
else if (command === "agents") {
|
|
268
|
+
dispatch({ type: "SET_SHOW_AGENTS_MANAGER", payload: true });
|
|
269
|
+
}
|
|
267
270
|
else if (command === "rewind") {
|
|
268
271
|
dispatch({ type: "SET_SHOW_REWIND_MANAGER", payload: true });
|
|
269
272
|
}
|