snow-ai 0.3.27 → 0.3.29
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/systemPrompt.js +4 -3
- package/dist/hooks/useCommandHandler.js +5 -3
- package/dist/hooks/useConversation.js +54 -42
- package/dist/mcp/filesystem.d.ts +11 -0
- package/dist/mcp/filesystem.js +95 -2
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +354 -35
- package/dist/ui/components/MessageList.d.ts +1 -0
- package/dist/ui/pages/ChatScreen.js +44 -7
- package/dist/ui/pages/SubAgentConfigScreen.js +27 -4
- package/dist/utils/sessionConverter.js +68 -21
- package/dist/utils/subAgentConfig.d.ts +3 -1
- package/dist/utils/subAgentConfig.js +3 -1
- package/dist/utils/subAgentExecutor.js +6 -1
- package/dist/utils/toolExecutor.d.ts +3 -1
- package/dist/utils/toolExecutor.js +90 -2
- package/dist/utils/vscodeConnection.d.ts +7 -0
- package/dist/utils/vscodeConnection.js +79 -2
- package/package.json +1 -1
package/dist/api/systemPrompt.js
CHANGED
|
@@ -195,9 +195,10 @@ Guidance and recommendations:
|
|
|
195
195
|
|
|
196
196
|
## 📚 Project Context (SNOW.md)
|
|
197
197
|
|
|
198
|
-
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
198
|
+
- Contains: project overview, architecture, tech stack.
|
|
199
|
+
- Generally located in the project root directory.
|
|
200
|
+
- You can read this file at any time to understand the project and recommend reading.
|
|
201
|
+
- This file may not exist. If you can't find it, please ignore it.
|
|
201
202
|
|
|
202
203
|
Remember: **ACTION > ANALYSIS**. Write code first, investigate only when blocked.`;
|
|
203
204
|
// Export SYSTEM_PROMPT as a getter function for real-time ROLE.md updates
|
|
@@ -27,6 +27,7 @@ export async function executeContextCompression() {
|
|
|
27
27
|
tool_calls: msg.tool_calls,
|
|
28
28
|
images: msg.images,
|
|
29
29
|
reasoning: msg.reasoning,
|
|
30
|
+
thinking: msg.thinking, // 保留 thinking 字段(Anthropic Extended Thinking)
|
|
30
31
|
subAgentInternal: msg.subAgentInternal,
|
|
31
32
|
}));
|
|
32
33
|
// Compress the context (全量压缩,保留最后一轮完整对话)
|
|
@@ -38,10 +39,10 @@ export async function executeContextCompression() {
|
|
|
38
39
|
}
|
|
39
40
|
// 构建新的会话消息列表
|
|
40
41
|
const newSessionMessages = [];
|
|
41
|
-
//
|
|
42
|
+
// 添加压缩摘要到会话(使用 user 角色,因为 Extended Thinking 模式下所有 assistant 消息都需要 thinking 块)
|
|
42
43
|
newSessionMessages.push({
|
|
43
|
-
role: '
|
|
44
|
-
content: compressionResult.summary
|
|
44
|
+
role: 'user',
|
|
45
|
+
content: `[Context Summary from Previous Conversation]\n\n${compressionResult.summary}`,
|
|
45
46
|
timestamp: Date.now(),
|
|
46
47
|
});
|
|
47
48
|
// 添加保留的最后一轮完整对话(保留完整的消息结构)
|
|
@@ -57,6 +58,7 @@ export async function executeContextCompression() {
|
|
|
57
58
|
...(msg.tool_calls && { tool_calls: msg.tool_calls }),
|
|
58
59
|
...(msg.images && { images: msg.images }),
|
|
59
60
|
...(msg.reasoning && { reasoning: msg.reasoning }),
|
|
61
|
+
...(msg.thinking && { thinking: msg.thinking }), // 保留 thinking 字段(Anthropic Extended Thinking)
|
|
60
62
|
...(msg.subAgentInternal !== undefined && {
|
|
61
63
|
subAgentInternal: msg.subAgentInternal,
|
|
62
64
|
}),
|
|
@@ -327,6 +327,10 @@ export async function handleConversationWithTools(options) {
|
|
|
327
327
|
]);
|
|
328
328
|
}
|
|
329
329
|
// Display tool calls in UI - 只有耗时工具才显示进行中状态
|
|
330
|
+
// Generate parallel group ID when there are multiple tools
|
|
331
|
+
const parallelGroupId = receivedToolCalls.length > 1
|
|
332
|
+
? `parallel-${Date.now()}-${Math.random()}`
|
|
333
|
+
: undefined;
|
|
330
334
|
for (const toolCall of receivedToolCalls) {
|
|
331
335
|
const toolDisplay = formatToolCallMessage(toolCall);
|
|
332
336
|
let toolArgs;
|
|
@@ -351,6 +355,8 @@ export async function handleConversationWithTools(options) {
|
|
|
351
355
|
toolDisplay,
|
|
352
356
|
toolCallId: toolCall.id, // Store tool call ID for later update
|
|
353
357
|
toolPending: true, // Mark as pending execution
|
|
358
|
+
// Mark parallel group for ALL tools (time-consuming or not)
|
|
359
|
+
parallelGroup: parallelGroupId,
|
|
354
360
|
},
|
|
355
361
|
]);
|
|
356
362
|
}
|
|
@@ -509,8 +515,10 @@ export async function handleConversationWithTools(options) {
|
|
|
509
515
|
if (subAgentMessage.message.type === 'tool_calls') {
|
|
510
516
|
const toolCalls = subAgentMessage.message.tool_calls;
|
|
511
517
|
if (toolCalls && toolCalls.length > 0) {
|
|
512
|
-
// Add tool call messages for each tool
|
|
513
|
-
const toolMessages = toolCalls
|
|
518
|
+
// Add tool call messages for each tool (only for time-consuming tools)
|
|
519
|
+
const toolMessages = toolCalls
|
|
520
|
+
.filter((toolCall) => isToolNeedTwoStepDisplay(toolCall.function.name))
|
|
521
|
+
.map((toolCall) => {
|
|
514
522
|
const toolDisplay = formatToolCallMessage(toolCall);
|
|
515
523
|
let toolArgs;
|
|
516
524
|
try {
|
|
@@ -537,16 +545,21 @@ export async function handleConversationWithTools(options) {
|
|
|
537
545
|
},
|
|
538
546
|
subAgentInternal: true, // Mark as internal sub-agent message
|
|
539
547
|
};
|
|
540
|
-
// Save to session as 'assistant' role for API compatibility
|
|
541
|
-
const sessionMsg = {
|
|
542
|
-
role: 'assistant',
|
|
543
|
-
content: `⚇⚡ ${toolDisplay.toolName}`,
|
|
544
|
-
subAgentInternal: true,
|
|
545
|
-
tool_calls: [toolCall],
|
|
546
|
-
};
|
|
547
|
-
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
|
|
548
548
|
return uiMsg;
|
|
549
549
|
});
|
|
550
|
+
// Save all tool calls to session (regardless of display type)
|
|
551
|
+
const sessionMsg = {
|
|
552
|
+
role: 'assistant',
|
|
553
|
+
content: toolCalls
|
|
554
|
+
.map((tc) => {
|
|
555
|
+
const display = formatToolCallMessage(tc);
|
|
556
|
+
return `⚇⚡ ${display.toolName}`;
|
|
557
|
+
})
|
|
558
|
+
.join(', '),
|
|
559
|
+
subAgentInternal: true,
|
|
560
|
+
tool_calls: toolCalls,
|
|
561
|
+
};
|
|
562
|
+
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
|
|
550
563
|
return [...prev, ...toolMessages];
|
|
551
564
|
}
|
|
552
565
|
}
|
|
@@ -724,6 +737,8 @@ export async function handleConversationWithTools(options) {
|
|
|
724
737
|
m.toolResult !== undefined ||
|
|
725
738
|
m.subAgentInternal === true));
|
|
726
739
|
// Update existing tool call messages with results
|
|
740
|
+
// Collect all result messages first, then add them in batch
|
|
741
|
+
const resultMessages = [];
|
|
727
742
|
for (const result of toolResults) {
|
|
728
743
|
const toolCall = receivedToolCalls.find(tc => tc.id === result.tool_call_id);
|
|
729
744
|
if (toolCall) {
|
|
@@ -733,17 +748,13 @@ export async function handleConversationWithTools(options) {
|
|
|
733
748
|
const isError = result.content.startsWith('Error:');
|
|
734
749
|
const statusIcon = isError ? '✗' : '✓';
|
|
735
750
|
const statusText = isError ? `\n └─ ${result.content}` : '';
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
// Pass the full result.content for ToolResultPreview to parse
|
|
744
|
-
toolResult: !isError ? result.content : undefined,
|
|
745
|
-
},
|
|
746
|
-
]);
|
|
751
|
+
resultMessages.push({
|
|
752
|
+
role: 'assistant',
|
|
753
|
+
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
754
|
+
streaming: false,
|
|
755
|
+
// Pass the full result.content for ToolResultPreview to parse
|
|
756
|
+
toolResult: !isError ? result.content : undefined,
|
|
757
|
+
});
|
|
747
758
|
// Save the tool result to conversation history
|
|
748
759
|
conversationMessages.push(result);
|
|
749
760
|
saveMessage(result).catch(error => {
|
|
@@ -790,27 +801,24 @@ export async function handleConversationWithTools(options) {
|
|
|
790
801
|
// - 普通工具(单步显示):完成消息需要包含参数和结果,使用 toolDisplay
|
|
791
802
|
// 获取工具参数的格式化信息
|
|
792
803
|
const toolDisplay = formatToolCallMessage(toolCall);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
{
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
toolResult: !isError ? result.content : undefined,
|
|
812
|
-
},
|
|
813
|
-
]);
|
|
804
|
+
const isNonTimeConsuming = !isToolNeedTwoStepDisplay(toolCall.function.name);
|
|
805
|
+
resultMessages.push({
|
|
806
|
+
role: 'assistant',
|
|
807
|
+
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
808
|
+
streaming: false,
|
|
809
|
+
toolCall: editDiffData
|
|
810
|
+
? {
|
|
811
|
+
name: toolCall.function.name,
|
|
812
|
+
arguments: editDiffData,
|
|
813
|
+
}
|
|
814
|
+
: undefined,
|
|
815
|
+
// 为普通工具添加参数显示(耗时工具在进行中状态已经显示过参数)
|
|
816
|
+
toolDisplay: isNonTimeConsuming ? toolDisplay : undefined,
|
|
817
|
+
// Store tool result for preview rendering
|
|
818
|
+
toolResult: !isError ? result.content : undefined,
|
|
819
|
+
// Mark parallel group for ALL tools (time-consuming or not)
|
|
820
|
+
parallelGroup: parallelGroupId,
|
|
821
|
+
});
|
|
814
822
|
}
|
|
815
823
|
// Add tool result to conversation history and save (skip if already saved above)
|
|
816
824
|
if (toolCall && !toolCall.function.name.startsWith('subagent-')) {
|
|
@@ -820,6 +828,10 @@ export async function handleConversationWithTools(options) {
|
|
|
820
828
|
});
|
|
821
829
|
}
|
|
822
830
|
}
|
|
831
|
+
// Add all result messages in batch to avoid intermediate renders
|
|
832
|
+
if (resultMessages.length > 0) {
|
|
833
|
+
setMessages(prev => [...prev, ...resultMessages]);
|
|
834
|
+
}
|
|
823
835
|
// Check if there are pending user messages to insert
|
|
824
836
|
if (options.getPendingMessages && options.clearPendingMessages) {
|
|
825
837
|
const pendingMessages = options.getPendingMessages();
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -10,8 +10,19 @@ export declare class FilesystemMCPService {
|
|
|
10
10
|
*/
|
|
11
11
|
private readonly prettierSupportedExtensions;
|
|
12
12
|
constructor(basePath?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Extract relevant symbol information for a specific line range
|
|
15
|
+
* This provides context that helps AI make more accurate modifications
|
|
16
|
+
* @param symbols - All symbols in the file
|
|
17
|
+
* @param startLine - Start line of the range
|
|
18
|
+
* @param endLine - End line of the range
|
|
19
|
+
* @param _totalLines - Total lines in the file (reserved for future use)
|
|
20
|
+
* @returns Formatted string with relevant symbol information
|
|
21
|
+
*/
|
|
22
|
+
private extractRelevantSymbols;
|
|
13
23
|
/**
|
|
14
24
|
* Get the content of a file with optional line range
|
|
25
|
+
* Enhanced with symbol information for better AI context
|
|
15
26
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
16
27
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
17
28
|
* @param endLine - Ending line number (1-indexed, inclusive, optional - defaults to file end). Used for single file or as default for array of strings
|
package/dist/mcp/filesystem.js
CHANGED
|
@@ -11,6 +11,8 @@ import { calculateSimilarity, normalizeForDisplay, } from './utils/filesystem/si
|
|
|
11
11
|
import { analyzeCodeStructure, findSmartContextBoundaries, } from './utils/filesystem/code-analysis.utils.js';
|
|
12
12
|
import { findClosestMatches, generateDiffMessage, } from './utils/filesystem/match-finder.utils.js';
|
|
13
13
|
import { parseEditBySearchParams, parseEditByLineParams, executeBatchOperation, } from './utils/filesystem/batch-operations.utils.js';
|
|
14
|
+
// ACE Code Search utilities for symbol parsing
|
|
15
|
+
import { parseFileSymbols } from './utils/aceCodeSearch/symbol.utils.js';
|
|
14
16
|
const { resolve, dirname, isAbsolute } = path;
|
|
15
17
|
const execAsync = promisify(exec);
|
|
16
18
|
/**
|
|
@@ -52,8 +54,76 @@ export class FilesystemMCPService {
|
|
|
52
54
|
});
|
|
53
55
|
this.basePath = resolve(basePath);
|
|
54
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract relevant symbol information for a specific line range
|
|
59
|
+
* This provides context that helps AI make more accurate modifications
|
|
60
|
+
* @param symbols - All symbols in the file
|
|
61
|
+
* @param startLine - Start line of the range
|
|
62
|
+
* @param endLine - End line of the range
|
|
63
|
+
* @param _totalLines - Total lines in the file (reserved for future use)
|
|
64
|
+
* @returns Formatted string with relevant symbol information
|
|
65
|
+
*/
|
|
66
|
+
extractRelevantSymbols(symbols, startLine, endLine, _totalLines) {
|
|
67
|
+
if (symbols.length === 0) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
// Categorize symbols
|
|
71
|
+
const imports = symbols.filter(s => s.type === 'import');
|
|
72
|
+
const exports = symbols.filter(s => s.type === 'export');
|
|
73
|
+
// Symbols within the requested range
|
|
74
|
+
const symbolsInRange = symbols.filter(s => s.line >= startLine && s.line <= endLine);
|
|
75
|
+
// Symbols defined before the range that might be referenced
|
|
76
|
+
const symbolsBeforeRange = symbols.filter(s => s.line < startLine);
|
|
77
|
+
// Build context information
|
|
78
|
+
const parts = [];
|
|
79
|
+
// Always include imports (crucial for understanding dependencies)
|
|
80
|
+
if (imports.length > 0) {
|
|
81
|
+
const importList = imports
|
|
82
|
+
.slice(0, 10) // Limit to avoid excessive tokens
|
|
83
|
+
.map(s => ` • ${s.name} (line ${s.line})`)
|
|
84
|
+
.join('\n');
|
|
85
|
+
parts.push(`📦 Imports:\n${importList}`);
|
|
86
|
+
}
|
|
87
|
+
// Symbols defined in the current range
|
|
88
|
+
if (symbolsInRange.length > 0) {
|
|
89
|
+
const rangeSymbols = symbolsInRange
|
|
90
|
+
.slice(0, 15)
|
|
91
|
+
.map(s => ` • ${s.type}: ${s.name} (line ${s.line})${s.signature ? ` - ${s.signature.slice(0, 60)}` : ''}`)
|
|
92
|
+
.join('\n');
|
|
93
|
+
parts.push(`🎯 Symbols in this range:\n${rangeSymbols}`);
|
|
94
|
+
}
|
|
95
|
+
// Key definitions before this range (that might be referenced)
|
|
96
|
+
if (symbolsBeforeRange.length > 0 && startLine > 1) {
|
|
97
|
+
const relevantBefore = symbolsBeforeRange
|
|
98
|
+
.filter(s => s.type === 'function' || s.type === 'class')
|
|
99
|
+
.slice(-5) // Last 5 before the range
|
|
100
|
+
.map(s => ` • ${s.type}: ${s.name} (line ${s.line})`)
|
|
101
|
+
.join('\n');
|
|
102
|
+
if (relevantBefore) {
|
|
103
|
+
parts.push(`⬆️ Key definitions above:\n${relevantBefore}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Exports (important for understanding module interface)
|
|
107
|
+
if (exports.length > 0) {
|
|
108
|
+
const exportList = exports
|
|
109
|
+
.slice(0, 10)
|
|
110
|
+
.map(s => ` • ${s.name} (line ${s.line})`)
|
|
111
|
+
.join('\n');
|
|
112
|
+
parts.push(`📤 Exports:\n${exportList}`);
|
|
113
|
+
}
|
|
114
|
+
if (parts.length === 0) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
return ('\n\n' +
|
|
118
|
+
'='.repeat(60) +
|
|
119
|
+
'\n📚 SYMBOL INDEX & DEFINITIONS:\n' +
|
|
120
|
+
'='.repeat(60) +
|
|
121
|
+
'\n' +
|
|
122
|
+
parts.join('\n\n'));
|
|
123
|
+
}
|
|
55
124
|
/**
|
|
56
125
|
* Get the content of a file with optional line range
|
|
126
|
+
* Enhanced with symbol information for better AI context
|
|
57
127
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
58
128
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
59
129
|
* @param endLine - Ending line number (1-indexed, inclusive, optional - defaults to file end). Used for single file or as default for array of strings
|
|
@@ -127,7 +197,18 @@ export class FilesystemMCPService {
|
|
|
127
197
|
const lineNum = start + index;
|
|
128
198
|
return `${lineNum}→${line}`;
|
|
129
199
|
});
|
|
130
|
-
|
|
200
|
+
let fileContent = `📄 ${file} (lines ${start}-${end}/${totalLines})\n${numberedLines.join('\n')}`;
|
|
201
|
+
// Parse and append symbol information
|
|
202
|
+
try {
|
|
203
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
204
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
205
|
+
if (symbolInfo) {
|
|
206
|
+
fileContent += symbolInfo;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Silently fail symbol parsing
|
|
211
|
+
}
|
|
131
212
|
allContents.push(fileContent);
|
|
132
213
|
filesData.push({
|
|
133
214
|
path: file,
|
|
@@ -197,7 +278,19 @@ export class FilesystemMCPService {
|
|
|
197
278
|
const lineNum = start + index;
|
|
198
279
|
return `${lineNum}→${line}`;
|
|
199
280
|
});
|
|
200
|
-
|
|
281
|
+
let partialContent = numberedLines.join('\n');
|
|
282
|
+
// Parse and append symbol information to provide better context for AI
|
|
283
|
+
try {
|
|
284
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
285
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
286
|
+
if (symbolInfo) {
|
|
287
|
+
partialContent += symbolInfo;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
// Silently fail symbol parsing - don't block file reading
|
|
292
|
+
// This is optional context enhancement, not critical
|
|
293
|
+
}
|
|
201
294
|
return {
|
|
202
295
|
content: partialContent,
|
|
203
296
|
startLine: start,
|