wave-code 0.4.0 → 0.6.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/dist/commands/plugin/uninstall.js +1 -1
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +38 -2
- package/dist/components/BackgroundTaskManager.d.ts +6 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
- package/dist/components/BackgroundTaskManager.js +114 -0
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +39 -5
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +13 -5
- package/dist/components/CompressDisplay.d.ts.map +1 -1
- package/dist/components/CompressDisplay.js +6 -10
- package/dist/components/ConfirmationDetails.d.ts +9 -0
- package/dist/components/ConfirmationDetails.d.ts.map +1 -0
- package/dist/components/ConfirmationDetails.js +53 -0
- package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
- package/dist/components/ConfirmationSelector.d.ts.map +1 -0
- package/dist/components/{Confirmation.js → ConfirmationSelector.js} +92 -101
- package/dist/components/DiffDisplay.d.ts +0 -1
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +82 -60
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +2 -2
- package/dist/components/HistorySearch.d.ts.map +1 -1
- package/dist/components/HistorySearch.js +12 -4
- package/dist/components/InputBox.d.ts +1 -3
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +9 -18
- package/dist/components/LoadingIndicator.d.ts +11 -0
- package/dist/components/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/LoadingIndicator.js +6 -0
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +114 -120
- package/dist/components/MessageItem.d.ts.map +1 -1
- package/dist/components/MessageItem.js +1 -2
- package/dist/components/MessageList.d.ts +2 -3
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +7 -7
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +4 -12
- package/dist/components/PluginDetail.js +1 -1
- package/dist/components/RewindCommand.d.ts +4 -0
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +19 -2
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +12 -5
- package/dist/components/TaskList.d.ts +3 -0
- package/dist/components/TaskList.d.ts.map +1 -0
- package/dist/components/TaskList.js +49 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/useChat.d.ts +15 -6
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +52 -43
- package/dist/hooks/useInputManager.d.ts +2 -13
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +8 -57
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +8 -4
- package/dist/hooks/useTasks.d.ts +2 -0
- package/dist/hooks/useTasks.d.ts.map +1 -0
- package/dist/hooks/useTasks.js +5 -0
- package/dist/managers/InputManager.d.ts +5 -28
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +26 -127
- package/package.json +9 -10
- package/src/commands/plugin/uninstall.ts +1 -1
- package/src/components/App.tsx +50 -3
- package/src/components/{BashShellManager.tsx → BackgroundTaskManager.tsx} +79 -73
- package/src/components/ChatInterface.tsx +79 -23
- package/src/components/CommandSelector.tsx +38 -20
- package/src/components/CompressDisplay.tsx +5 -22
- package/src/components/ConfirmationDetails.tsx +108 -0
- package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +162 -187
- package/src/components/DiffDisplay.tsx +122 -107
- package/src/components/FileSelector.tsx +0 -2
- package/src/components/HistorySearch.tsx +45 -21
- package/src/components/InputBox.tsx +14 -34
- package/src/components/LoadingIndicator.tsx +56 -0
- package/src/components/Markdown.tsx +126 -318
- package/src/components/MessageItem.tsx +1 -3
- package/src/components/MessageList.tsx +10 -67
- package/src/components/PlanDisplay.tsx +5 -33
- package/src/components/PluginDetail.tsx +1 -1
- package/src/components/RewindCommand.tsx +38 -1
- package/src/components/SubagentBlock.tsx +28 -14
- package/src/components/TaskList.tsx +70 -0
- package/src/components/ToolResultDisplay.tsx +6 -2
- package/src/contexts/useChat.tsx +82 -60
- package/src/hooks/useInputManager.ts +9 -73
- package/src/hooks/usePluginManager.ts +10 -4
- package/src/hooks/useTasks.ts +6 -0
- package/src/managers/InputManager.ts +30 -157
- package/dist/components/BashShellManager.d.ts +0 -6
- package/dist/components/BashShellManager.d.ts.map +0 -1
- package/dist/components/BashShellManager.js +0 -116
- package/dist/components/Confirmation.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.d.ts +0 -8
- package/dist/components/MemoryDisplay.d.ts.map +0 -1
- package/dist/components/MemoryDisplay.js +0 -25
- package/dist/components/MemoryTypeSelector.d.ts +0 -8
- package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
- package/dist/components/MemoryTypeSelector.js +0 -38
- package/src/components/MemoryDisplay.tsx +0 -62
- package/src/components/MemoryTypeSelector.tsx +0 -98
|
@@ -6,13 +6,30 @@ export interface RewindCommandProps {
|
|
|
6
6
|
messages: Message[];
|
|
7
7
|
onSelect: (index: number) => void;
|
|
8
8
|
onCancel: () => void;
|
|
9
|
+
getFullMessageThread?: () => Promise<{
|
|
10
|
+
messages: Message[];
|
|
11
|
+
sessionIds: string[];
|
|
12
|
+
}>;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
export const RewindCommand: React.FC<RewindCommandProps> = ({
|
|
12
|
-
messages,
|
|
16
|
+
messages: initialMessages,
|
|
13
17
|
onSelect,
|
|
14
18
|
onCancel,
|
|
19
|
+
getFullMessageThread,
|
|
15
20
|
}) => {
|
|
21
|
+
const [messages, setMessages] = useState<Message[]>(initialMessages);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(!!getFullMessageThread);
|
|
23
|
+
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (getFullMessageThread) {
|
|
26
|
+
getFullMessageThread().then(({ messages: fullMessages }) => {
|
|
27
|
+
setMessages(fullMessages);
|
|
28
|
+
setIsLoading(false);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}, [getFullMessageThread]);
|
|
32
|
+
|
|
16
33
|
// Filter user messages as checkpoints
|
|
17
34
|
const checkpoints = messages
|
|
18
35
|
.map((msg, index) => ({ msg, index }))
|
|
@@ -20,6 +37,11 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
|
|
|
20
37
|
|
|
21
38
|
const [selectedIndex, setSelectedIndex] = useState(checkpoints.length - 1);
|
|
22
39
|
|
|
40
|
+
// Update selectedIndex when checkpoints change (after loading full thread)
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
setSelectedIndex(checkpoints.length - 1);
|
|
43
|
+
}, [checkpoints.length]);
|
|
44
|
+
|
|
23
45
|
useInput((input, key) => {
|
|
24
46
|
if (key.return) {
|
|
25
47
|
if (checkpoints.length > 0 && selectedIndex >= 0) {
|
|
@@ -44,6 +66,21 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
|
|
|
44
66
|
}
|
|
45
67
|
});
|
|
46
68
|
|
|
69
|
+
if (isLoading) {
|
|
70
|
+
return (
|
|
71
|
+
<Box
|
|
72
|
+
flexDirection="column"
|
|
73
|
+
paddingX={1}
|
|
74
|
+
borderStyle="single"
|
|
75
|
+
borderColor="cyan"
|
|
76
|
+
borderLeft={false}
|
|
77
|
+
borderRight={false}
|
|
78
|
+
>
|
|
79
|
+
<Text color="cyan">Loading full message thread...</Text>
|
|
80
|
+
</Box>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
47
84
|
if (checkpoints.length === 0) {
|
|
48
85
|
return (
|
|
49
86
|
<Box
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import type { SubagentBlock as SubagentBlockType } from "wave-agent-sdk";
|
|
4
4
|
import { useChat } from "../contexts/useChat.js";
|
|
@@ -9,10 +9,21 @@ interface SubagentBlockProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
12
|
-
const { subagentMessages } = useChat();
|
|
12
|
+
const { subagentMessages, subagentLatestTokens } = useChat();
|
|
13
13
|
|
|
14
14
|
// Get messages for this subagent from context
|
|
15
|
-
const messages =
|
|
15
|
+
const messages = useMemo(
|
|
16
|
+
() => subagentMessages[block.subagentId] || [],
|
|
17
|
+
[subagentMessages, block.subagentId],
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Get latest turn tokens for this subagent
|
|
21
|
+
const latestTurnTokens = subagentLatestTokens[block.subagentId] || 0;
|
|
22
|
+
|
|
23
|
+
// If the subagent is running in the background, don't show the block
|
|
24
|
+
if (block.runInBackground) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
16
27
|
|
|
17
28
|
// Status indicator mapping
|
|
18
29
|
const getStatusIndicator = (status: SubagentBlockType["status"]) => {
|
|
@@ -58,7 +69,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
58
69
|
return { tools: tools.reverse(), totalToolCount }; // Reverse to show oldest first, newest last
|
|
59
70
|
};
|
|
60
71
|
|
|
61
|
-
const { tools: lastTwoTools
|
|
72
|
+
const { tools: lastTwoTools } = getLastTwoTools();
|
|
62
73
|
|
|
63
74
|
// Get the last text message content if completed
|
|
64
75
|
const getLastTextMessage = () => {
|
|
@@ -88,9 +99,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
88
99
|
borderStyle="classic"
|
|
89
100
|
borderColor="magenta"
|
|
90
101
|
paddingX={1}
|
|
91
|
-
paddingY={0}
|
|
92
102
|
flexDirection="column"
|
|
93
|
-
marginBottom={1}
|
|
94
103
|
>
|
|
95
104
|
{/* Header Section */}
|
|
96
105
|
<Box flexDirection="row" gap={1}>
|
|
@@ -102,26 +111,31 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
102
111
|
</Text>
|
|
103
112
|
<Text color="gray" dimColor>
|
|
104
113
|
{" "}
|
|
105
|
-
({messages.length} messages
|
|
114
|
+
({messages.length} messages
|
|
115
|
+
{latestTurnTokens > 0 && (
|
|
116
|
+
<>
|
|
117
|
+
{" | "}
|
|
118
|
+
<Text color="blue" bold>
|
|
119
|
+
{latestTurnTokens.toLocaleString()}
|
|
120
|
+
</Text>
|
|
121
|
+
{" tokens"}
|
|
122
|
+
</>
|
|
123
|
+
)}
|
|
124
|
+
)
|
|
106
125
|
</Text>
|
|
107
126
|
</Box>
|
|
108
127
|
</Box>
|
|
109
128
|
|
|
110
129
|
{/* Last Text Message Section */}
|
|
111
130
|
{lastTextMessage && (
|
|
112
|
-
<Box
|
|
131
|
+
<Box>
|
|
113
132
|
<Markdown>{lastTextMessage}</Markdown>
|
|
114
133
|
</Box>
|
|
115
134
|
)}
|
|
116
135
|
|
|
117
136
|
{/* Tool Names Section - Vertical List */}
|
|
118
137
|
{block.status !== "completed" && lastTwoTools.length > 0 && (
|
|
119
|
-
<Box flexDirection="column"
|
|
120
|
-
{totalToolCount > 2 && (
|
|
121
|
-
<Text color="gray" dimColor>
|
|
122
|
-
...
|
|
123
|
-
</Text>
|
|
124
|
-
)}
|
|
138
|
+
<Box flexDirection="column">
|
|
125
139
|
{lastTwoTools.map((tool, index) => (
|
|
126
140
|
<Box key={index} flexDirection="row">
|
|
127
141
|
<Text color="magenta">🔧 </Text>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useChat } from "../contexts/useChat.js";
|
|
3
|
+
import { Box, Text, useStdout } from "ink";
|
|
4
|
+
import { useTasks } from "../hooks/useTasks.js";
|
|
5
|
+
|
|
6
|
+
export const TaskList: React.FC = () => {
|
|
7
|
+
const tasks = useTasks();
|
|
8
|
+
const { isTaskListVisible } = useChat();
|
|
9
|
+
const { stdout } = useStdout();
|
|
10
|
+
const terminalWidth = stdout?.columns ?? 80;
|
|
11
|
+
const maxSubjectWidth = Math.max(20, terminalWidth - 10);
|
|
12
|
+
|
|
13
|
+
if (tasks.length === 0 || !isTaskListVisible) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const getStatusIcon = (status: string, isBlocked: boolean) => {
|
|
18
|
+
if (isBlocked) {
|
|
19
|
+
return <Text color="red">🔒</Text>;
|
|
20
|
+
}
|
|
21
|
+
switch (status) {
|
|
22
|
+
case "pending":
|
|
23
|
+
return <Text color="gray">○</Text>;
|
|
24
|
+
case "in_progress":
|
|
25
|
+
return <Text color="yellow">●</Text>;
|
|
26
|
+
case "completed":
|
|
27
|
+
return <Text color="green">✓</Text>;
|
|
28
|
+
case "deleted":
|
|
29
|
+
return <Text color="red">✕</Text>;
|
|
30
|
+
default:
|
|
31
|
+
return <Text color="gray">?</Text>;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const truncate = (text: string, maxWidth: number) => {
|
|
36
|
+
if (text.length <= maxWidth) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
return text.slice(0, maxWidth - 3) + "...";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Box flexDirection="column">
|
|
44
|
+
{tasks.map((task) => {
|
|
45
|
+
const isDimmed =
|
|
46
|
+
task.status === "completed" || task.status === "deleted";
|
|
47
|
+
const isBlocked = task.blockedBy && task.blockedBy.length > 0;
|
|
48
|
+
const blockingTaskIds = isBlocked
|
|
49
|
+
? task.blockedBy.map((id) => `#${id}`)
|
|
50
|
+
: [];
|
|
51
|
+
|
|
52
|
+
const blockedByText =
|
|
53
|
+
isBlocked && blockingTaskIds.length > 0
|
|
54
|
+
? ` (Blocked by: ${blockingTaskIds.join(", ")})`
|
|
55
|
+
: "";
|
|
56
|
+
|
|
57
|
+
const fullText = `${task.subject}${blockedByText}`;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Box key={task.id} gap={1}>
|
|
61
|
+
{getStatusIcon(task.status, isBlocked)}
|
|
62
|
+
<Text dimColor={isDimmed}>
|
|
63
|
+
{truncate(fullText, maxSubjectWidth)}
|
|
64
|
+
</Text>
|
|
65
|
+
</Box>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</Box>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -44,6 +44,9 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
44
44
|
|
|
45
45
|
const toolName = name ? String(name) : "Tool";
|
|
46
46
|
|
|
47
|
+
const isBackgroundable =
|
|
48
|
+
stage === "running" && (toolName === "Bash" || toolName === "Task");
|
|
49
|
+
|
|
47
50
|
// Get shortResult, if not available show last 5 lines of result
|
|
48
51
|
const getShortResult = () => {
|
|
49
52
|
if (block.shortResult) {
|
|
@@ -65,7 +68,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
65
68
|
const shortResult = getShortResult();
|
|
66
69
|
|
|
67
70
|
return (
|
|
68
|
-
<Box flexDirection="column"
|
|
71
|
+
<Box flexDirection="column">
|
|
69
72
|
<Box>
|
|
70
73
|
<Text color="magenta">🔧 </Text>
|
|
71
74
|
<Text color="white">{toolName}</Text>
|
|
@@ -76,6 +79,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
76
79
|
<Text color={getStatusColor()}> {getStatusText()}</Text>
|
|
77
80
|
{/* Display image indicator */}
|
|
78
81
|
{hasImages() && <Text color="blue"> {getImageIndicator()}</Text>}
|
|
82
|
+
{isBackgroundable && <Text color="gray"> [Ctrl-B] Background</Text>}
|
|
79
83
|
</Box>
|
|
80
84
|
|
|
81
85
|
{/* Display shortResult in collapsed state */}
|
|
@@ -87,7 +91,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
87
91
|
flexDirection="column"
|
|
88
92
|
>
|
|
89
93
|
{shortResult.split("\n").map((line, index) => (
|
|
90
|
-
<Text key={index} color="
|
|
94
|
+
<Text key={index} color="gray">
|
|
91
95
|
{line}
|
|
92
96
|
</Text>
|
|
93
97
|
))}
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -11,7 +11,8 @@ import { useAppConfig } from "./useAppConfig.js";
|
|
|
11
11
|
import type {
|
|
12
12
|
Message,
|
|
13
13
|
McpServerStatus,
|
|
14
|
-
|
|
14
|
+
BackgroundTask,
|
|
15
|
+
Task,
|
|
15
16
|
SlashCommand,
|
|
16
17
|
PermissionDecision,
|
|
17
18
|
PermissionMode,
|
|
@@ -30,9 +31,10 @@ export interface ChatContextType {
|
|
|
30
31
|
isLoading: boolean;
|
|
31
32
|
isCommandRunning: boolean;
|
|
32
33
|
isCompressing: boolean;
|
|
33
|
-
userInputHistory: string[];
|
|
34
34
|
// Message display state
|
|
35
35
|
isExpanded: boolean;
|
|
36
|
+
isTaskListVisible: boolean;
|
|
37
|
+
setIsTaskListVisible: (visible: boolean) => void;
|
|
36
38
|
// AI functionality
|
|
37
39
|
sessionId: string;
|
|
38
40
|
sendMessage: (
|
|
@@ -41,23 +43,24 @@ export interface ChatContextType {
|
|
|
41
43
|
) => Promise<void>;
|
|
42
44
|
abortMessage: () => void;
|
|
43
45
|
latestTotalTokens: number;
|
|
44
|
-
// Memory functionality
|
|
45
|
-
saveMemory: (message: string, type: "project" | "user") => Promise<void>;
|
|
46
46
|
// MCP functionality
|
|
47
47
|
mcpServers: McpServerStatus[];
|
|
48
48
|
connectMcpServer: (serverName: string) => Promise<boolean>;
|
|
49
49
|
disconnectMcpServer: (serverName: string) => Promise<boolean>;
|
|
50
|
-
// Background
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
// Background tasks
|
|
51
|
+
backgroundTasks: BackgroundTask[];
|
|
52
|
+
// Session tasks
|
|
53
|
+
sessionTasks: Task[];
|
|
54
|
+
getBackgroundTaskOutput: (
|
|
55
|
+
taskId: string,
|
|
54
56
|
) => { stdout: string; stderr: string; status: string } | null;
|
|
55
|
-
|
|
57
|
+
stopBackgroundTask: (taskId: string) => boolean;
|
|
56
58
|
// Slash Command functionality
|
|
57
59
|
slashCommands: SlashCommand[];
|
|
58
60
|
hasSlashCommand: (commandId: string) => boolean;
|
|
59
61
|
// Subagent messages
|
|
60
62
|
subagentMessages: Record<string, Message[]>;
|
|
63
|
+
subagentLatestTokens: Record<string, number>;
|
|
61
64
|
// Permission functionality
|
|
62
65
|
permissionMode: PermissionMode;
|
|
63
66
|
setPermissionMode: (mode: PermissionMode) => void;
|
|
@@ -78,9 +81,17 @@ export interface ChatContextType {
|
|
|
78
81
|
hideConfirmation: () => void;
|
|
79
82
|
handleConfirmationDecision: (decision: PermissionDecision) => void;
|
|
80
83
|
handleConfirmationCancel: () => void;
|
|
84
|
+
// Background current task
|
|
85
|
+
backgroundCurrentTask: () => void;
|
|
81
86
|
// Rewind functionality
|
|
82
87
|
rewindId: number;
|
|
83
88
|
handleRewindSelect: (index: number) => Promise<void>;
|
|
89
|
+
getFullMessageThread: () => Promise<{
|
|
90
|
+
messages: Message[];
|
|
91
|
+
sessionIds: string[];
|
|
92
|
+
}>;
|
|
93
|
+
wasLastDetailsTooTall: number;
|
|
94
|
+
setWasLastDetailsTooTall: React.Dispatch<React.SetStateAction<number>>;
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -108,6 +119,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
108
119
|
|
|
109
120
|
// Message Display State
|
|
110
121
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
122
|
+
const [isTaskListVisible, setIsTaskListVisible] = useState(true);
|
|
111
123
|
|
|
112
124
|
// AI State
|
|
113
125
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
@@ -116,15 +128,14 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
116
128
|
const [sessionId, setSessionId] = useState("");
|
|
117
129
|
const [isCommandRunning, setIsCommandRunning] = useState(false);
|
|
118
130
|
const [isCompressing, setIsCompressing] = useState(false);
|
|
119
|
-
const [userInputHistory, setUserInputHistory] = useState<string[]>([]);
|
|
120
131
|
|
|
121
132
|
// MCP State
|
|
122
133
|
const [mcpServers, setMcpServers] = useState<McpServerStatus[]>([]);
|
|
123
134
|
|
|
124
|
-
// Background
|
|
125
|
-
const [
|
|
126
|
-
|
|
127
|
-
);
|
|
135
|
+
// Background tasks state
|
|
136
|
+
const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
|
|
137
|
+
// Session tasks state
|
|
138
|
+
const [sessionTasks, setSessionTasks] = useState<Task[]>([]);
|
|
128
139
|
|
|
129
140
|
// Command state
|
|
130
141
|
const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
|
|
@@ -133,6 +144,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
133
144
|
const [subagentMessages, setSubagentMessages] = useState<
|
|
134
145
|
Record<string, Message[]>
|
|
135
146
|
>({});
|
|
147
|
+
const [subagentLatestTokens, setSubagentLatestTokens] = useState<
|
|
148
|
+
Record<string, number>
|
|
149
|
+
>({});
|
|
136
150
|
|
|
137
151
|
// Permission state
|
|
138
152
|
const [permissionMode, setPermissionModeState] =
|
|
@@ -171,6 +185,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
171
185
|
// Rewind state
|
|
172
186
|
const [rewindId, setRewindId] = useState(0);
|
|
173
187
|
|
|
188
|
+
// Confirmation too tall state
|
|
189
|
+
const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
|
|
190
|
+
|
|
174
191
|
const agentRef = useRef<Agent | null>(null);
|
|
175
192
|
|
|
176
193
|
// Permission confirmation methods with queue support
|
|
@@ -209,29 +226,36 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
209
226
|
setMcpServers([...servers]);
|
|
210
227
|
},
|
|
211
228
|
onSessionIdChange: (sessionId) => {
|
|
212
|
-
|
|
213
|
-
setSessionId(sessionId);
|
|
214
|
-
});
|
|
229
|
+
setSessionId(sessionId);
|
|
215
230
|
},
|
|
216
231
|
onLatestTotalTokensChange: (tokens) => {
|
|
217
232
|
setlatestTotalTokens(tokens);
|
|
218
233
|
},
|
|
219
|
-
onUserInputHistoryChange: (history) => {
|
|
220
|
-
setUserInputHistory([...history]);
|
|
221
|
-
},
|
|
222
234
|
onCompressionStateChange: (isCompressingState) => {
|
|
223
235
|
setIsCompressing(isCompressingState);
|
|
224
236
|
},
|
|
225
|
-
|
|
226
|
-
|
|
237
|
+
onTasksChange: (tasks) => {
|
|
238
|
+
setBackgroundTasks([...tasks]);
|
|
239
|
+
},
|
|
240
|
+
onSessionTasksChange: (tasks) => {
|
|
241
|
+
setSessionTasks([...tasks]);
|
|
227
242
|
},
|
|
228
|
-
onSubagentMessagesChange: (subagentId, messages) => {
|
|
243
|
+
onSubagentMessagesChange: (subagentId: string, messages: Message[]) => {
|
|
229
244
|
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
230
245
|
setSubagentMessages((prev) => ({
|
|
231
246
|
...prev,
|
|
232
247
|
[subagentId]: [...messages],
|
|
233
248
|
}));
|
|
234
249
|
},
|
|
250
|
+
onSubagentLatestTotalTokensChange: (
|
|
251
|
+
subagentId: string,
|
|
252
|
+
tokens: number,
|
|
253
|
+
) => {
|
|
254
|
+
setSubagentLatestTokens((prev) => ({
|
|
255
|
+
...prev,
|
|
256
|
+
[subagentId]: tokens,
|
|
257
|
+
}));
|
|
258
|
+
},
|
|
235
259
|
onPermissionModeChange: (mode) => {
|
|
236
260
|
setPermissionModeState(mode);
|
|
237
261
|
},
|
|
@@ -283,7 +307,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
283
307
|
setlatestTotalTokens(agent.latestTotalTokens);
|
|
284
308
|
setIsCommandRunning(agent.isCommandRunning);
|
|
285
309
|
setIsCompressing(agent.isCompressing);
|
|
286
|
-
setUserInputHistory(agent.userInputHistory);
|
|
287
310
|
setPermissionModeState(agent.getPermissionMode());
|
|
288
311
|
|
|
289
312
|
// Get initial MCP servers state
|
|
@@ -338,16 +361,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
338
361
|
if (!hasTextContent && !hasImageAttachments) return;
|
|
339
362
|
|
|
340
363
|
try {
|
|
341
|
-
// Handle memory mode - check if it's a memory message (starts with # and only one line)
|
|
342
|
-
if (content.startsWith("#") && !content.includes("\n")) {
|
|
343
|
-
const memoryText = content.substring(1).trim();
|
|
344
|
-
if (!memoryText) return;
|
|
345
|
-
|
|
346
|
-
// In memory mode, don't add user message, only wait for user to choose memory type then add assistant message
|
|
347
|
-
// Don't auto-save, wait for user to choose memory type
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
364
|
// Handle bash mode - check if it's a bash command (starts with ! and only one line)
|
|
352
365
|
if (content.startsWith("!") && !content.includes("\n")) {
|
|
353
366
|
const command = content.substring(1).trim();
|
|
@@ -394,14 +407,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
394
407
|
agentRef.current?.abortMessage();
|
|
395
408
|
}, []);
|
|
396
409
|
|
|
397
|
-
// Memory save function - delegate to Agent
|
|
398
|
-
const saveMemory = useCallback(
|
|
399
|
-
async (message: string, type: "project" | "user") => {
|
|
400
|
-
await agentRef.current?.saveMemory(message, type);
|
|
401
|
-
},
|
|
402
|
-
[],
|
|
403
|
-
);
|
|
404
|
-
|
|
405
410
|
// Permission management methods
|
|
406
411
|
const setPermissionMode = useCallback((mode: PermissionMode) => {
|
|
407
412
|
setPermissionModeState((prev) => {
|
|
@@ -422,15 +427,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
422
427
|
return (await agentRef.current?.disconnectMcpServer(serverName)) ?? false;
|
|
423
428
|
}, []);
|
|
424
429
|
|
|
425
|
-
// Background
|
|
426
|
-
const
|
|
430
|
+
// Background task management methods - delegate to Agent
|
|
431
|
+
const getBackgroundTaskOutput = useCallback((taskId: string) => {
|
|
427
432
|
if (!agentRef.current) return null;
|
|
428
|
-
return agentRef.current.
|
|
433
|
+
return agentRef.current.getBackgroundTaskOutput(taskId);
|
|
429
434
|
}, []);
|
|
430
435
|
|
|
431
|
-
const
|
|
436
|
+
const stopBackgroundTask = useCallback((taskId: string) => {
|
|
432
437
|
if (!agentRef.current) return false;
|
|
433
|
-
return agentRef.current.
|
|
438
|
+
return agentRef.current.stopBackgroundTask(taskId);
|
|
434
439
|
}, []);
|
|
435
440
|
|
|
436
441
|
const hasSlashCommand = useCallback((commandId: string) => {
|
|
@@ -482,33 +487,44 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
482
487
|
hideConfirmation();
|
|
483
488
|
}, [currentConfirmation, hideConfirmation]);
|
|
484
489
|
|
|
490
|
+
const backgroundCurrentTask = useCallback(() => {
|
|
491
|
+
agentRef.current?.backgroundCurrentTask();
|
|
492
|
+
}, []);
|
|
493
|
+
|
|
485
494
|
const handleRewindSelect = useCallback(async (index: number) => {
|
|
486
495
|
if (agentRef.current) {
|
|
487
496
|
try {
|
|
488
497
|
await agentRef.current.truncateHistory(index);
|
|
489
498
|
|
|
490
499
|
// Clear terminal screen after rewind
|
|
491
|
-
|
|
492
|
-
setRewindId((prev) => prev + 1);
|
|
493
|
-
});
|
|
500
|
+
setRewindId((prev) => prev + 1);
|
|
494
501
|
} catch (error) {
|
|
495
502
|
logger.error("Failed to rewind:", error);
|
|
496
503
|
}
|
|
497
504
|
}
|
|
498
505
|
}, []);
|
|
499
506
|
|
|
507
|
+
const getFullMessageThread = useCallback(async () => {
|
|
508
|
+
if (agentRef.current) {
|
|
509
|
+
return await agentRef.current.getFullMessageThread();
|
|
510
|
+
}
|
|
511
|
+
return { messages: [], sessionIds: [] };
|
|
512
|
+
}, []);
|
|
513
|
+
|
|
500
514
|
// Listen for Ctrl+O hotkey to toggle collapse/expand state and ESC to cancel confirmation
|
|
501
515
|
useInput((input, key) => {
|
|
502
516
|
if (key.ctrl && input === "o") {
|
|
503
517
|
// Clear terminal screen when expanded state changes
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
return newExpanded;
|
|
508
|
-
});
|
|
518
|
+
setIsExpanded((prev) => {
|
|
519
|
+
const newExpanded = !prev;
|
|
520
|
+
return newExpanded;
|
|
509
521
|
});
|
|
510
522
|
}
|
|
511
523
|
|
|
524
|
+
if (key.ctrl && input === "t") {
|
|
525
|
+
setIsTaskListVisible((prev) => !prev);
|
|
526
|
+
}
|
|
527
|
+
|
|
512
528
|
// Handle ESC key to cancel confirmation
|
|
513
529
|
if (key.escape && isConfirmationVisible) {
|
|
514
530
|
handleConfirmationCancel();
|
|
@@ -519,23 +535,25 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
519
535
|
messages,
|
|
520
536
|
isLoading,
|
|
521
537
|
isCommandRunning,
|
|
522
|
-
userInputHistory,
|
|
523
538
|
isExpanded,
|
|
539
|
+
isTaskListVisible,
|
|
540
|
+
setIsTaskListVisible,
|
|
524
541
|
sessionId,
|
|
525
542
|
sendMessage,
|
|
526
543
|
abortMessage,
|
|
527
544
|
latestTotalTokens,
|
|
528
545
|
isCompressing,
|
|
529
|
-
saveMemory,
|
|
530
546
|
mcpServers,
|
|
531
547
|
connectMcpServer,
|
|
532
548
|
disconnectMcpServer,
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
549
|
+
backgroundTasks,
|
|
550
|
+
sessionTasks,
|
|
551
|
+
getBackgroundTaskOutput,
|
|
552
|
+
stopBackgroundTask,
|
|
536
553
|
slashCommands,
|
|
537
554
|
hasSlashCommand,
|
|
538
555
|
subagentMessages,
|
|
556
|
+
subagentLatestTokens,
|
|
539
557
|
permissionMode,
|
|
540
558
|
setPermissionMode,
|
|
541
559
|
isConfirmationVisible,
|
|
@@ -544,8 +562,12 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
544
562
|
hideConfirmation,
|
|
545
563
|
handleConfirmationDecision,
|
|
546
564
|
handleConfirmationCancel,
|
|
565
|
+
backgroundCurrentTask,
|
|
547
566
|
rewindId,
|
|
548
567
|
handleRewindSelect,
|
|
568
|
+
getFullMessageThread,
|
|
569
|
+
wasLastDetailsTooTall,
|
|
570
|
+
setWasLastDetailsTooTall,
|
|
549
571
|
};
|
|
550
572
|
|
|
551
573
|
return (
|