wave-code 0.14.0 → 0.14.2
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.map +1 -1
- package/dist/components/BackgroundTaskManager.js +36 -25
- package/dist/components/ChatInterface.js +2 -2
- package/dist/components/CompactDisplay.d.ts +9 -0
- package/dist/components/CompactDisplay.d.ts.map +1 -0
- package/dist/components/{CompressDisplay.js → CompactDisplay.js} +2 -2
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +2 -2
- package/dist/components/LoadingIndicator.d.ts +2 -2
- package/dist/components/LoadingIndicator.d.ts.map +1 -1
- package/dist/components/LoadingIndicator.js +2 -2
- package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
- package/dist/components/MarketplaceAddForm.js +48 -17
- package/dist/components/MarketplaceDetail.d.ts.map +1 -1
- package/dist/components/MarketplaceDetail.js +2 -1
- package/dist/components/MarketplaceList.d.ts.map +1 -1
- package/dist/components/MarketplaceList.js +2 -1
- package/dist/components/McpManager.d.ts.map +1 -1
- package/dist/components/McpManager.js +22 -27
- package/dist/components/MessageBlockItem.js +2 -2
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +22 -22
- package/dist/components/PluginManagerShell.d.ts +1 -0
- package/dist/components/PluginManagerShell.d.ts.map +1 -1
- package/dist/components/PluginManagerShell.js +2 -2
- package/dist/components/PluginManagerTypes.d.ts +2 -2
- package/dist/components/PluginManagerTypes.d.ts.map +1 -1
- package/dist/contexts/useChat.d.ts +2 -1
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +127 -100
- package/dist/hooks/usePluginManager.d.ts +3 -1
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +16 -7
- package/dist/reducers/backgroundTaskManagerReducer.d.ts +43 -0
- package/dist/reducers/backgroundTaskManagerReducer.d.ts.map +1 -0
- package/dist/reducers/backgroundTaskManagerReducer.js +23 -0
- package/dist/reducers/marketplaceAddFormReducer.d.ts +24 -0
- package/dist/reducers/marketplaceAddFormReducer.d.ts.map +1 -0
- package/dist/reducers/marketplaceAddFormReducer.js +18 -0
- package/dist/reducers/mcpManagerReducer.d.ts +16 -0
- package/dist/reducers/mcpManagerReducer.d.ts.map +1 -0
- package/dist/reducers/mcpManagerReducer.js +15 -0
- package/dist/reducers/pluginDetailReducer.d.ts +25 -0
- package/dist/reducers/pluginDetailReducer.d.ts.map +1 -0
- package/dist/reducers/pluginDetailReducer.js +38 -0
- package/dist/utils/usageSummary.d.ts +1 -1
- package/dist/utils/usageSummary.d.ts.map +1 -1
- package/dist/utils/usageSummary.js +7 -7
- package/package.json +2 -2
- package/src/components/BackgroundTaskManager.tsx +32 -34
- package/src/components/ChatInterface.tsx +3 -3
- package/src/components/{CompressDisplay.tsx → CompactDisplay.tsx} +5 -5
- package/src/components/InputBox.tsx +7 -1
- package/src/components/LoadingIndicator.tsx +5 -5
- package/src/components/MarketplaceAddForm.tsx +76 -22
- package/src/components/MarketplaceDetail.tsx +4 -0
- package/src/components/MarketplaceList.tsx +4 -0
- package/src/components/McpManager.tsx +30 -34
- package/src/components/MessageBlockItem.tsx +3 -3
- package/src/components/PluginDetail.tsx +25 -33
- package/src/components/PluginManagerShell.tsx +3 -2
- package/src/components/PluginManagerTypes.ts +8 -2
- package/src/contexts/useChat.tsx +60 -26
- package/src/hooks/usePluginManager.ts +18 -7
- package/src/reducers/backgroundTaskManagerReducer.ts +60 -0
- package/src/reducers/marketplaceAddFormReducer.ts +35 -0
- package/src/reducers/mcpManagerReducer.ts +31 -0
- package/src/reducers/pluginDetailReducer.ts +58 -0
- package/src/utils/usageSummary.ts +8 -8
- package/dist/components/CompressDisplay.d.ts +0 -9
- package/dist/components/CompressDisplay.d.ts.map +0 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useReducer } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { McpServerStatus } from "wave-agent-sdk";
|
|
4
|
+
import {
|
|
5
|
+
mcpManagerReducer,
|
|
6
|
+
type McpManagerState,
|
|
7
|
+
} from "../reducers/mcpManagerReducer.js";
|
|
4
8
|
|
|
5
9
|
export interface McpManagerProps {
|
|
6
10
|
onCancel: () => void;
|
|
@@ -9,33 +13,25 @@ export interface McpManagerProps {
|
|
|
9
13
|
onDisconnectServer: (serverName: string) => Promise<boolean>;
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
const initialState: McpManagerState = {
|
|
17
|
+
selectedIndex: 0,
|
|
18
|
+
viewMode: "list",
|
|
19
|
+
};
|
|
20
|
+
|
|
12
21
|
export const McpManager: React.FC<McpManagerProps> = ({
|
|
13
22
|
onCancel,
|
|
14
23
|
servers,
|
|
15
24
|
onConnectServer,
|
|
16
25
|
onDisconnectServer,
|
|
17
26
|
}) => {
|
|
18
|
-
const [
|
|
19
|
-
const [viewMode, setViewMode] = useState<"list" | "detail">("list");
|
|
20
|
-
|
|
21
|
-
// Keep ref in sync with state to avoid stale closures in useInput
|
|
22
|
-
const selectedIndexRef = useRef(selectedIndex);
|
|
23
|
-
const viewModeRef = useRef(viewMode);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
selectedIndexRef.current = selectedIndex;
|
|
27
|
-
}, [selectedIndex]);
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
viewModeRef.current = viewMode;
|
|
31
|
-
}, [viewMode]);
|
|
27
|
+
const [state, dispatch] = useReducer(mcpManagerReducer, initialState);
|
|
32
28
|
|
|
33
29
|
// Dynamically calculate selectedServer based on selectedIndex and servers
|
|
34
30
|
const selectedServer =
|
|
35
|
-
viewMode === "detail" &&
|
|
31
|
+
state.viewMode === "detail" &&
|
|
36
32
|
servers.length > 0 &&
|
|
37
|
-
selectedIndex < servers.length
|
|
38
|
-
? servers[selectedIndex]
|
|
33
|
+
state.selectedIndex < servers.length
|
|
34
|
+
? servers[state.selectedIndex]
|
|
39
35
|
: null;
|
|
40
36
|
|
|
41
37
|
const formatTime = (timestamp: number): string => {
|
|
@@ -78,15 +74,15 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
78
74
|
|
|
79
75
|
useInput((input, key) => {
|
|
80
76
|
if (key.return) {
|
|
81
|
-
if (
|
|
82
|
-
|
|
77
|
+
if (state.viewMode === "list") {
|
|
78
|
+
dispatch({ type: "SET_VIEW_MODE", viewMode: "detail" });
|
|
83
79
|
}
|
|
84
80
|
return;
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
if (key.escape) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
84
|
+
if (state.viewMode === "detail") {
|
|
85
|
+
dispatch({ type: "SET_VIEW_MODE", viewMode: "list" });
|
|
90
86
|
} else {
|
|
91
87
|
onCancel();
|
|
92
88
|
}
|
|
@@ -94,18 +90,18 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
if (key.upArrow) {
|
|
97
|
-
|
|
93
|
+
dispatch({ type: "MOVE_UP", serverCount: servers.length });
|
|
98
94
|
return;
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
if (key.downArrow) {
|
|
102
|
-
|
|
98
|
+
dispatch({ type: "MOVE_DOWN", serverCount: servers.length });
|
|
103
99
|
return;
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
// Hotkeys for server actions
|
|
107
103
|
if (input === "c") {
|
|
108
|
-
const server = servers[
|
|
104
|
+
const server = servers[state.selectedIndex];
|
|
109
105
|
if (
|
|
110
106
|
server &&
|
|
111
107
|
(server.status === "disconnected" || server.status === "error")
|
|
@@ -116,7 +112,7 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
116
112
|
}
|
|
117
113
|
|
|
118
114
|
if (input === "d") {
|
|
119
|
-
const server = servers[
|
|
115
|
+
const server = servers[state.selectedIndex];
|
|
120
116
|
if (server && server.status === "connected") {
|
|
121
117
|
handleDisconnect(server.name);
|
|
122
118
|
}
|
|
@@ -124,7 +120,7 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
124
120
|
}
|
|
125
121
|
});
|
|
126
122
|
|
|
127
|
-
if (viewMode === "detail" && selectedServer) {
|
|
123
|
+
if (state.viewMode === "detail" && selectedServer) {
|
|
128
124
|
return (
|
|
129
125
|
<Box
|
|
130
126
|
flexDirection="column"
|
|
@@ -273,10 +269,10 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
273
269
|
{servers.map((server, index) => (
|
|
274
270
|
<Box key={server.name} flexDirection="column">
|
|
275
271
|
<Text
|
|
276
|
-
color={index === selectedIndex ? "black" : "white"}
|
|
277
|
-
backgroundColor={index === selectedIndex ? "cyan" : undefined}
|
|
272
|
+
color={index === state.selectedIndex ? "black" : "white"}
|
|
273
|
+
backgroundColor={index === state.selectedIndex ? "cyan" : undefined}
|
|
278
274
|
>
|
|
279
|
-
{index === selectedIndex ? "▶ " : " "}
|
|
275
|
+
{index === state.selectedIndex ? "▶ " : " "}
|
|
280
276
|
{index + 1}.{" "}
|
|
281
277
|
<Text color={getStatusColor(server.status)}>
|
|
282
278
|
{getStatusIcon(server.status)}
|
|
@@ -286,7 +282,7 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
286
282
|
<Text color="green"> · {server.toolCount} tools</Text>
|
|
287
283
|
)}
|
|
288
284
|
</Text>
|
|
289
|
-
{index === selectedIndex && (
|
|
285
|
+
{index === state.selectedIndex && (
|
|
290
286
|
<Box marginLeft={4} flexDirection="column">
|
|
291
287
|
<Text color="gray" dimColor>
|
|
292
288
|
{server.config.command}
|
|
@@ -305,11 +301,11 @@ export const McpManager: React.FC<McpManagerProps> = ({
|
|
|
305
301
|
<Box marginTop={1}>
|
|
306
302
|
<Text dimColor>
|
|
307
303
|
↑/↓ to select · Enter to view ·{" "}
|
|
308
|
-
{servers[selectedIndex]?.status === "disconnected" ||
|
|
309
|
-
servers[selectedIndex]?.status === "error"
|
|
304
|
+
{servers[state.selectedIndex]?.status === "disconnected" ||
|
|
305
|
+
servers[state.selectedIndex]?.status === "error"
|
|
310
306
|
? "c to connect · "
|
|
311
307
|
: ""}
|
|
312
|
-
{servers[selectedIndex]?.status === "connected"
|
|
308
|
+
{servers[state.selectedIndex]?.status === "connected"
|
|
313
309
|
? "d to disconnect · "
|
|
314
310
|
: ""}
|
|
315
311
|
Esc to close
|
|
@@ -4,7 +4,7 @@ import type { Message, MessageBlock } from "wave-agent-sdk";
|
|
|
4
4
|
import { MessageSource } from "wave-agent-sdk";
|
|
5
5
|
import { BangDisplay } from "./BangDisplay.js";
|
|
6
6
|
import { ToolDisplay } from "./ToolDisplay.js";
|
|
7
|
-
import {
|
|
7
|
+
import { CompactDisplay } from "./CompactDisplay.js";
|
|
8
8
|
import { ReasoningDisplay } from "./ReasoningDisplay.js";
|
|
9
9
|
import { Markdown } from "./Markdown.js";
|
|
10
10
|
import { TaskNotificationMessage } from "./TaskNotificationMessage.js";
|
|
@@ -79,8 +79,8 @@ export const MessageBlockItem = ({
|
|
|
79
79
|
</Box>
|
|
80
80
|
)}
|
|
81
81
|
|
|
82
|
-
{block.type === "
|
|
83
|
-
<
|
|
82
|
+
{block.type === "compact" && (
|
|
83
|
+
<CompactDisplay block={block} isExpanded={isExpanded} />
|
|
84
84
|
)}
|
|
85
85
|
|
|
86
86
|
{block.type === "reasoning" && (
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useReducer } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
+
import {
|
|
5
|
+
pluginDetailReducer,
|
|
6
|
+
type PluginDetailState,
|
|
7
|
+
} from "../reducers/pluginDetailReducer.js";
|
|
4
8
|
|
|
5
9
|
const SCOPES = [
|
|
6
10
|
{ id: "project", label: "Install for all collaborators (project scope)" },
|
|
@@ -11,20 +15,10 @@ const SCOPES = [
|
|
|
11
15
|
export const PluginDetail: React.FC = () => {
|
|
12
16
|
const { state, discoverablePlugins, installedPlugins, actions } =
|
|
13
17
|
usePluginManagerContext();
|
|
14
|
-
const [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const selectedScopeIndexRef = useRef(selectedScopeIndex);
|
|
19
|
-
const selectedActionIndexRef = useRef(selectedActionIndex);
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
selectedScopeIndexRef.current = selectedScopeIndex;
|
|
23
|
-
}, [selectedScopeIndex]);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
selectedActionIndexRef.current = selectedActionIndex;
|
|
27
|
-
}, [selectedActionIndex]);
|
|
18
|
+
const [detailState, dispatch] = useReducer(pluginDetailReducer, {
|
|
19
|
+
selectedScopeIndex: 0,
|
|
20
|
+
selectedActionIndex: 0,
|
|
21
|
+
} as PluginDetailState);
|
|
28
22
|
|
|
29
23
|
const plugin =
|
|
30
24
|
discoverablePlugins.find(
|
|
@@ -48,22 +42,20 @@ export const PluginDetail: React.FC = () => {
|
|
|
48
42
|
);
|
|
49
43
|
actions.setView(isFromDiscover ? "DISCOVER" : "INSTALLED");
|
|
50
44
|
} else if (key.upArrow) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
);
|
|
45
|
+
dispatch({
|
|
46
|
+
type: "MOVE_ACTION_UP",
|
|
47
|
+
maxIndex: INSTALLED_ACTIONS.length - 1,
|
|
48
|
+
});
|
|
49
|
+
dispatch({ type: "MOVE_SCOPE_UP", maxIndex: SCOPES.length - 1 });
|
|
57
50
|
} else if (key.downArrow) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
51
|
+
dispatch({
|
|
52
|
+
type: "MOVE_ACTION_DOWN",
|
|
53
|
+
maxIndex: INSTALLED_ACTIONS.length - 1,
|
|
54
|
+
});
|
|
55
|
+
dispatch({ type: "MOVE_SCOPE_DOWN", maxIndex: SCOPES.length - 1 });
|
|
64
56
|
} else if (key.return && plugin && !state.isLoading) {
|
|
65
57
|
if (isInstalledAndEnabled) {
|
|
66
|
-
const action = INSTALLED_ACTIONS[
|
|
58
|
+
const action = INSTALLED_ACTIONS[detailState.selectedActionIndex].id;
|
|
67
59
|
if (action === "uninstall") {
|
|
68
60
|
actions.uninstallPlugin(plugin.name, plugin.marketplace);
|
|
69
61
|
} else {
|
|
@@ -73,7 +65,7 @@ export const PluginDetail: React.FC = () => {
|
|
|
73
65
|
actions.installPlugin(
|
|
74
66
|
plugin.name,
|
|
75
67
|
plugin.marketplace,
|
|
76
|
-
SCOPES[
|
|
68
|
+
SCOPES[detailState.selectedScopeIndex].id,
|
|
77
69
|
);
|
|
78
70
|
}
|
|
79
71
|
}
|
|
@@ -121,14 +113,14 @@ export const PluginDetail: React.FC = () => {
|
|
|
121
113
|
<Text
|
|
122
114
|
key={action.id}
|
|
123
115
|
color={
|
|
124
|
-
index === selectedActionIndex
|
|
116
|
+
index === detailState.selectedActionIndex
|
|
125
117
|
? state.isLoading
|
|
126
118
|
? "gray"
|
|
127
119
|
: "yellow"
|
|
128
120
|
: undefined
|
|
129
121
|
}
|
|
130
122
|
>
|
|
131
|
-
{index === selectedActionIndex ? "> " : " "}
|
|
123
|
+
{index === detailState.selectedActionIndex ? "> " : " "}
|
|
132
124
|
{action.label}
|
|
133
125
|
</Text>
|
|
134
126
|
))}
|
|
@@ -147,14 +139,14 @@ export const PluginDetail: React.FC = () => {
|
|
|
147
139
|
<Text
|
|
148
140
|
key={scope.id}
|
|
149
141
|
color={
|
|
150
|
-
index === selectedScopeIndex
|
|
142
|
+
index === detailState.selectedScopeIndex
|
|
151
143
|
? state.isLoading
|
|
152
144
|
? "gray"
|
|
153
145
|
: "green"
|
|
154
146
|
: undefined
|
|
155
147
|
}
|
|
156
148
|
>
|
|
157
|
-
{index === selectedScopeIndex ? "> " : " "}
|
|
149
|
+
{index === detailState.selectedScopeIndex ? "> " : " "}
|
|
158
150
|
{scope.label}
|
|
159
151
|
</Text>
|
|
160
152
|
))}
|
|
@@ -13,8 +13,9 @@ import { PluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
|
13
13
|
export const PluginManagerShell: React.FC<{
|
|
14
14
|
children?: React.ReactNode;
|
|
15
15
|
onCancel?: () => void;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
onPluginInstalled?: () => void;
|
|
17
|
+
}> = ({ children, onCancel, onPluginInstalled }) => {
|
|
18
|
+
const pluginManager = usePluginManager({ onPluginInstalled });
|
|
18
19
|
const { state, actions, discoverablePlugins } = pluginManager;
|
|
19
20
|
|
|
20
21
|
const setView = (view: ViewType) => {
|
|
@@ -33,8 +33,14 @@ export interface PluginManagerContextType {
|
|
|
33
33
|
actions: {
|
|
34
34
|
setView: (view: ViewType) => void;
|
|
35
35
|
setSelectedId: (id: string | null) => void;
|
|
36
|
-
addMarketplace: (
|
|
37
|
-
|
|
36
|
+
addMarketplace: (
|
|
37
|
+
source: string,
|
|
38
|
+
scope?: "user" | "project" | "local",
|
|
39
|
+
) => Promise<void>;
|
|
40
|
+
removeMarketplace: (
|
|
41
|
+
name: string,
|
|
42
|
+
scope?: "user" | "project" | "local",
|
|
43
|
+
) => Promise<void>;
|
|
38
44
|
updateMarketplace: (name: string) => Promise<void>;
|
|
39
45
|
installPlugin: (
|
|
40
46
|
name: string,
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -37,7 +37,7 @@ export interface ChatContextType {
|
|
|
37
37
|
messages: Message[];
|
|
38
38
|
isLoading: boolean;
|
|
39
39
|
isCommandRunning: boolean;
|
|
40
|
-
|
|
40
|
+
isCompacting: boolean;
|
|
41
41
|
// Message display state
|
|
42
42
|
isExpanded: boolean;
|
|
43
43
|
isTaskListVisible: boolean;
|
|
@@ -117,6 +117,8 @@ export interface ChatContextType {
|
|
|
117
117
|
workingDirectory: string;
|
|
118
118
|
version?: string;
|
|
119
119
|
workdir?: string;
|
|
120
|
+
// Agent recreation (e.g. after plugin install)
|
|
121
|
+
recreateAgent: () => void;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -202,7 +204,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
202
204
|
const [latestTotalTokens, setlatestTotalTokens] = useState(0);
|
|
203
205
|
const [sessionId, setSessionId] = useState("");
|
|
204
206
|
const [isCommandRunning, setIsCommandRunning] = useState(false);
|
|
205
|
-
const [
|
|
207
|
+
const [isCompacting, setIsCompacting] = useState(false);
|
|
206
208
|
const [currentModel, setCurrentModelState] = useState("");
|
|
207
209
|
const [configuredModels, setConfiguredModels] = useState<string[]>([]);
|
|
208
210
|
const [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);
|
|
@@ -332,8 +334,11 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
332
334
|
);
|
|
333
335
|
|
|
334
336
|
// Initialize AI manager
|
|
335
|
-
|
|
336
|
-
|
|
337
|
+
const initializeAgent = useCallback(
|
|
338
|
+
async (restoreSessionIdOverride?: string) => {
|
|
339
|
+
const effectiveRestoreSessionId =
|
|
340
|
+
restoreSessionIdOverride ?? restoreSessionId;
|
|
341
|
+
|
|
337
342
|
const callbacks: AgentCallbacks = {
|
|
338
343
|
onMessagesChange: () => {
|
|
339
344
|
throttledSetMessages();
|
|
@@ -347,8 +352,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
347
352
|
onLatestTotalTokensChange: (tokens) => {
|
|
348
353
|
throttledSetTokens(tokens);
|
|
349
354
|
},
|
|
350
|
-
|
|
351
|
-
|
|
355
|
+
onCompactionStateChange: (isCompactingState) => {
|
|
356
|
+
setIsCompacting(isCompactingState);
|
|
352
357
|
},
|
|
353
358
|
onBackgroundTasksChange: (tasks) => {
|
|
354
359
|
setBackgroundTasks([...tasks]);
|
|
@@ -400,7 +405,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
400
405
|
|
|
401
406
|
const agent = await Agent.create({
|
|
402
407
|
callbacks,
|
|
403
|
-
restoreSessionId,
|
|
408
|
+
restoreSessionId: effectiveRestoreSessionId,
|
|
404
409
|
continueLastSession,
|
|
405
410
|
logger,
|
|
406
411
|
permissionMode:
|
|
@@ -426,7 +431,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
426
431
|
setIsLoading(agent.isLoading);
|
|
427
432
|
setlatestTotalTokens(agent.latestTotalTokens);
|
|
428
433
|
setIsCommandRunning(agent.isCommandRunning);
|
|
429
|
-
|
|
434
|
+
setIsCompacting(agent.isCompacting);
|
|
430
435
|
setPermissionModeState(agent.getPermissionMode());
|
|
431
436
|
setWorkingDirectory(agent.workingDirectory);
|
|
432
437
|
setCurrentModelState(agent.getModelConfig().model);
|
|
@@ -442,25 +447,53 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
442
447
|
} catch (error) {
|
|
443
448
|
console.error("Failed to initialize AI manager:", error);
|
|
444
449
|
}
|
|
445
|
-
}
|
|
450
|
+
},
|
|
451
|
+
[
|
|
452
|
+
restoreSessionId,
|
|
453
|
+
continueLastSession,
|
|
454
|
+
bypassPermissions,
|
|
455
|
+
showConfirmation,
|
|
456
|
+
pluginDirs,
|
|
457
|
+
tools,
|
|
458
|
+
allowedTools,
|
|
459
|
+
disallowedTools,
|
|
460
|
+
workdir,
|
|
461
|
+
worktreeSession,
|
|
462
|
+
model,
|
|
463
|
+
initialPermissionMode,
|
|
464
|
+
throttledSetMessages,
|
|
465
|
+
throttledSetTokens,
|
|
466
|
+
],
|
|
467
|
+
);
|
|
446
468
|
|
|
469
|
+
// Recreate agent (e.g. after plugin install) — destroys current agent and reinitializes
|
|
470
|
+
const recreateAgent = useCallback(() => {
|
|
471
|
+
const currentSessionId = agentRef.current?.sessionId;
|
|
472
|
+
if (agentRef.current) {
|
|
473
|
+
try {
|
|
474
|
+
agentRef.current.destroy();
|
|
475
|
+
} catch {
|
|
476
|
+
// Ignore destroy errors
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
agentRef.current = null;
|
|
480
|
+
setMessages([]);
|
|
481
|
+
setMcpServers([]);
|
|
482
|
+
setSlashCommands([]);
|
|
483
|
+
setSessionId("");
|
|
484
|
+
setIsLoading(false);
|
|
485
|
+
setlatestTotalTokens(0);
|
|
486
|
+
setIsCommandRunning(false);
|
|
487
|
+
setIsCompacting(false);
|
|
488
|
+
if (currentSessionId) {
|
|
489
|
+
initializeAgent(currentSessionId);
|
|
490
|
+
}
|
|
491
|
+
}, [initializeAgent]);
|
|
492
|
+
|
|
493
|
+
// Run initial agent creation
|
|
494
|
+
useEffect(() => {
|
|
447
495
|
initializeAgent();
|
|
448
|
-
}, [
|
|
449
|
-
restoreSessionId,
|
|
450
|
-
continueLastSession,
|
|
451
|
-
bypassPermissions,
|
|
452
|
-
showConfirmation,
|
|
453
|
-
pluginDirs,
|
|
454
|
-
tools,
|
|
455
|
-
allowedTools,
|
|
456
|
-
disallowedTools,
|
|
457
|
-
workdir,
|
|
458
|
-
worktreeSession,
|
|
459
|
-
model,
|
|
460
|
-
initialPermissionMode,
|
|
461
|
-
throttledSetMessages,
|
|
462
|
-
throttledSetTokens,
|
|
463
|
-
]);
|
|
496
|
+
}, [initializeAgent]);
|
|
464
497
|
|
|
465
498
|
// Cleanup on unmount
|
|
466
499
|
useEffect(() => {
|
|
@@ -720,7 +753,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
720
753
|
configuredModels,
|
|
721
754
|
getConfiguredModels,
|
|
722
755
|
setModel,
|
|
723
|
-
|
|
756
|
+
isCompacting,
|
|
724
757
|
mcpServers,
|
|
725
758
|
connectMcpServer,
|
|
726
759
|
disconnectMcpServer,
|
|
@@ -751,6 +784,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
751
784
|
workingDirectory,
|
|
752
785
|
version,
|
|
753
786
|
workdir,
|
|
787
|
+
recreateAgent,
|
|
754
788
|
};
|
|
755
789
|
|
|
756
790
|
return (
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
PluginManagerContextType,
|
|
12
12
|
} from "../components/PluginManagerTypes.js";
|
|
13
13
|
|
|
14
|
-
export function usePluginManager(
|
|
14
|
+
export function usePluginManager(options?: {
|
|
15
|
+
onPluginInstalled?: () => void;
|
|
16
|
+
}): PluginManagerContextType {
|
|
15
17
|
const [state, setState] = useState<PluginManagerState>({
|
|
16
18
|
currentView: "DISCOVER",
|
|
17
19
|
selectedId: null,
|
|
@@ -141,16 +143,20 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
141
143
|
}, []);
|
|
142
144
|
|
|
143
145
|
const addMarketplace = useCallback(
|
|
144
|
-
async (source: string) => {
|
|
146
|
+
async (source: string, scope: "user" | "project" | "local" = "user") => {
|
|
145
147
|
clearPluginFeedback();
|
|
146
148
|
setState((prev: PluginManagerState) => ({
|
|
147
149
|
...prev,
|
|
148
150
|
isLoading: true,
|
|
149
151
|
}));
|
|
150
152
|
try {
|
|
151
|
-
await pluginCore.addMarketplace(source);
|
|
153
|
+
await pluginCore.addMarketplace(source, scope);
|
|
152
154
|
await refresh();
|
|
153
|
-
setSuccessMessage(`Marketplace added successfully`);
|
|
155
|
+
setSuccessMessage(`Marketplace added successfully (${scope} scope)`);
|
|
156
|
+
setState((prev: PluginManagerState) => ({
|
|
157
|
+
...prev,
|
|
158
|
+
currentView: "MARKETPLACES",
|
|
159
|
+
}));
|
|
154
160
|
} catch (error) {
|
|
155
161
|
setState((prev: PluginManagerState) => ({
|
|
156
162
|
...prev,
|
|
@@ -163,16 +169,20 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
163
169
|
);
|
|
164
170
|
|
|
165
171
|
const removeMarketplace = useCallback(
|
|
166
|
-
async (name: string) => {
|
|
172
|
+
async (name: string, scope?: "user" | "project" | "local") => {
|
|
167
173
|
clearPluginFeedback();
|
|
168
174
|
setState((prev: PluginManagerState) => ({
|
|
169
175
|
...prev,
|
|
170
176
|
isLoading: true,
|
|
171
177
|
}));
|
|
172
178
|
try {
|
|
173
|
-
await pluginCore.removeMarketplace(name);
|
|
179
|
+
await pluginCore.removeMarketplace(name, scope);
|
|
174
180
|
await refresh();
|
|
175
181
|
setSuccessMessage(`Marketplace '${name}' removed successfully`);
|
|
182
|
+
setState((prev: PluginManagerState) => ({
|
|
183
|
+
...prev,
|
|
184
|
+
currentView: "MARKETPLACES",
|
|
185
|
+
}));
|
|
176
186
|
} catch (error) {
|
|
177
187
|
setState((prev: PluginManagerState) => ({
|
|
178
188
|
...prev,
|
|
@@ -222,6 +232,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
222
232
|
await pluginCore.installPlugin(pluginId, scope);
|
|
223
233
|
await refresh();
|
|
224
234
|
setSuccessMessage(`Plugin '${name}' installed successfully`);
|
|
235
|
+
options?.onPluginInstalled?.();
|
|
225
236
|
} catch (error) {
|
|
226
237
|
setState((prev: PluginManagerState) => ({
|
|
227
238
|
...prev,
|
|
@@ -230,7 +241,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
230
241
|
}));
|
|
231
242
|
}
|
|
232
243
|
},
|
|
233
|
-
[pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
|
|
244
|
+
[pluginCore, refresh, clearPluginFeedback, setSuccessMessage, options],
|
|
234
245
|
);
|
|
235
246
|
|
|
236
247
|
const uninstallPlugin = useCallback(
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface Task {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
status: "running" | "completed" | "failed" | "killed";
|
|
6
|
+
startTime: number;
|
|
7
|
+
exitCode?: number;
|
|
8
|
+
runtime?: number;
|
|
9
|
+
outputPath?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DetailOutput {
|
|
13
|
+
stdout: string;
|
|
14
|
+
stderr: string;
|
|
15
|
+
status: string;
|
|
16
|
+
outputPath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BackgroundTaskManagerState {
|
|
20
|
+
tasks: Task[];
|
|
21
|
+
selectedIndex: number;
|
|
22
|
+
viewMode: "list" | "detail";
|
|
23
|
+
detailTaskId: string | null;
|
|
24
|
+
detailOutput: DetailOutput | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type BackgroundTaskManagerAction =
|
|
28
|
+
| { type: "SET_TASKS"; tasks: Task[] }
|
|
29
|
+
| { type: "SELECT_INDEX"; index: number }
|
|
30
|
+
| { type: "SET_VIEW_MODE"; mode: "list" | "detail" }
|
|
31
|
+
| { type: "SET_DETAIL_TASK_ID"; id: string | null }
|
|
32
|
+
| { type: "SET_DETAIL_OUTPUT"; output: DetailOutput | null }
|
|
33
|
+
| { type: "RESET_DETAIL" };
|
|
34
|
+
|
|
35
|
+
export function backgroundTaskManagerReducer(
|
|
36
|
+
state: BackgroundTaskManagerState,
|
|
37
|
+
action: BackgroundTaskManagerAction,
|
|
38
|
+
): BackgroundTaskManagerState {
|
|
39
|
+
switch (action.type) {
|
|
40
|
+
case "SET_TASKS":
|
|
41
|
+
return { ...state, tasks: action.tasks };
|
|
42
|
+
case "SELECT_INDEX":
|
|
43
|
+
return { ...state, selectedIndex: action.index };
|
|
44
|
+
case "SET_VIEW_MODE":
|
|
45
|
+
return { ...state, viewMode: action.mode };
|
|
46
|
+
case "SET_DETAIL_TASK_ID":
|
|
47
|
+
return { ...state, detailTaskId: action.id };
|
|
48
|
+
case "SET_DETAIL_OUTPUT":
|
|
49
|
+
return { ...state, detailOutput: action.output };
|
|
50
|
+
case "RESET_DETAIL":
|
|
51
|
+
return {
|
|
52
|
+
...state,
|
|
53
|
+
viewMode: "list",
|
|
54
|
+
detailTaskId: null,
|
|
55
|
+
detailOutput: null,
|
|
56
|
+
};
|
|
57
|
+
default:
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface MarketplaceAddFormState {
|
|
2
|
+
source: string;
|
|
3
|
+
scopeIndex: number;
|
|
4
|
+
step: "source" | "scope";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type MarketplaceAddFormAction =
|
|
8
|
+
| { type: "SET_SOURCE"; source: string }
|
|
9
|
+
| { type: "SET_SCOPE_INDEX"; index: number }
|
|
10
|
+
| { type: "SET_STEP"; step: "source" | "scope" }
|
|
11
|
+
| { type: "INSERT_CHAR"; text: string }
|
|
12
|
+
| { type: "DELETE_CHAR" }
|
|
13
|
+
| { type: "BACK_TO_SOURCE" };
|
|
14
|
+
|
|
15
|
+
export function marketplaceAddFormReducer(
|
|
16
|
+
state: MarketplaceAddFormState,
|
|
17
|
+
action: MarketplaceAddFormAction,
|
|
18
|
+
): MarketplaceAddFormState {
|
|
19
|
+
switch (action.type) {
|
|
20
|
+
case "SET_SOURCE":
|
|
21
|
+
return { ...state, source: action.source };
|
|
22
|
+
case "SET_SCOPE_INDEX":
|
|
23
|
+
return { ...state, scopeIndex: action.index };
|
|
24
|
+
case "SET_STEP":
|
|
25
|
+
return { ...state, step: action.step };
|
|
26
|
+
case "INSERT_CHAR":
|
|
27
|
+
return { ...state, source: state.source + action.text };
|
|
28
|
+
case "DELETE_CHAR":
|
|
29
|
+
return { ...state, source: state.source.slice(0, -1) };
|
|
30
|
+
case "BACK_TO_SOURCE":
|
|
31
|
+
return { ...state, step: "source" };
|
|
32
|
+
default:
|
|
33
|
+
return state;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface McpManagerState {
|
|
2
|
+
selectedIndex: number;
|
|
3
|
+
viewMode: "list" | "detail";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type McpManagerAction =
|
|
7
|
+
| { type: "MOVE_UP"; serverCount: number }
|
|
8
|
+
| { type: "MOVE_DOWN"; serverCount: number }
|
|
9
|
+
| { type: "SET_VIEW_MODE"; viewMode: "list" | "detail" };
|
|
10
|
+
|
|
11
|
+
export function mcpManagerReducer(
|
|
12
|
+
state: McpManagerState,
|
|
13
|
+
action: McpManagerAction,
|
|
14
|
+
): McpManagerState {
|
|
15
|
+
switch (action.type) {
|
|
16
|
+
case "MOVE_UP":
|
|
17
|
+
return { ...state, selectedIndex: Math.max(0, state.selectedIndex - 1) };
|
|
18
|
+
case "MOVE_DOWN":
|
|
19
|
+
return {
|
|
20
|
+
...state,
|
|
21
|
+
selectedIndex: Math.min(
|
|
22
|
+
action.serverCount - 1,
|
|
23
|
+
state.selectedIndex + 1,
|
|
24
|
+
),
|
|
25
|
+
};
|
|
26
|
+
case "SET_VIEW_MODE":
|
|
27
|
+
return { ...state, viewMode: action.viewMode };
|
|
28
|
+
default:
|
|
29
|
+
return state;
|
|
30
|
+
}
|
|
31
|
+
}
|