snow-ai 0.2.23 → 0.2.25
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/agents/compactAgent.d.ts +55 -0
- package/dist/agents/compactAgent.js +301 -0
- package/dist/api/chat.d.ts +0 -8
- package/dist/api/chat.js +1 -144
- package/dist/api/responses.d.ts +0 -11
- package/dist/api/responses.js +1 -189
- package/dist/api/systemPrompt.d.ts +1 -1
- package/dist/api/systemPrompt.js +80 -206
- package/dist/app.d.ts +2 -1
- package/dist/app.js +11 -13
- package/dist/cli.js +23 -3
- package/dist/hooks/useConversation.js +51 -7
- package/dist/hooks/useGlobalNavigation.d.ts +1 -1
- package/dist/hooks/useKeyboardInput.js +14 -8
- package/dist/mcp/filesystem.d.ts +49 -6
- package/dist/mcp/filesystem.js +243 -86
- package/dist/mcp/websearch.d.ts +118 -0
- package/dist/mcp/websearch.js +451 -0
- package/dist/ui/components/ToolResultPreview.js +60 -1
- package/dist/ui/pages/ChatScreen.d.ts +4 -2
- package/dist/ui/pages/ChatScreen.js +62 -14
- package/dist/ui/pages/{ApiConfigScreen.d.ts → ConfigScreen.d.ts} +1 -1
- package/dist/ui/pages/ConfigScreen.js +549 -0
- package/dist/ui/pages/{ModelConfigScreen.d.ts → ProxyConfigScreen.d.ts} +1 -1
- package/dist/ui/pages/ProxyConfigScreen.js +143 -0
- package/dist/ui/pages/WelcomeScreen.js +15 -15
- package/dist/utils/apiConfig.d.ts +8 -2
- package/dist/utils/apiConfig.js +21 -0
- package/dist/utils/commandExecutor.d.ts +1 -1
- package/dist/utils/contextCompressor.js +363 -49
- package/dist/utils/mcpToolsManager.d.ts +1 -1
- package/dist/utils/mcpToolsManager.js +106 -6
- package/dist/utils/resourceMonitor.d.ts +65 -0
- package/dist/utils/resourceMonitor.js +175 -0
- package/dist/utils/retryUtils.js +6 -0
- package/dist/utils/sessionManager.d.ts +1 -0
- package/dist/utils/sessionManager.js +10 -0
- package/dist/utils/textBuffer.js +7 -2
- package/dist/utils/toolExecutor.d.ts +2 -2
- package/dist/utils/toolExecutor.js +4 -4
- package/package.json +5 -1
- package/dist/ui/pages/ApiConfigScreen.js +0 -161
- package/dist/ui/pages/ModelConfigScreen.js +0 -467
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { exec, execSync } from 'child_process';
|
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import App from './app.js';
|
|
9
9
|
import { vscodeConnection } from './utils/vscodeConnection.js';
|
|
10
|
+
import { resourceMonitor } from './utils/resourceMonitor.js';
|
|
10
11
|
const execAsync = promisify(exec);
|
|
11
12
|
// Check for updates asynchronously
|
|
12
13
|
async function checkForUpdates(currentVersion) {
|
|
@@ -34,6 +35,7 @@ const cli = meow(`
|
|
|
34
35
|
--help Show help
|
|
35
36
|
--version Show version
|
|
36
37
|
--update Update to latest version
|
|
38
|
+
-c Skip welcome screen and resume last conversation
|
|
37
39
|
`, {
|
|
38
40
|
importMeta: import.meta,
|
|
39
41
|
flags: {
|
|
@@ -41,6 +43,10 @@ const cli = meow(`
|
|
|
41
43
|
type: 'boolean',
|
|
42
44
|
default: false,
|
|
43
45
|
},
|
|
46
|
+
c: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
44
50
|
},
|
|
45
51
|
});
|
|
46
52
|
// Handle update flag
|
|
@@ -56,8 +62,20 @@ if (cli.flags.update) {
|
|
|
56
62
|
process.exit(1);
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
// Start resource monitoring in development/debug mode
|
|
66
|
+
if (process.env['NODE_ENV'] === 'development' || process.env['DEBUG']) {
|
|
67
|
+
resourceMonitor.startMonitoring(30000); // Monitor every 30 seconds
|
|
68
|
+
// Check for leaks every 5 minutes
|
|
69
|
+
setInterval(() => {
|
|
70
|
+
const { hasLeak, reasons } = resourceMonitor.checkForLeaks();
|
|
71
|
+
if (hasLeak) {
|
|
72
|
+
console.error('⚠️ Potential memory leak detected:');
|
|
73
|
+
reasons.forEach(reason => console.error(` - ${reason}`));
|
|
74
|
+
}
|
|
75
|
+
}, 5 * 60 * 1000);
|
|
76
|
+
}
|
|
59
77
|
// Startup component that shows loading spinner during update check
|
|
60
|
-
const Startup = ({ version }) => {
|
|
78
|
+
const Startup = ({ version, skipWelcome, }) => {
|
|
61
79
|
const [appReady, setAppReady] = React.useState(false);
|
|
62
80
|
React.useEffect(() => {
|
|
63
81
|
let mounted = true;
|
|
@@ -87,13 +105,15 @@ const Startup = ({ version }) => {
|
|
|
87
105
|
React.createElement(Spinner, { type: "dots" })),
|
|
88
106
|
React.createElement(Text, null, " Checking for updates..."))));
|
|
89
107
|
}
|
|
90
|
-
return React.createElement(App, { version: version });
|
|
108
|
+
return React.createElement(App, { version: version, skipWelcome: skipWelcome });
|
|
91
109
|
};
|
|
92
110
|
// Disable bracketed paste mode on startup
|
|
93
111
|
process.stdout.write('\x1b[?2004l');
|
|
94
112
|
// Re-enable on exit to avoid polluting parent shell
|
|
95
113
|
const cleanup = () => {
|
|
96
114
|
process.stdout.write('\x1b[?2004l');
|
|
115
|
+
// Stop resource monitoring
|
|
116
|
+
resourceMonitor.stopMonitoring();
|
|
97
117
|
// Disconnect VSCode connection before exit
|
|
98
118
|
vscodeConnection.stop();
|
|
99
119
|
};
|
|
@@ -106,7 +126,7 @@ process.on('SIGTERM', () => {
|
|
|
106
126
|
cleanup();
|
|
107
127
|
process.exit(0);
|
|
108
128
|
});
|
|
109
|
-
render(React.createElement(Startup, { version: cli.pkg.version }), {
|
|
129
|
+
render(React.createElement(Startup, { version: cli.pkg.version, skipWelcome: cli.flags.c }), {
|
|
110
130
|
exitOnCtrlC: false,
|
|
111
131
|
patchConsole: true,
|
|
112
132
|
});
|
|
@@ -10,6 +10,7 @@ import { getOpenAiConfig } from '../utils/apiConfig.js';
|
|
|
10
10
|
import { sessionManager } from '../utils/sessionManager.js';
|
|
11
11
|
import { formatTodoContext } from '../utils/todoPreprocessor.js';
|
|
12
12
|
import { formatToolCallMessage } from '../utils/messageFormatter.js';
|
|
13
|
+
import { resourceMonitor } from '../utils/resourceMonitor.js';
|
|
13
14
|
/**
|
|
14
15
|
* Handle conversation with streaming and tool calls
|
|
15
16
|
*/
|
|
@@ -64,13 +65,28 @@ export async function handleConversationWithTools(options) {
|
|
|
64
65
|
}).catch(error => {
|
|
65
66
|
console.error('Failed to save user message:', error);
|
|
66
67
|
});
|
|
67
|
-
// Initialize token encoder
|
|
68
|
+
// Initialize token encoder with proper cleanup tracking
|
|
68
69
|
let encoder;
|
|
70
|
+
let encoderFreed = false;
|
|
71
|
+
const freeEncoder = () => {
|
|
72
|
+
if (!encoderFreed && encoder) {
|
|
73
|
+
try {
|
|
74
|
+
encoder.free();
|
|
75
|
+
encoderFreed = true;
|
|
76
|
+
resourceMonitor.trackEncoderFreed();
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.error('Failed to free encoder:', e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
69
83
|
try {
|
|
70
84
|
encoder = encoding_for_model('gpt-4');
|
|
85
|
+
resourceMonitor.trackEncoderCreated();
|
|
71
86
|
}
|
|
72
87
|
catch (e) {
|
|
73
88
|
encoder = encoding_for_model('gpt-3.5-turbo');
|
|
89
|
+
resourceMonitor.trackEncoderCreated();
|
|
74
90
|
}
|
|
75
91
|
setStreamTokenCount(0);
|
|
76
92
|
const config = getOpenAiConfig();
|
|
@@ -83,8 +99,10 @@ export async function handleConversationWithTools(options) {
|
|
|
83
99
|
const sessionApprovedTools = new Set();
|
|
84
100
|
try {
|
|
85
101
|
while (true) {
|
|
86
|
-
if (controller.signal.aborted)
|
|
102
|
+
if (controller.signal.aborted) {
|
|
103
|
+
freeEncoder();
|
|
87
104
|
break;
|
|
105
|
+
}
|
|
88
106
|
let streamedContent = '';
|
|
89
107
|
let receivedToolCalls;
|
|
90
108
|
// Stream AI response - choose API based on config
|
|
@@ -200,6 +218,7 @@ export async function handleConversationWithTools(options) {
|
|
|
200
218
|
// If aborted during streaming, exit the loop
|
|
201
219
|
// (discontinued message already added by ChatScreen ESC handler)
|
|
202
220
|
if (controller.signal.aborted) {
|
|
221
|
+
freeEncoder();
|
|
203
222
|
break;
|
|
204
223
|
}
|
|
205
224
|
// If there are tool calls, we need to handle them specially
|
|
@@ -222,6 +241,17 @@ export async function handleConversationWithTools(options) {
|
|
|
222
241
|
saveMessage(assistantMessage).catch(error => {
|
|
223
242
|
console.error('Failed to save assistant message:', error);
|
|
224
243
|
});
|
|
244
|
+
// If there's text content before tool calls, display it first
|
|
245
|
+
if (streamedContent && streamedContent.trim()) {
|
|
246
|
+
setMessages(prev => [
|
|
247
|
+
...prev,
|
|
248
|
+
{
|
|
249
|
+
role: 'assistant',
|
|
250
|
+
content: streamedContent.trim(),
|
|
251
|
+
streaming: false,
|
|
252
|
+
},
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
225
255
|
// Display tool calls in UI with pending status
|
|
226
256
|
for (const toolCall of receivedToolCalls) {
|
|
227
257
|
const toolDisplay = formatToolCallMessage(toolCall);
|
|
@@ -292,7 +322,7 @@ export async function handleConversationWithTools(options) {
|
|
|
292
322
|
if (options.setIsStreaming) {
|
|
293
323
|
options.setIsStreaming(false);
|
|
294
324
|
}
|
|
295
|
-
|
|
325
|
+
freeEncoder();
|
|
296
326
|
return; // Exit the conversation loop
|
|
297
327
|
}
|
|
298
328
|
// If approved_always, add ALL these tools to both global and session-approved sets
|
|
@@ -307,7 +337,12 @@ export async function handleConversationWithTools(options) {
|
|
|
307
337
|
approvedTools.push(...toolsNeedingConfirmation);
|
|
308
338
|
}
|
|
309
339
|
// Execute approved tools
|
|
310
|
-
const toolResults = await executeToolCalls(approvedTools);
|
|
340
|
+
const toolResults = await executeToolCalls(approvedTools, controller.signal, setStreamTokenCount);
|
|
341
|
+
// Check if aborted during tool execution
|
|
342
|
+
if (controller.signal.aborted) {
|
|
343
|
+
freeEncoder();
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
311
346
|
// Check if there are TODO related tool calls, if yes refresh TODO list
|
|
312
347
|
const hasTodoTools = approvedTools.some(t => t.function.name.startsWith('todo-'));
|
|
313
348
|
const hasTodoUpdateTools = approvedTools.some(t => t.function.name === 'todo-update');
|
|
@@ -367,6 +402,13 @@ export async function handleConversationWithTools(options) {
|
|
|
367
402
|
// If parsing fails, just show regular result
|
|
368
403
|
}
|
|
369
404
|
}
|
|
405
|
+
// Check if this tool should show preview (websearch, ace, filesystem-read, etc.)
|
|
406
|
+
const shouldShowPreview = toolCall.function.name.startsWith('websearch-') ||
|
|
407
|
+
toolCall.function.name.startsWith('ace-') ||
|
|
408
|
+
toolCall.function.name === 'filesystem-read' ||
|
|
409
|
+
toolCall.function.name === 'filesystem-list' ||
|
|
410
|
+
toolCall.function.name === 'filesystem-create' ||
|
|
411
|
+
toolCall.function.name === 'filesystem-write';
|
|
370
412
|
// Update the existing pending message instead of adding a new one
|
|
371
413
|
setMessages(prev => prev.map(msg => {
|
|
372
414
|
if (msg.toolCallId === toolCall.id && msg.toolPending) {
|
|
@@ -384,7 +426,9 @@ export async function handleConversationWithTools(options) {
|
|
|
384
426
|
name: toolCall.function.name,
|
|
385
427
|
arguments: terminalResultData,
|
|
386
428
|
}
|
|
387
|
-
:
|
|
429
|
+
: shouldShowPreview
|
|
430
|
+
? undefined // Clear toolCall for preview-enabled tools
|
|
431
|
+
: msg.toolCall, // Keep original toolCall for other tools
|
|
388
432
|
// Store tool result for preview rendering
|
|
389
433
|
toolResult: !isError ? result.content : undefined,
|
|
390
434
|
};
|
|
@@ -464,10 +508,10 @@ export async function handleConversationWithTools(options) {
|
|
|
464
508
|
break;
|
|
465
509
|
}
|
|
466
510
|
// Free encoder
|
|
467
|
-
|
|
511
|
+
freeEncoder();
|
|
468
512
|
}
|
|
469
513
|
catch (error) {
|
|
470
|
-
|
|
514
|
+
freeEncoder();
|
|
471
515
|
throw error;
|
|
472
516
|
}
|
|
473
517
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const NAVIGATION_EVENT = "navigate";
|
|
2
2
|
export interface NavigationEvent {
|
|
3
|
-
destination: 'welcome' | 'chat' | 'settings' | '
|
|
3
|
+
destination: 'welcome' | 'chat' | 'settings' | 'mcp' | 'systemprompt' | 'customheaders';
|
|
4
4
|
}
|
|
5
5
|
export declare function navigateTo(destination: NavigationEvent['destination']): void;
|
|
6
6
|
export declare function onNavigate(handler: (event: NavigationEvent) => void): () => void;
|
|
@@ -26,15 +26,21 @@ export function useKeyboardInput(options) {
|
|
|
26
26
|
useInput((input, key) => {
|
|
27
27
|
if (disabled)
|
|
28
28
|
return;
|
|
29
|
-
// Filter out focus events
|
|
30
|
-
// Focus events
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
29
|
+
// Filter out focus events more robustly
|
|
30
|
+
// Focus events: ESC[I (focus in) or ESC[O (focus out)
|
|
31
|
+
// Some terminals may send these with or without ESC, and they might appear
|
|
32
|
+
// anywhere in the input string (especially during drag-and-drop with Shift held)
|
|
33
|
+
// We need to filter them out but NOT remove legitimate user input
|
|
34
|
+
if (
|
|
35
|
+
// Complete escape sequences
|
|
36
|
+
input === '\x1b[I' ||
|
|
34
37
|
input === '\x1b[O' ||
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
// Standalone sequences (exact match only)
|
|
39
|
+
input === '[I' ||
|
|
40
|
+
input === '[O' ||
|
|
41
|
+
// Filter if input ONLY contains focus events and whitespace
|
|
42
|
+
// This handles cases like " [I" or "[I " from drag-and-drop with Shift
|
|
43
|
+
/^[\s\x1b\[IO]+$/.test(input) && (input.includes('[I') || input.includes('[O'))) {
|
|
38
44
|
return;
|
|
39
45
|
}
|
|
40
46
|
// Shift+Tab - Toggle YOLO mode
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -39,6 +39,26 @@ export declare class FilesystemMCPService {
|
|
|
39
39
|
*/
|
|
40
40
|
private readonly prettierSupportedExtensions;
|
|
41
41
|
constructor(basePath?: string);
|
|
42
|
+
/**
|
|
43
|
+
* Calculate similarity between two strings using a smarter algorithm
|
|
44
|
+
* This normalizes whitespace first to avoid false negatives from spacing differences
|
|
45
|
+
* Returns a value between 0 (completely different) and 1 (identical)
|
|
46
|
+
*/
|
|
47
|
+
private calculateSimilarity;
|
|
48
|
+
/**
|
|
49
|
+
* Calculate Levenshtein distance between two strings
|
|
50
|
+
*/
|
|
51
|
+
private levenshteinDistance;
|
|
52
|
+
/**
|
|
53
|
+
* Find the closest matching candidates in the file content
|
|
54
|
+
* Returns top N candidates sorted by similarity
|
|
55
|
+
*/
|
|
56
|
+
private findClosestMatches;
|
|
57
|
+
/**
|
|
58
|
+
* Generate a helpful diff message showing differences between search and actual content
|
|
59
|
+
* Note: This is ONLY for display purposes. Tabs/spaces are normalized for better readability.
|
|
60
|
+
*/
|
|
61
|
+
private generateDiffMessage;
|
|
42
62
|
/**
|
|
43
63
|
* Analyze code structure for balance and completeness
|
|
44
64
|
* Helps AI identify bracket mismatches, unclosed tags, and boundary issues
|
|
@@ -50,14 +70,14 @@ export declare class FilesystemMCPService {
|
|
|
50
70
|
*/
|
|
51
71
|
private findSmartContextBoundaries;
|
|
52
72
|
/**
|
|
53
|
-
* Get the content of a file with
|
|
73
|
+
* Get the content of a file with optional line range
|
|
54
74
|
* @param filePath - Path to the file (relative to base path or absolute)
|
|
55
|
-
* @param startLine - Starting line number (1-indexed, inclusive)
|
|
56
|
-
* @param endLine - Ending line number (1-indexed, inclusive)
|
|
75
|
+
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1)
|
|
76
|
+
* @param endLine - Ending line number (1-indexed, inclusive, optional - defaults to 500 or file end)
|
|
57
77
|
* @returns Object containing the requested content with line numbers and metadata
|
|
58
78
|
* @throws Error if file doesn't exist or cannot be read
|
|
59
79
|
*/
|
|
60
|
-
getFileContent(filePath: string, startLine
|
|
80
|
+
getFileContent(filePath: string, startLine?: number, endLine?: number): Promise<{
|
|
61
81
|
content: string;
|
|
62
82
|
startLine: number;
|
|
63
83
|
endLine: number;
|
|
@@ -107,10 +127,10 @@ export declare class FilesystemMCPService {
|
|
|
107
127
|
}>;
|
|
108
128
|
/**
|
|
109
129
|
* Edit a file by searching for exact content and replacing it
|
|
110
|
-
* This method
|
|
130
|
+
* This method uses SMART MATCHING to handle whitespace differences automatically.
|
|
111
131
|
*
|
|
112
132
|
* @param filePath - Path to the file to edit
|
|
113
|
-
* @param searchContent -
|
|
133
|
+
* @param searchContent - Content to search for (whitespace will be normalized automatically)
|
|
114
134
|
* @param replaceContent - New content to replace the search content with
|
|
115
135
|
* @param occurrence - Which occurrence to replace (1-indexed, default: 1, use -1 for all)
|
|
116
136
|
* @param contextLines - Number of context lines to return before and after the edit (default: 8)
|
|
@@ -169,6 +189,29 @@ export declare class FilesystemMCPService {
|
|
|
169
189
|
private validatePath;
|
|
170
190
|
}
|
|
171
191
|
export declare const filesystemService: FilesystemMCPService;
|
|
192
|
+
/**
|
|
193
|
+
* MCP Tool definitions for integration
|
|
194
|
+
*
|
|
195
|
+
* 🎯 **RECOMMENDED WORKFLOW FOR AI AGENTS**:
|
|
196
|
+
*
|
|
197
|
+
* 1️⃣ **SEARCH FIRST** (DON'T skip this!):
|
|
198
|
+
* - Use ace_text_search() to find code patterns/strings
|
|
199
|
+
* - Use ace_search_symbols() to find functions/classes by name
|
|
200
|
+
* - Use ace_file_outline() to understand file structure
|
|
201
|
+
*
|
|
202
|
+
* 2️⃣ **READ STRATEGICALLY** (Only after search):
|
|
203
|
+
* - Use filesystem_read() WITHOUT line numbers to read entire file
|
|
204
|
+
* - OR use filesystem_read(filePath, startLine, endLine) to read specific range
|
|
205
|
+
* - ⚠️ AVOID reading files line-by-line from top - wastes tokens!
|
|
206
|
+
*
|
|
207
|
+
* 3️⃣ **EDIT SAFELY**:
|
|
208
|
+
* - PREFER filesystem_edit_search() for modifying existing code (no line counting!)
|
|
209
|
+
* - Use filesystem_edit() only for adding new code or when search-replace doesn't fit
|
|
210
|
+
*
|
|
211
|
+
* 📊 **TOKEN EFFICIENCY**:
|
|
212
|
+
* - ❌ BAD: Read file top-to-bottom, repeat reading, blind scanning
|
|
213
|
+
* - ✅ GOOD: Search → Targeted read → Edit with context
|
|
214
|
+
*/
|
|
172
215
|
export declare const mcpTools: ({
|
|
173
216
|
name: string;
|
|
174
217
|
description: string;
|