snow-ai 0.3.4 → 0.3.5
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/api/anthropic.js +38 -13
- package/dist/api/types.d.ts +1 -0
- package/dist/hooks/useConversation.js +212 -59
- package/dist/hooks/useSnapshotState.d.ts +2 -0
- package/dist/hooks/useToolConfirmation.js +1 -1
- package/dist/mcp/subagent.d.ts +35 -0
- package/dist/mcp/subagent.js +64 -0
- package/dist/ui/components/ChatInput.d.ts +1 -2
- package/dist/ui/components/ChatInput.js +47 -39
- package/dist/ui/components/FileRollbackConfirmation.d.ts +3 -2
- package/dist/ui/components/FileRollbackConfirmation.js +81 -22
- package/dist/ui/components/MessageList.d.ts +7 -1
- package/dist/ui/components/MessageList.js +16 -5
- package/dist/ui/pages/ChatScreen.js +29 -20
- package/dist/ui/pages/ConfigScreen.js +47 -46
- package/dist/ui/pages/ProxyConfigScreen.js +1 -1
- package/dist/ui/pages/SubAgentConfigScreen.d.ts +9 -0
- package/dist/ui/pages/SubAgentConfigScreen.js +352 -0
- package/dist/ui/pages/SubAgentListScreen.d.ts +9 -0
- package/dist/ui/pages/SubAgentListScreen.js +114 -0
- package/dist/ui/pages/WelcomeScreen.js +30 -2
- package/dist/utils/incrementalSnapshot.d.ts +7 -0
- package/dist/utils/incrementalSnapshot.js +34 -0
- package/dist/utils/mcpToolsManager.js +41 -1
- package/dist/utils/retryUtils.js +5 -0
- package/dist/utils/sessionConverter.d.ts +1 -0
- package/dist/utils/sessionConverter.js +192 -89
- package/dist/utils/subAgentConfig.d.ts +43 -0
- package/dist/utils/subAgentConfig.js +126 -0
- package/dist/utils/subAgentExecutor.d.ts +29 -0
- package/dist/utils/subAgentExecutor.js +272 -0
- package/dist/utils/toolExecutor.d.ts +10 -2
- package/dist/utils/toolExecutor.js +46 -5
- package/package.json +1 -1
- package/dist/ui/pages/ConfigProfileScreen.d.ts +0 -7
- package/dist/ui/pages/ConfigProfileScreen.js +0 -300
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
type Props = {
|
|
3
3
|
fileCount: number;
|
|
4
|
-
|
|
4
|
+
filePaths: string[];
|
|
5
|
+
onConfirm: (rollbackFiles: boolean | null) => void;
|
|
5
6
|
};
|
|
6
|
-
export default function FileRollbackConfirmation({ fileCount, onConfirm }: Props): React.JSX.Element;
|
|
7
|
+
export default function FileRollbackConfirmation({ fileCount, filePaths, onConfirm }: Props): React.JSX.Element;
|
|
7
8
|
export {};
|
|
@@ -1,34 +1,71 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
export default function FileRollbackConfirmation({ fileCount, onConfirm }) {
|
|
3
|
+
export default function FileRollbackConfirmation({ fileCount, filePaths, onConfirm }) {
|
|
4
4
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
5
|
+
const [showFullList, setShowFullList] = useState(false);
|
|
6
|
+
const [fileScrollIndex, setFileScrollIndex] = useState(0);
|
|
5
7
|
const options = [
|
|
6
8
|
{ label: 'Yes, rollback files and conversation', value: true },
|
|
7
9
|
{ label: 'No, rollback conversation only', value: false }
|
|
8
10
|
];
|
|
9
11
|
useInput((_, key) => {
|
|
10
|
-
//
|
|
11
|
-
if (key.
|
|
12
|
-
|
|
12
|
+
// Tab - toggle full file list view
|
|
13
|
+
if (key.tab) {
|
|
14
|
+
setShowFullList(prev => !prev);
|
|
15
|
+
setFileScrollIndex(0); // Reset scroll when toggling
|
|
13
16
|
return;
|
|
14
17
|
}
|
|
15
|
-
//
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
// In full list mode, use up/down to scroll files
|
|
19
|
+
if (showFullList) {
|
|
20
|
+
const maxVisibleFiles = 10;
|
|
21
|
+
const maxScroll = Math.max(0, filePaths.length - maxVisibleFiles);
|
|
22
|
+
if (key.upArrow) {
|
|
23
|
+
setFileScrollIndex(prev => Math.max(0, prev - 1));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (key.downArrow) {
|
|
27
|
+
setFileScrollIndex(prev => Math.min(maxScroll, prev + 1));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// In compact mode, up/down navigate options
|
|
33
|
+
if (key.upArrow) {
|
|
34
|
+
setSelectedIndex(prev => Math.max(0, prev - 1));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (key.downArrow) {
|
|
38
|
+
setSelectedIndex(prev => Math.min(options.length - 1, prev + 1));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
19
41
|
}
|
|
20
|
-
// Enter - confirm selection
|
|
21
|
-
if (key.return) {
|
|
42
|
+
// Enter - confirm selection (only when not in full list mode)
|
|
43
|
+
if (key.return && !showFullList) {
|
|
22
44
|
onConfirm(options[selectedIndex]?.value ?? false);
|
|
23
45
|
return;
|
|
24
46
|
}
|
|
25
|
-
// ESC - cancel rollback
|
|
47
|
+
// ESC - exit full list mode or cancel rollback
|
|
26
48
|
if (key.escape) {
|
|
27
|
-
|
|
49
|
+
if (showFullList) {
|
|
50
|
+
setShowFullList(false);
|
|
51
|
+
setFileScrollIndex(0);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
onConfirm(null); // null means cancel everything
|
|
55
|
+
}
|
|
28
56
|
return;
|
|
29
57
|
}
|
|
30
58
|
});
|
|
31
|
-
|
|
59
|
+
// Display logic for file list
|
|
60
|
+
const maxFilesToShowCompact = 5;
|
|
61
|
+
const maxFilesToShowFull = 10;
|
|
62
|
+
const displayFiles = showFullList
|
|
63
|
+
? filePaths.slice(fileScrollIndex, fileScrollIndex + maxFilesToShowFull)
|
|
64
|
+
: filePaths.slice(0, maxFilesToShowCompact);
|
|
65
|
+
const remainingCountCompact = fileCount - maxFilesToShowCompact;
|
|
66
|
+
const hasMoreAbove = showFullList && fileScrollIndex > 0;
|
|
67
|
+
const hasMoreBelow = showFullList && (fileScrollIndex + maxFilesToShowFull) < filePaths.length;
|
|
68
|
+
return (React.createElement(Box, { flexDirection: "column", marginX: 1, marginBottom: 1, padding: 1 },
|
|
32
69
|
React.createElement(Box, { marginBottom: 1 },
|
|
33
70
|
React.createElement(Text, { color: "yellow", bold: true }, "\u26A0 File Rollback Confirmation")),
|
|
34
71
|
React.createElement(Box, { marginBottom: 1 },
|
|
@@ -37,13 +74,35 @@ export default function FileRollbackConfirmation({ fileCount, onConfirm }) {
|
|
|
37
74
|
fileCount,
|
|
38
75
|
" file",
|
|
39
76
|
fileCount > 1 ? 's' : '',
|
|
40
|
-
" that will be rolled back
|
|
41
|
-
React.createElement(Box, { marginBottom: 1 },
|
|
42
|
-
React.createElement(Text, { color: "gray", dimColor: true },
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
77
|
+
" that will be rolled back:")),
|
|
78
|
+
React.createElement(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 2 },
|
|
79
|
+
hasMoreAbove && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
80
|
+
"\u2191 ",
|
|
81
|
+
fileScrollIndex,
|
|
82
|
+
" more above...")),
|
|
83
|
+
displayFiles.map((file, index) => (React.createElement(Text, { key: index, color: "cyan", dimColor: true },
|
|
84
|
+
"\u2022 ",
|
|
85
|
+
file))),
|
|
86
|
+
hasMoreBelow && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
87
|
+
"\u2193 ",
|
|
88
|
+
filePaths.length - (fileScrollIndex + maxFilesToShowFull),
|
|
89
|
+
" more below...")),
|
|
90
|
+
!showFullList && remainingCountCompact > 0 && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
91
|
+
"... and ",
|
|
92
|
+
remainingCountCompact,
|
|
93
|
+
" more file",
|
|
94
|
+
remainingCountCompact > 1 ? 's' : ''))),
|
|
95
|
+
!showFullList && (React.createElement(React.Fragment, null,
|
|
96
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
97
|
+
React.createElement(Text, { color: "gray", dimColor: true }, "Do you want to rollback the files as well?")),
|
|
98
|
+
React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, options.map((option, index) => (React.createElement(Box, { key: index },
|
|
99
|
+
React.createElement(Text, { color: index === selectedIndex ? 'green' : 'white', bold: index === selectedIndex },
|
|
100
|
+
index === selectedIndex ? '❯ ' : ' ',
|
|
101
|
+
option.label))))))),
|
|
102
|
+
React.createElement(Box, null,
|
|
103
|
+
React.createElement(Text, { color: "gray", dimColor: true }, showFullList
|
|
104
|
+
? '↑↓ scroll · Tab back · ESC close'
|
|
105
|
+
: fileCount > maxFilesToShowCompact
|
|
106
|
+
? `↑↓ select · Tab view all (${fileCount} files) · Enter confirm · ESC cancel`
|
|
107
|
+
: '↑↓ select · Enter confirm · ESC cancel'))));
|
|
49
108
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SelectedFile } from '../../utils/fileUtils.js';
|
|
3
3
|
export interface Message {
|
|
4
|
-
role: 'user' | 'assistant' | 'command';
|
|
4
|
+
role: 'user' | 'assistant' | 'command' | 'subagent';
|
|
5
5
|
content: string;
|
|
6
6
|
streaming?: boolean;
|
|
7
7
|
discontinued?: boolean;
|
|
@@ -39,6 +39,12 @@ export interface Message {
|
|
|
39
39
|
exitCode?: number;
|
|
40
40
|
command?: string;
|
|
41
41
|
};
|
|
42
|
+
subAgent?: {
|
|
43
|
+
agentId: string;
|
|
44
|
+
agentName: string;
|
|
45
|
+
isComplete?: boolean;
|
|
46
|
+
};
|
|
47
|
+
subAgentInternal?: boolean;
|
|
42
48
|
}
|
|
43
49
|
interface Props {
|
|
44
50
|
messages: Message[];
|
|
@@ -11,20 +11,31 @@ const MessageList = memo(({ messages, animationFrame, maxMessages = 6 }) => {
|
|
|
11
11
|
? 'green'
|
|
12
12
|
: message.role === 'command'
|
|
13
13
|
? 'gray'
|
|
14
|
-
: message.
|
|
15
|
-
?
|
|
16
|
-
:
|
|
14
|
+
: message.role === 'subagent'
|
|
15
|
+
? 'magenta'
|
|
16
|
+
: message.streaming
|
|
17
|
+
? STREAM_COLORS[animationFrame]
|
|
18
|
+
: 'cyan';
|
|
17
19
|
return (React.createElement(Box, { key: index },
|
|
18
20
|
React.createElement(Text, { color: iconColor, bold: true }, message.role === 'user'
|
|
19
21
|
? '⛇'
|
|
20
22
|
: message.role === 'command'
|
|
21
23
|
? '⌘'
|
|
22
|
-
: '
|
|
24
|
+
: message.role === 'subagent'
|
|
25
|
+
? '◈'
|
|
26
|
+
: '❆'),
|
|
23
27
|
React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" }, message.role === 'command' ? (React.createElement(Text, { color: "gray" },
|
|
24
28
|
"\u2514\u2500 ",
|
|
25
|
-
message.commandName)) : (React.createElement(React.Fragment, null,
|
|
29
|
+
message.commandName)) : message.role === 'subagent' ? (React.createElement(React.Fragment, null,
|
|
30
|
+
React.createElement(Text, { color: "magenta", dimColor: true },
|
|
31
|
+
"\u2514\u2500 Sub-Agent: ",
|
|
32
|
+
message.subAgent?.agentName,
|
|
33
|
+
message.subAgent?.isComplete ? ' ✓' : ' ...'),
|
|
34
|
+
React.createElement(Box, { marginLeft: 2 },
|
|
35
|
+
React.createElement(MarkdownRenderer, { content: message.content || ' ', color: "gray" })))) : (React.createElement(React.Fragment, null,
|
|
26
36
|
React.createElement(MarkdownRenderer, { content: message.content || ' ', color: message.role === 'user' ? 'gray' : undefined }),
|
|
27
37
|
(message.systemInfo ||
|
|
38
|
+
message.files ||
|
|
28
39
|
message.files ||
|
|
29
40
|
message.images) && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
30
41
|
message.systemInfo && (React.createElement(React.Fragment, null,
|
|
@@ -130,6 +130,20 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
130
130
|
clearTimeout(handler);
|
|
131
131
|
};
|
|
132
132
|
}, [terminalWidth, stdout]);
|
|
133
|
+
// Reload messages from session when remountKey changes (to restore sub-agent messages)
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (remountKey === 0)
|
|
136
|
+
return; // Skip initial render
|
|
137
|
+
const reloadMessages = async () => {
|
|
138
|
+
const currentSession = sessionManager.getCurrentSession();
|
|
139
|
+
if (currentSession && currentSession.messages.length > 0) {
|
|
140
|
+
// Convert session messages back to UI format
|
|
141
|
+
const uiMessages = convertSessionMessagesToUI(currentSession.messages);
|
|
142
|
+
setMessages(uiMessages);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
reloadMessages();
|
|
146
|
+
}, [remountKey]);
|
|
133
147
|
// Use tool confirmation hook
|
|
134
148
|
const { pendingToolConfirmation, requestToolConfirmation, isToolAutoApproved, addMultipleToAlwaysApproved, } = useToolConfirmation();
|
|
135
149
|
// Minimum terminal height required for proper rendering
|
|
@@ -249,9 +263,15 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
249
263
|
}
|
|
250
264
|
// Show confirmation dialog if there are files to rollback
|
|
251
265
|
if (totalFileCount > 0) {
|
|
266
|
+
// Get list of files that will be rolled back
|
|
267
|
+
const currentSession = sessionManager.getCurrentSession();
|
|
268
|
+
const filePaths = currentSession
|
|
269
|
+
? await incrementalSnapshotManager.getFilesToRollback(currentSession.id, selectedIndex)
|
|
270
|
+
: [];
|
|
252
271
|
snapshotState.setPendingRollback({
|
|
253
272
|
messageIndex: selectedIndex,
|
|
254
|
-
fileCount:
|
|
273
|
+
fileCount: filePaths.length, // Use actual unique file count
|
|
274
|
+
filePaths,
|
|
255
275
|
});
|
|
256
276
|
}
|
|
257
277
|
else {
|
|
@@ -278,6 +298,11 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
278
298
|
snapshotState.setPendingRollback(null);
|
|
279
299
|
};
|
|
280
300
|
const handleRollbackConfirm = (rollbackFiles) => {
|
|
301
|
+
if (rollbackFiles === null) {
|
|
302
|
+
// User cancelled - just close the dialog without doing anything
|
|
303
|
+
snapshotState.setPendingRollback(null);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
281
306
|
if (snapshotState.pendingRollback) {
|
|
282
307
|
performRollback(snapshotState.pendingRollback.messageIndex, rollbackFiles);
|
|
283
308
|
}
|
|
@@ -527,7 +552,7 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
527
552
|
"\u2022 Working directory: ",
|
|
528
553
|
workingDirectory)))),
|
|
529
554
|
...messages
|
|
530
|
-
.filter(m => !m.streaming
|
|
555
|
+
.filter(m => !m.streaming)
|
|
531
556
|
.map((message, index) => {
|
|
532
557
|
// Determine tool message type and color
|
|
533
558
|
let toolStatusColor = 'cyan';
|
|
@@ -670,22 +695,6 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
670
695
|
message.discontinued && (React.createElement(Text, { color: "red", bold: true }, "\u2514\u2500 user discontinue"))))))));
|
|
671
696
|
}),
|
|
672
697
|
] }, item => item),
|
|
673
|
-
messages
|
|
674
|
-
.filter(m => m.toolPending)
|
|
675
|
-
.map((message, index) => (React.createElement(Box, { key: `pending-tool-${index}`, marginBottom: 1, paddingX: 1, width: terminalWidth },
|
|
676
|
-
React.createElement(Text, { color: "yellowBright", bold: true }, "\u2746"),
|
|
677
|
-
React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" },
|
|
678
|
-
React.createElement(Box, { flexDirection: "row" },
|
|
679
|
-
React.createElement(MarkdownRenderer, { content: message.content || ' ', color: "yellow" }),
|
|
680
|
-
React.createElement(Box, { marginLeft: 1 },
|
|
681
|
-
React.createElement(Text, { color: "yellow" },
|
|
682
|
-
React.createElement(Spinner, { type: "dots" })))),
|
|
683
|
-
message.toolDisplay && message.toolDisplay.args.length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 }, message.toolDisplay.args.map((arg, argIndex) => (React.createElement(Text, { key: argIndex, color: "gray", dimColor: true },
|
|
684
|
-
arg.isLast ? '└─' : '├─',
|
|
685
|
-
" ",
|
|
686
|
-
arg.key,
|
|
687
|
-
": ",
|
|
688
|
-
arg.value))))))))),
|
|
689
698
|
(streamingState.isStreaming || isSaving) && !pendingToolConfirmation && (React.createElement(Box, { marginBottom: 1, paddingX: 1, width: terminalWidth },
|
|
690
699
|
React.createElement(Text, { color: ['#FF6EBF', 'green', 'blue', 'cyan', '#B588F8'][streamingState.animationFrame], bold: true }, "\u2746"),
|
|
691
700
|
React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" }, streamingState.isStreaming ? (React.createElement(React.Fragment, null, streamingState.retryStatus &&
|
|
@@ -738,7 +747,7 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
738
747
|
React.createElement(MCPInfoPanel, null),
|
|
739
748
|
React.createElement(Box, { marginTop: 1 },
|
|
740
749
|
React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to close")))),
|
|
741
|
-
snapshotState.pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: snapshotState.pendingRollback.fileCount, onConfirm: handleRollbackConfirm })),
|
|
750
|
+
snapshotState.pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: snapshotState.pendingRollback.fileCount, filePaths: snapshotState.pendingRollback.filePaths || [], onConfirm: handleRollbackConfirm })),
|
|
742
751
|
!pendingToolConfirmation &&
|
|
743
752
|
!isCompressing &&
|
|
744
753
|
!showSessionPanel &&
|
|
@@ -752,7 +761,7 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
752
761
|
cacheReadTokens: streamingState.contextUsage.cache_read_input_tokens,
|
|
753
762
|
cachedTokens: streamingState.contextUsage.cached_tokens,
|
|
754
763
|
}
|
|
755
|
-
: undefined
|
|
764
|
+
: undefined }),
|
|
756
765
|
vscodeState.vscodeConnectionStatus !== 'disconnected' && (React.createElement(Box, { marginTop: 1 },
|
|
757
766
|
React.createElement(Text, { color: vscodeState.vscodeConnectionStatus === 'connecting'
|
|
758
767
|
? 'yellow'
|
|
@@ -3,7 +3,6 @@ import { Box, Text, useInput } from 'ink';
|
|
|
3
3
|
import Gradient from 'ink-gradient';
|
|
4
4
|
import { Select, Alert, Spinner } from '@inkjs/ui';
|
|
5
5
|
import TextInput from 'ink-text-input';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
6
|
import { getOpenAiConfig, updateOpenAiConfig, validateApiConfig, } from '../../utils/apiConfig.js';
|
|
8
7
|
import { fetchAvailableModels, filterModels, } from '../../api/models.js';
|
|
9
8
|
import { getActiveProfileName, getAllProfiles, switchProfile, createProfile, deleteProfile, saveProfile, } from '../../utils/configManager.js';
|
|
@@ -158,34 +157,6 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
158
157
|
return compactModelName;
|
|
159
158
|
return '';
|
|
160
159
|
};
|
|
161
|
-
const handleProfileChange = (value) => {
|
|
162
|
-
if (value === '__CREATE_NEW__') {
|
|
163
|
-
setProfileMode('creating');
|
|
164
|
-
setNewProfileName('');
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (value === '__DELETE__') {
|
|
168
|
-
if (activeProfile === 'default') {
|
|
169
|
-
setErrors(['Cannot delete the default profile']);
|
|
170
|
-
setIsEditing(false); // Exit editing mode to prevent Select component error
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
setProfileMode('deleting');
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Switch profile
|
|
177
|
-
try {
|
|
178
|
-
switchProfile(value);
|
|
179
|
-
loadProfilesAndConfig();
|
|
180
|
-
setIsEditing(false);
|
|
181
|
-
setErrors([]);
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
setErrors([
|
|
185
|
-
err instanceof Error ? err.message : 'Failed to switch profile',
|
|
186
|
-
]);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
160
|
const handleCreateProfile = () => {
|
|
190
161
|
const cleaned = stripFocusArtifacts(newProfileName).trim();
|
|
191
162
|
if (!cleaned) {
|
|
@@ -204,7 +175,9 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
204
175
|
basicModel,
|
|
205
176
|
maxContextTokens,
|
|
206
177
|
maxTokens,
|
|
207
|
-
compactModel: compactModelName
|
|
178
|
+
compactModel: compactModelName
|
|
179
|
+
? { modelName: compactModelName }
|
|
180
|
+
: undefined,
|
|
208
181
|
},
|
|
209
182
|
};
|
|
210
183
|
createProfile(cleaned, currentConfig);
|
|
@@ -321,6 +294,25 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
321
294
|
if (isFocusEventInput(rawInput)) {
|
|
322
295
|
return;
|
|
323
296
|
}
|
|
297
|
+
// Handle profile shortcuts (highest priority - works even in Select mode)
|
|
298
|
+
if (currentField === 'profile' && (input === 'n' || input === 'N')) {
|
|
299
|
+
// Handle profile creation (works in both normal and editing mode)
|
|
300
|
+
setProfileMode('creating');
|
|
301
|
+
setNewProfileName('');
|
|
302
|
+
setIsEditing(false); // Exit Select editing mode
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (currentField === 'profile' && (input === 'd' || input === 'D')) {
|
|
306
|
+
// Handle profile deletion (works in both normal and editing mode)
|
|
307
|
+
if (activeProfile === 'default') {
|
|
308
|
+
setErrors(['Cannot delete the default profile']);
|
|
309
|
+
setIsEditing(false);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
setProfileMode('deleting');
|
|
313
|
+
setIsEditing(false); // Exit Select editing mode
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
324
316
|
// Handle profile creation mode
|
|
325
317
|
if (profileMode === 'creating') {
|
|
326
318
|
if (key.return) {
|
|
@@ -638,20 +630,24 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
638
630
|
currentField === 'compactModelName' && 'Compact Model',
|
|
639
631
|
":"),
|
|
640
632
|
React.createElement(Box, { marginLeft: 3, marginTop: 1 },
|
|
641
|
-
currentField === 'profile' && (React.createElement(
|
|
642
|
-
|
|
633
|
+
currentField === 'profile' && (React.createElement(Box, { flexDirection: "column" },
|
|
634
|
+
profiles.length > 1 && (React.createElement(Text, { color: "gray", dimColor: true }, "Scroll to see more profiles (\u2191\u2193)")),
|
|
635
|
+
React.createElement(Select, { options: profiles.map(p => ({
|
|
643
636
|
label: `${p.displayName}${p.isActive ? ' (Active)' : ''}`,
|
|
644
637
|
value: p.name,
|
|
645
|
-
})),
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
638
|
+
})), defaultValue: activeProfile, onChange: value => {
|
|
639
|
+
switchProfile(value);
|
|
640
|
+
loadProfilesAndConfig();
|
|
641
|
+
setIsEditing(false);
|
|
642
|
+
setErrors([]);
|
|
643
|
+
} }),
|
|
644
|
+
React.createElement(Box, { flexDirection: "row", marginTop: 1 },
|
|
645
|
+
React.createElement(Box, { marginRight: 2 },
|
|
646
|
+
React.createElement(Text, { color: "green" }, "+ New"),
|
|
647
|
+
React.createElement(Text, { color: "gray" }, " (n)")),
|
|
648
|
+
React.createElement(Box, null,
|
|
649
|
+
React.createElement(Text, { color: "red" }, "\uD83C\uDD87 Delete"),
|
|
650
|
+
React.createElement(Text, { color: "gray" }, " (d)"))))),
|
|
655
651
|
currentField === 'requestMethod' && (React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: value => {
|
|
656
652
|
setRequestMethod(value);
|
|
657
653
|
setIsEditing(false);
|
|
@@ -669,7 +665,9 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
669
665
|
currentField === 'basicModel' ||
|
|
670
666
|
currentField === 'compactModelName') &&
|
|
671
667
|
'Type to filter, ↑↓ to select, Enter to confirm, Esc to cancel',
|
|
672
|
-
|
|
668
|
+
currentField === 'profile' &&
|
|
669
|
+
'↑↓ to select profile, N to create new, D to delete, Enter to confirm, Esc to cancel',
|
|
670
|
+
currentField === 'requestMethod' &&
|
|
673
671
|
'↑↓ to select, Enter to confirm, Esc to cancel')))) : (React.createElement(Box, { flexDirection: "column" },
|
|
674
672
|
React.createElement(Box, { flexDirection: "column" },
|
|
675
673
|
React.createElement(Text, { color: currentField === 'profile' ? 'green' : 'white' },
|
|
@@ -707,7 +705,7 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
707
705
|
"Anthropic Beta:"),
|
|
708
706
|
React.createElement(Box, { marginLeft: 3 },
|
|
709
707
|
React.createElement(Text, { color: "gray" },
|
|
710
|
-
anthropicBeta ? '
|
|
708
|
+
anthropicBeta ? '☒ Enabled' : '☐ Disabled',
|
|
711
709
|
" (Press Enter to toggle)"))),
|
|
712
710
|
React.createElement(Box, { flexDirection: "column" },
|
|
713
711
|
React.createElement(Text, { color: currentField === 'advancedModel' ? 'green' : 'white' },
|
|
@@ -760,7 +758,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
760
758
|
currentField === 'compactModelName')) && (React.createElement(Box, { flexDirection: "column", marginTop: 1 }, isEditing ? (React.createElement(Alert, { variant: "info" },
|
|
761
759
|
"Editing mode:",
|
|
762
760
|
' ',
|
|
763
|
-
currentField === 'maxContextTokens' ||
|
|
761
|
+
currentField === 'maxContextTokens' ||
|
|
762
|
+
currentField === 'maxTokens'
|
|
764
763
|
? 'Type to edit, Enter to save'
|
|
765
|
-
: 'Press Enter to save and exit editing')) : (React.createElement(Alert, { variant: "info" },
|
|
764
|
+
: 'Press Enter to save and exit editing')) : (React.createElement(Alert, { variant: "info" }, currentField === 'profile'
|
|
765
|
+
? 'Use ↑↓ to navigate, N to create new profile, D to delete profile, Ctrl+S or Esc to save'
|
|
766
|
+
: 'Use ↑↓ to navigate, Enter to edit, M for manual input, Ctrl+S or Esc to save'))))));
|
|
766
767
|
}
|
|
@@ -98,7 +98,7 @@ export default function ProxyConfigScreen({ onBack, onSave, inlineMode = false }
|
|
|
98
98
|
"Enable Proxy:"),
|
|
99
99
|
React.createElement(Box, { marginLeft: 3 },
|
|
100
100
|
React.createElement(Text, { color: "gray" },
|
|
101
|
-
enabled ? '
|
|
101
|
+
enabled ? '☒ Enabled' : '☐ Disabled',
|
|
102
102
|
" (Press Enter to toggle)")))),
|
|
103
103
|
React.createElement(Box, { marginBottom: 1 },
|
|
104
104
|
React.createElement(Box, { flexDirection: "column" },
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
onBack: () => void;
|
|
4
|
+
onSave: () => void;
|
|
5
|
+
inlineMode?: boolean;
|
|
6
|
+
agentId?: string;
|
|
7
|
+
};
|
|
8
|
+
export default function SubAgentConfigScreen({ onBack, onSave, inlineMode, agentId, }: Props): React.JSX.Element;
|
|
9
|
+
export {};
|