wave-code 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/BackgroundTaskManager.d.ts +6 -0
- package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
- package/dist/components/{TaskManager.js → BackgroundTaskManager.js} +1 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +55 -5
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +10 -2
- 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} +34 -96
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +44 -1
- 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 -2
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +5 -9
- 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 -121
- 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/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +9 -6
- 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.js +1 -1
- package/dist/contexts/useChat.d.ts +5 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +25 -25
- package/dist/hooks/useInputManager.d.ts +2 -7
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +8 -40
- 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 +4 -19
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +22 -65
- package/package.json +5 -6
- package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
- package/src/components/ChatInterface.tsx +100 -20
- package/src/components/CommandSelector.tsx +35 -17
- package/src/components/CompressDisplay.tsx +5 -22
- package/src/components/ConfirmationDetails.tsx +108 -0
- package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +69 -184
- package/src/components/DiffDisplay.tsx +62 -1
- package/src/components/FileSelector.tsx +0 -2
- package/src/components/HistorySearch.tsx +45 -21
- package/src/components/InputBox.tsx +9 -24
- package/src/components/LoadingIndicator.tsx +56 -0
- package/src/components/Markdown.tsx +126 -323
- package/src/components/MessageItem.tsx +1 -3
- package/src/components/MessageList.tsx +10 -67
- package/src/components/PlanDisplay.tsx +4 -27
- package/src/components/SubagentBlock.tsx +25 -16
- package/src/components/TaskList.tsx +70 -0
- package/src/components/ToolResultDisplay.tsx +2 -2
- package/src/contexts/useChat.tsx +38 -33
- package/src/hooks/useInputManager.ts +9 -47
- package/src/hooks/useTasks.ts +6 -0
- package/src/managers/InputManager.ts +25 -83
- 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/dist/components/TaskManager.d.ts +0 -6
- package/dist/components/TaskManager.d.ts.map +0 -1
- package/src/components/MemoryDisplay.tsx +0 -62
- package/src/components/MemoryTypeSelector.tsx +0 -98
|
@@ -7,9 +7,8 @@ export interface MessageListProps {
|
|
|
7
7
|
messages: Message[];
|
|
8
8
|
isLoading?: boolean;
|
|
9
9
|
isCommandRunning?: boolean;
|
|
10
|
-
isCompressing?: boolean;
|
|
11
|
-
latestTotalTokens?: number;
|
|
12
10
|
isExpanded?: boolean;
|
|
11
|
+
forceStaticLastMessage?: boolean;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export const MessageList = React.memo(
|
|
@@ -17,15 +16,16 @@ export const MessageList = React.memo(
|
|
|
17
16
|
messages,
|
|
18
17
|
isLoading = false,
|
|
19
18
|
isCommandRunning = false,
|
|
20
|
-
isCompressing = false,
|
|
21
|
-
latestTotalTokens = 0,
|
|
22
19
|
isExpanded = false,
|
|
20
|
+
forceStaticLastMessage = false,
|
|
23
21
|
}: MessageListProps) => {
|
|
24
22
|
// Empty message state
|
|
25
23
|
if (messages.length === 0) {
|
|
26
24
|
return (
|
|
27
|
-
<Box flexDirection="column"
|
|
28
|
-
<
|
|
25
|
+
<Box flexDirection="column" gap={1}>
|
|
26
|
+
<Box flexDirection="column" paddingY={1}>
|
|
27
|
+
<Text color="gray">Welcome to WAVE Code Assistant!</Text>
|
|
28
|
+
</Box>
|
|
29
29
|
</Box>
|
|
30
30
|
);
|
|
31
31
|
}
|
|
@@ -42,7 +42,8 @@ export const MessageList = React.memo(
|
|
|
42
42
|
: 0;
|
|
43
43
|
|
|
44
44
|
// Compute which messages to render statically vs dynamically
|
|
45
|
-
const shouldRenderLastDynamic =
|
|
45
|
+
const shouldRenderLastDynamic =
|
|
46
|
+
!forceStaticLastMessage && (isLoading || isCommandRunning);
|
|
46
47
|
const staticMessages = shouldRenderLastDynamic
|
|
47
48
|
? displayMessages.slice(0, -1)
|
|
48
49
|
: displayMessages;
|
|
@@ -52,7 +53,7 @@ export const MessageList = React.memo(
|
|
|
52
53
|
: [];
|
|
53
54
|
|
|
54
55
|
return (
|
|
55
|
-
<Box flexDirection="column" gap={1}>
|
|
56
|
+
<Box flexDirection="column" gap={1} paddingBottom={1}>
|
|
56
57
|
{/* Show omitted message count when limiting */}
|
|
57
58
|
{omittedCount > 0 && (
|
|
58
59
|
<Box>
|
|
@@ -86,7 +87,7 @@ export const MessageList = React.memo(
|
|
|
86
87
|
const previousMessage =
|
|
87
88
|
messageIndex > 0 ? displayMessages[messageIndex - 1] : undefined;
|
|
88
89
|
return (
|
|
89
|
-
<Box key={`dynamic-${index}`}
|
|
90
|
+
<Box key={`dynamic-${index}`}>
|
|
90
91
|
<MessageItem
|
|
91
92
|
message={message}
|
|
92
93
|
shouldShowHeader={previousMessage?.role !== message.role}
|
|
@@ -95,64 +96,6 @@ export const MessageList = React.memo(
|
|
|
95
96
|
</Box>
|
|
96
97
|
);
|
|
97
98
|
})}
|
|
98
|
-
|
|
99
|
-
{(isLoading || isCommandRunning || isCompressing) && (
|
|
100
|
-
<Box flexDirection="column" gap={1}>
|
|
101
|
-
{isLoading && (
|
|
102
|
-
<Box>
|
|
103
|
-
<Text color="yellow">💭 AI is thinking... </Text>
|
|
104
|
-
<Text color="gray" dimColor>
|
|
105
|
-
|{" "}
|
|
106
|
-
</Text>
|
|
107
|
-
<Text color="red" bold>
|
|
108
|
-
Esc
|
|
109
|
-
</Text>
|
|
110
|
-
<Text color="gray" dimColor>
|
|
111
|
-
{" "}
|
|
112
|
-
to abort
|
|
113
|
-
</Text>
|
|
114
|
-
</Box>
|
|
115
|
-
)}
|
|
116
|
-
{isCommandRunning && (
|
|
117
|
-
<Text color="blue">🚀 Command is running...</Text>
|
|
118
|
-
)}
|
|
119
|
-
{isCompressing && (
|
|
120
|
-
<Text color="magenta">🗜️ Compressing message history...</Text>
|
|
121
|
-
)}
|
|
122
|
-
</Box>
|
|
123
|
-
)}
|
|
124
|
-
|
|
125
|
-
{/* Bottom info and shortcut key hints */}
|
|
126
|
-
{messages.length > 0 && (
|
|
127
|
-
<Box>
|
|
128
|
-
<Box justifyContent="space-between" width="100%">
|
|
129
|
-
<Box>
|
|
130
|
-
<Text color="gray">
|
|
131
|
-
Messages {messages.length}
|
|
132
|
-
{latestTotalTokens > 0 && (
|
|
133
|
-
<>
|
|
134
|
-
<Text color="gray" dimColor>
|
|
135
|
-
{" "}
|
|
136
|
-
|{" "}
|
|
137
|
-
</Text>
|
|
138
|
-
<Text color="blue" bold>
|
|
139
|
-
{latestTotalTokens.toLocaleString()}
|
|
140
|
-
</Text>
|
|
141
|
-
<Text color="gray" dimColor>
|
|
142
|
-
{" "}
|
|
143
|
-
tokens
|
|
144
|
-
</Text>
|
|
145
|
-
</>
|
|
146
|
-
)}
|
|
147
|
-
</Text>
|
|
148
|
-
</Box>
|
|
149
|
-
<Text color="gray" dimColor>
|
|
150
|
-
<Text color="cyan">Ctrl+O</Text> Toggle{" "}
|
|
151
|
-
{isExpanded ? "Collapse" : "Expand"}
|
|
152
|
-
</Text>
|
|
153
|
-
</Box>
|
|
154
|
-
</Box>
|
|
155
|
-
)}
|
|
156
99
|
</Box>
|
|
157
100
|
);
|
|
158
101
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { Box
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box } from "ink";
|
|
3
3
|
import { Markdown } from "./Markdown.js";
|
|
4
4
|
|
|
5
5
|
interface PlanDisplayProps {
|
|
@@ -7,35 +7,12 @@ interface PlanDisplayProps {
|
|
|
7
7
|
isExpanded?: boolean;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const PlanDisplay: React.FC<PlanDisplayProps> = ({
|
|
11
|
-
plan,
|
|
12
|
-
isExpanded = false,
|
|
13
|
-
}) => {
|
|
14
|
-
const { stdout } = useStdout();
|
|
15
|
-
const maxHeight = useMemo(() => {
|
|
16
|
-
// Similar to DiffDisplay.tsx maxHeight calculation
|
|
17
|
-
return Math.max(5, (stdout?.rows || 24) - 25);
|
|
18
|
-
}, [stdout?.rows]);
|
|
19
|
-
|
|
20
|
-
const lines = useMemo(() => plan.split("\n"), [plan]);
|
|
21
|
-
const isOverflowing = !isExpanded && lines.length > maxHeight;
|
|
22
|
-
|
|
10
|
+
export const PlanDisplay: React.FC<PlanDisplayProps> = ({ plan }) => {
|
|
23
11
|
return (
|
|
24
12
|
<Box flexDirection="column" marginTop={1}>
|
|
25
|
-
<Box
|
|
26
|
-
flexDirection="column"
|
|
27
|
-
height={isOverflowing ? maxHeight : undefined}
|
|
28
|
-
overflow="hidden"
|
|
29
|
-
>
|
|
13
|
+
<Box flexDirection="column">
|
|
30
14
|
<Markdown>{plan}</Markdown>
|
|
31
15
|
</Box>
|
|
32
|
-
{isOverflowing && (
|
|
33
|
-
<Box marginTop={1}>
|
|
34
|
-
<Text color="yellow" dimColor>
|
|
35
|
-
... (plan truncated, {lines.length} lines total)
|
|
36
|
-
</Text>
|
|
37
|
-
</Box>
|
|
38
|
-
)}
|
|
39
16
|
</Box>
|
|
40
17
|
);
|
|
41
18
|
};
|
|
@@ -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,16 +9,22 @@ 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
|
+
|
|
14
|
+
// Get messages for this subagent from context
|
|
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;
|
|
13
22
|
|
|
14
23
|
// If the subagent is running in the background, don't show the block
|
|
15
24
|
if (block.runInBackground) {
|
|
16
25
|
return null;
|
|
17
26
|
}
|
|
18
27
|
|
|
19
|
-
// Get messages for this subagent from context
|
|
20
|
-
const messages = subagentMessages[block.subagentId] || [];
|
|
21
|
-
|
|
22
28
|
// Status indicator mapping
|
|
23
29
|
const getStatusIndicator = (status: SubagentBlockType["status"]) => {
|
|
24
30
|
switch (status) {
|
|
@@ -63,7 +69,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
63
69
|
return { tools: tools.reverse(), totalToolCount }; // Reverse to show oldest first, newest last
|
|
64
70
|
};
|
|
65
71
|
|
|
66
|
-
const { tools: lastTwoTools
|
|
72
|
+
const { tools: lastTwoTools } = getLastTwoTools();
|
|
67
73
|
|
|
68
74
|
// Get the last text message content if completed
|
|
69
75
|
const getLastTextMessage = () => {
|
|
@@ -93,9 +99,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
93
99
|
borderStyle="classic"
|
|
94
100
|
borderColor="magenta"
|
|
95
101
|
paddingX={1}
|
|
96
|
-
paddingY={0}
|
|
97
102
|
flexDirection="column"
|
|
98
|
-
marginBottom={1}
|
|
99
103
|
>
|
|
100
104
|
{/* Header Section */}
|
|
101
105
|
<Box flexDirection="row" gap={1}>
|
|
@@ -107,26 +111,31 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
|
|
|
107
111
|
</Text>
|
|
108
112
|
<Text color="gray" dimColor>
|
|
109
113
|
{" "}
|
|
110
|
-
({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
|
+
)
|
|
111
125
|
</Text>
|
|
112
126
|
</Box>
|
|
113
127
|
</Box>
|
|
114
128
|
|
|
115
129
|
{/* Last Text Message Section */}
|
|
116
130
|
{lastTextMessage && (
|
|
117
|
-
<Box
|
|
131
|
+
<Box>
|
|
118
132
|
<Markdown>{lastTextMessage}</Markdown>
|
|
119
133
|
</Box>
|
|
120
134
|
)}
|
|
121
135
|
|
|
122
136
|
{/* Tool Names Section - Vertical List */}
|
|
123
137
|
{block.status !== "completed" && lastTwoTools.length > 0 && (
|
|
124
|
-
<Box flexDirection="column"
|
|
125
|
-
{totalToolCount > 2 && (
|
|
126
|
-
<Text color="gray" dimColor>
|
|
127
|
-
...
|
|
128
|
-
</Text>
|
|
129
|
-
)}
|
|
138
|
+
<Box flexDirection="column">
|
|
130
139
|
{lastTwoTools.map((tool, index) => (
|
|
131
140
|
<Box key={index} flexDirection="row">
|
|
132
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
|
+
};
|
|
@@ -68,7 +68,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
68
68
|
const shortResult = getShortResult();
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
|
-
<Box flexDirection="column"
|
|
71
|
+
<Box flexDirection="column">
|
|
72
72
|
<Box>
|
|
73
73
|
<Text color="magenta">🔧 </Text>
|
|
74
74
|
<Text color="white">{toolName}</Text>
|
|
@@ -91,7 +91,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
91
91
|
flexDirection="column"
|
|
92
92
|
>
|
|
93
93
|
{shortResult.split("\n").map((line, index) => (
|
|
94
|
-
<Text key={index} color="
|
|
94
|
+
<Text key={index} color="gray">
|
|
95
95
|
{line}
|
|
96
96
|
</Text>
|
|
97
97
|
))}
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
Message,
|
|
13
13
|
McpServerStatus,
|
|
14
14
|
BackgroundTask,
|
|
15
|
+
Task,
|
|
15
16
|
SlashCommand,
|
|
16
17
|
PermissionDecision,
|
|
17
18
|
PermissionMode,
|
|
@@ -33,6 +34,8 @@ export interface ChatContextType {
|
|
|
33
34
|
userInputHistory: string[];
|
|
34
35
|
// Message display state
|
|
35
36
|
isExpanded: boolean;
|
|
37
|
+
isTaskListVisible: boolean;
|
|
38
|
+
setIsTaskListVisible: (visible: boolean) => void;
|
|
36
39
|
// AI functionality
|
|
37
40
|
sessionId: string;
|
|
38
41
|
sendMessage: (
|
|
@@ -41,14 +44,14 @@ export interface ChatContextType {
|
|
|
41
44
|
) => Promise<void>;
|
|
42
45
|
abortMessage: () => void;
|
|
43
46
|
latestTotalTokens: number;
|
|
44
|
-
// Memory functionality
|
|
45
|
-
saveMemory: (message: string, type: "project" | "user") => Promise<void>;
|
|
46
47
|
// MCP functionality
|
|
47
48
|
mcpServers: McpServerStatus[];
|
|
48
49
|
connectMcpServer: (serverName: string) => Promise<boolean>;
|
|
49
50
|
disconnectMcpServer: (serverName: string) => Promise<boolean>;
|
|
50
51
|
// Background tasks
|
|
51
52
|
backgroundTasks: BackgroundTask[];
|
|
53
|
+
// Session tasks
|
|
54
|
+
sessionTasks: Task[];
|
|
52
55
|
getBackgroundTaskOutput: (
|
|
53
56
|
taskId: string,
|
|
54
57
|
) => { stdout: string; stderr: string; status: string } | null;
|
|
@@ -58,6 +61,7 @@ export interface ChatContextType {
|
|
|
58
61
|
hasSlashCommand: (commandId: string) => boolean;
|
|
59
62
|
// Subagent messages
|
|
60
63
|
subagentMessages: Record<string, Message[]>;
|
|
64
|
+
subagentLatestTokens: Record<string, number>;
|
|
61
65
|
// Permission functionality
|
|
62
66
|
permissionMode: PermissionMode;
|
|
63
67
|
setPermissionMode: (mode: PermissionMode) => void;
|
|
@@ -110,6 +114,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
110
114
|
|
|
111
115
|
// Message Display State
|
|
112
116
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
117
|
+
const [isTaskListVisible, setIsTaskListVisible] = useState(true);
|
|
113
118
|
|
|
114
119
|
// AI State
|
|
115
120
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
@@ -125,6 +130,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
125
130
|
|
|
126
131
|
// Background tasks state
|
|
127
132
|
const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
|
|
133
|
+
// Session tasks state
|
|
134
|
+
const [sessionTasks, setSessionTasks] = useState<Task[]>([]);
|
|
128
135
|
|
|
129
136
|
// Command state
|
|
130
137
|
const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
|
|
@@ -133,6 +140,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
133
140
|
const [subagentMessages, setSubagentMessages] = useState<
|
|
134
141
|
Record<string, Message[]>
|
|
135
142
|
>({});
|
|
143
|
+
const [subagentLatestTokens, setSubagentLatestTokens] = useState<
|
|
144
|
+
Record<string, number>
|
|
145
|
+
>({});
|
|
136
146
|
|
|
137
147
|
// Permission state
|
|
138
148
|
const [permissionMode, setPermissionModeState] =
|
|
@@ -209,9 +219,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
209
219
|
setMcpServers([...servers]);
|
|
210
220
|
},
|
|
211
221
|
onSessionIdChange: (sessionId) => {
|
|
212
|
-
|
|
213
|
-
setSessionId(sessionId);
|
|
214
|
-
});
|
|
222
|
+
setSessionId(sessionId);
|
|
215
223
|
},
|
|
216
224
|
onLatestTotalTokensChange: (tokens) => {
|
|
217
225
|
setlatestTotalTokens(tokens);
|
|
@@ -225,13 +233,25 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
225
233
|
onTasksChange: (tasks) => {
|
|
226
234
|
setBackgroundTasks([...tasks]);
|
|
227
235
|
},
|
|
228
|
-
|
|
236
|
+
onSessionTasksChange: (tasks) => {
|
|
237
|
+
setSessionTasks([...tasks]);
|
|
238
|
+
},
|
|
239
|
+
onSubagentMessagesChange: (subagentId: string, messages: Message[]) => {
|
|
229
240
|
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
230
241
|
setSubagentMessages((prev) => ({
|
|
231
242
|
...prev,
|
|
232
243
|
[subagentId]: [...messages],
|
|
233
244
|
}));
|
|
234
245
|
},
|
|
246
|
+
onSubagentLatestTotalTokensChange: (
|
|
247
|
+
subagentId: string,
|
|
248
|
+
tokens: number,
|
|
249
|
+
) => {
|
|
250
|
+
setSubagentLatestTokens((prev) => ({
|
|
251
|
+
...prev,
|
|
252
|
+
[subagentId]: tokens,
|
|
253
|
+
}));
|
|
254
|
+
},
|
|
235
255
|
onPermissionModeChange: (mode) => {
|
|
236
256
|
setPermissionModeState(mode);
|
|
237
257
|
},
|
|
@@ -338,16 +358,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
338
358
|
if (!hasTextContent && !hasImageAttachments) return;
|
|
339
359
|
|
|
340
360
|
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
361
|
// Handle bash mode - check if it's a bash command (starts with ! and only one line)
|
|
352
362
|
if (content.startsWith("!") && !content.includes("\n")) {
|
|
353
363
|
const command = content.substring(1).trim();
|
|
@@ -394,14 +404,6 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
394
404
|
agentRef.current?.abortMessage();
|
|
395
405
|
}, []);
|
|
396
406
|
|
|
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
407
|
// Permission management methods
|
|
406
408
|
const setPermissionMode = useCallback((mode: PermissionMode) => {
|
|
407
409
|
setPermissionModeState((prev) => {
|
|
@@ -492,9 +494,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
492
494
|
await agentRef.current.truncateHistory(index);
|
|
493
495
|
|
|
494
496
|
// Clear terminal screen after rewind
|
|
495
|
-
|
|
496
|
-
setRewindId((prev) => prev + 1);
|
|
497
|
-
});
|
|
497
|
+
setRewindId((prev) => prev + 1);
|
|
498
498
|
} catch (error) {
|
|
499
499
|
logger.error("Failed to rewind:", error);
|
|
500
500
|
}
|
|
@@ -505,14 +505,16 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
505
505
|
useInput((input, key) => {
|
|
506
506
|
if (key.ctrl && input === "o") {
|
|
507
507
|
// Clear terminal screen when expanded state changes
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
return newExpanded;
|
|
512
|
-
});
|
|
508
|
+
setIsExpanded((prev) => {
|
|
509
|
+
const newExpanded = !prev;
|
|
510
|
+
return newExpanded;
|
|
513
511
|
});
|
|
514
512
|
}
|
|
515
513
|
|
|
514
|
+
if (key.ctrl && input === "t") {
|
|
515
|
+
setIsTaskListVisible((prev) => !prev);
|
|
516
|
+
}
|
|
517
|
+
|
|
516
518
|
// Handle ESC key to cancel confirmation
|
|
517
519
|
if (key.escape && isConfirmationVisible) {
|
|
518
520
|
handleConfirmationCancel();
|
|
@@ -525,21 +527,24 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
525
527
|
isCommandRunning,
|
|
526
528
|
userInputHistory,
|
|
527
529
|
isExpanded,
|
|
530
|
+
isTaskListVisible,
|
|
531
|
+
setIsTaskListVisible,
|
|
528
532
|
sessionId,
|
|
529
533
|
sendMessage,
|
|
530
534
|
abortMessage,
|
|
531
535
|
latestTotalTokens,
|
|
532
536
|
isCompressing,
|
|
533
|
-
saveMemory,
|
|
534
537
|
mcpServers,
|
|
535
538
|
connectMcpServer,
|
|
536
539
|
disconnectMcpServer,
|
|
537
540
|
backgroundTasks,
|
|
541
|
+
sessionTasks,
|
|
538
542
|
getBackgroundTaskOutput,
|
|
539
543
|
stopBackgroundTask,
|
|
540
544
|
slashCommands,
|
|
541
545
|
hasSlashCommand,
|
|
542
546
|
subagentMessages,
|
|
547
|
+
subagentLatestTokens,
|
|
543
548
|
permissionMode,
|
|
544
549
|
setPermissionMode,
|
|
545
550
|
isConfirmationVisible,
|
|
@@ -33,11 +33,8 @@ export const useInputManager = (
|
|
|
33
33
|
show: false,
|
|
34
34
|
query: "",
|
|
35
35
|
});
|
|
36
|
-
const [
|
|
37
|
-
|
|
38
|
-
message: "",
|
|
39
|
-
});
|
|
40
|
-
const [showTaskManager, setShowTaskManager] = useState(false);
|
|
36
|
+
const [showBackgroundTaskManager, setShowBackgroundTaskManager] =
|
|
37
|
+
useState(false);
|
|
41
38
|
const [showMcpManager, setShowMcpManager] = useState(false);
|
|
42
39
|
const [showRewindManager, setShowRewindManager] = useState(false);
|
|
43
40
|
const [permissionMode, setPermissionModeState] =
|
|
@@ -61,11 +58,8 @@ export const useInputManager = (
|
|
|
61
58
|
onHistorySearchStateChange: (show, query) => {
|
|
62
59
|
setHistorySearchState({ show, query });
|
|
63
60
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
onTaskManagerStateChange: (show) => {
|
|
68
|
-
setShowTaskManager(show);
|
|
61
|
+
onBackgroundTaskManagerStateChange: (show) => {
|
|
62
|
+
setShowBackgroundTaskManager(show);
|
|
69
63
|
},
|
|
70
64
|
onMcpManagerStateChange: (show) => {
|
|
71
65
|
setShowMcpManager(show);
|
|
@@ -78,9 +72,6 @@ export const useInputManager = (
|
|
|
78
72
|
callbacks.onPermissionModeChange?.(mode);
|
|
79
73
|
},
|
|
80
74
|
onImagesStateChange: setAttachedImages,
|
|
81
|
-
onShowTaskManager: () => setShowTaskManager(true),
|
|
82
|
-
onShowMcpManager: () => setShowMcpManager(true),
|
|
83
|
-
onShowRewindManager: () => setShowRewindManager(true),
|
|
84
75
|
...callbacks,
|
|
85
76
|
});
|
|
86
77
|
|
|
@@ -101,11 +92,8 @@ export const useInputManager = (
|
|
|
101
92
|
onHistorySearchStateChange: (show, query) => {
|
|
102
93
|
setHistorySearchState({ show, query });
|
|
103
94
|
},
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
onTaskManagerStateChange: (show) => {
|
|
108
|
-
setShowTaskManager(show);
|
|
95
|
+
onBackgroundTaskManagerStateChange: (show) => {
|
|
96
|
+
setShowBackgroundTaskManager(show);
|
|
109
97
|
},
|
|
110
98
|
onMcpManagerStateChange: (show) => {
|
|
111
99
|
setShowMcpManager(show);
|
|
@@ -118,9 +106,6 @@ export const useInputManager = (
|
|
|
118
106
|
callbacks.onPermissionModeChange?.(mode);
|
|
119
107
|
},
|
|
120
108
|
onImagesStateChange: setAttachedImages,
|
|
121
|
-
onShowTaskManager: () => setShowTaskManager(true),
|
|
122
|
-
onShowMcpManager: () => setShowMcpManager(true),
|
|
123
|
-
onShowRewindManager: () => setShowRewindManager(true),
|
|
124
109
|
...callbacks,
|
|
125
110
|
});
|
|
126
111
|
}
|
|
@@ -252,22 +237,6 @@ export const useInputManager = (
|
|
|
252
237
|
managerRef.current?.handleCancelHistorySearch();
|
|
253
238
|
}, []);
|
|
254
239
|
|
|
255
|
-
// Memory type selector methods
|
|
256
|
-
const activateMemoryTypeSelector = useCallback((message: string) => {
|
|
257
|
-
managerRef.current?.activateMemoryTypeSelector(message);
|
|
258
|
-
}, []);
|
|
259
|
-
|
|
260
|
-
const handleMemoryTypeSelect = useCallback(
|
|
261
|
-
async (type: "project" | "user") => {
|
|
262
|
-
await managerRef.current?.handleMemoryTypeSelect(type);
|
|
263
|
-
},
|
|
264
|
-
[],
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
const handleCancelMemoryTypeSelect = useCallback(() => {
|
|
268
|
-
managerRef.current?.handleCancelMemoryTypeSelect();
|
|
269
|
-
}, []);
|
|
270
|
-
|
|
271
240
|
// Input history methods
|
|
272
241
|
const setUserInputHistory = useCallback((history: string[]) => {
|
|
273
242
|
managerRef.current?.setUserInputHistory(history);
|
|
@@ -317,9 +286,7 @@ export const useInputManager = (
|
|
|
317
286
|
slashPosition: commandSelectorState.position,
|
|
318
287
|
showHistorySearch: historySearchState.show,
|
|
319
288
|
historySearchQuery: historySearchState.query,
|
|
320
|
-
|
|
321
|
-
memoryMessage: memoryTypeSelectorState.message,
|
|
322
|
-
showTaskManager,
|
|
289
|
+
showBackgroundTaskManager,
|
|
323
290
|
showMcpManager,
|
|
324
291
|
showRewindManager,
|
|
325
292
|
permissionMode,
|
|
@@ -354,11 +321,6 @@ export const useInputManager = (
|
|
|
354
321
|
handleHistorySearchSelect,
|
|
355
322
|
handleCancelHistorySearch,
|
|
356
323
|
|
|
357
|
-
// Memory type selector
|
|
358
|
-
activateMemoryTypeSelector,
|
|
359
|
-
handleMemoryTypeSelect,
|
|
360
|
-
handleCancelMemoryTypeSelect,
|
|
361
|
-
|
|
362
324
|
// Input history
|
|
363
325
|
setUserInputHistory,
|
|
364
326
|
navigateHistory,
|
|
@@ -368,8 +330,8 @@ export const useInputManager = (
|
|
|
368
330
|
handleSpecialCharInput,
|
|
369
331
|
|
|
370
332
|
// Bash/MCP Manager
|
|
371
|
-
|
|
372
|
-
managerRef.current?.
|
|
333
|
+
setShowBackgroundTaskManager: useCallback((show: boolean) => {
|
|
334
|
+
managerRef.current?.setShowBackgroundTaskManager(show);
|
|
373
335
|
}, []),
|
|
374
336
|
setShowMcpManager: useCallback((show: boolean) => {
|
|
375
337
|
managerRef.current?.setShowMcpManager(show);
|