snow-ai 0.3.36 → 0.4.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/agents/codebaseIndexAgent.js +1 -0
- package/dist/agents/codebaseReviewAgent.d.ts +61 -0
- package/dist/agents/codebaseReviewAgent.js +301 -0
- package/dist/agents/promptOptimizeAgent.d.ts +54 -0
- package/dist/agents/promptOptimizeAgent.js +268 -0
- package/dist/api/anthropic.js +1 -0
- package/dist/api/chat.js +1 -0
- package/dist/api/embedding.js +1 -0
- package/dist/api/gemini.js +2 -1
- package/dist/api/responses.js +1 -0
- package/dist/api/systemPrompt.d.ts +1 -5
- package/dist/api/systemPrompt.js +168 -100
- package/dist/app.js +14 -6
- package/dist/cli.js +1 -1
- package/dist/hooks/useCommandPanel.js +48 -46
- package/dist/hooks/useConversation.d.ts +2 -1
- package/dist/hooks/useConversation.js +116 -30
- package/dist/hooks/useGlobalExit.js +4 -2
- package/dist/hooks/useStreamingState.d.ts +9 -0
- package/dist/hooks/useStreamingState.js +3 -0
- package/dist/i18n/I18nContext.d.ts +14 -0
- package/dist/i18n/I18nContext.js +24 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/lang/en.d.ts +2 -0
- package/dist/i18n/lang/en.js +483 -0
- package/dist/i18n/lang/es.d.ts +2 -0
- package/dist/i18n/lang/es.js +483 -0
- package/dist/i18n/lang/ja.d.ts +2 -0
- package/dist/i18n/lang/ja.js +483 -0
- package/dist/i18n/lang/ko.d.ts +2 -0
- package/dist/i18n/lang/ko.js +483 -0
- package/dist/i18n/lang/zh-TW.d.ts +2 -0
- package/dist/i18n/lang/zh-TW.js +483 -0
- package/dist/i18n/lang/zh.d.ts +2 -0
- package/dist/i18n/lang/zh.js +483 -0
- package/dist/i18n/translations.d.ts +2 -0
- package/dist/i18n/translations.js +14 -0
- package/dist/i18n/types.d.ts +459 -0
- package/dist/i18n/types.js +1 -0
- package/dist/mcp/aceCodeSearch.d.ts +17 -48
- package/dist/mcp/aceCodeSearch.js +24 -56
- package/dist/mcp/bash.js +8 -1
- package/dist/mcp/codebaseSearch.d.ts +1 -1
- package/dist/mcp/codebaseSearch.js +159 -30
- package/dist/mcp/filesystem.d.ts +3 -80
- package/dist/mcp/filesystem.js +23 -103
- package/dist/mcp/subagent.d.ts +2 -1
- package/dist/mcp/subagent.js +54 -5
- package/dist/ui/components/ChatInput.js +22 -25
- package/dist/ui/components/CommandPanel.d.ts +1 -1
- package/dist/ui/components/CommandPanel.js +20 -13
- package/dist/ui/components/DiffViewer.d.ts +1 -1
- package/dist/ui/components/DiffViewer.js +101 -91
- package/dist/ui/components/FileList.js +22 -11
- package/dist/ui/components/HelpPanel.js +47 -21
- package/dist/ui/components/Menu.js +6 -2
- package/dist/ui/components/MessageList.d.ts +6 -0
- package/dist/ui/components/MessageList.js +1 -1
- package/dist/ui/components/ToolConfirmation.d.ts +4 -1
- package/dist/ui/components/ToolConfirmation.js +28 -2
- package/dist/ui/components/ToolResultPreview.d.ts +2 -1
- package/dist/ui/components/ToolResultPreview.js +41 -25
- package/dist/ui/pages/ChatScreen.js +177 -56
- package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
- package/dist/ui/pages/ConfigScreen.js +138 -98
- package/dist/ui/pages/CustomHeadersScreen.js +75 -69
- package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
- package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
- package/dist/ui/pages/ProxyConfigScreen.js +27 -23
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
- package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
- package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
- package/dist/ui/pages/WelcomeScreen.js +40 -26
- package/dist/utils/apiConfig.d.ts +2 -0
- package/dist/utils/codebaseConfig.d.ts +1 -5
- package/dist/utils/codebaseConfig.js +2 -10
- package/dist/utils/codebaseSearchEvents.d.ts +16 -0
- package/dist/utils/codebaseSearchEvents.js +13 -0
- package/dist/utils/commands/agent.js +2 -2
- package/dist/utils/commands/init.js +1 -1
- package/dist/utils/configManager.js +26 -5
- package/dist/utils/contextCompressor.js +1 -1
- package/dist/utils/languageConfig.d.ts +21 -0
- package/dist/utils/languageConfig.js +61 -0
- package/dist/utils/mcpToolsManager.js +0 -9
- package/dist/utils/notebookManager.js +11 -4
- package/dist/utils/sessionConverter.js +13 -3
- package/dist/utils/sessionManager.d.ts +1 -0
- package/dist/utils/subAgentConfig.d.ts +10 -5
- package/dist/utils/subAgentConfig.js +112 -19
- package/dist/utils/subAgentExecutor.d.ts +9 -1
- package/dist/utils/subAgentExecutor.js +122 -9
- package/dist/utils/toolExecutor.d.ts +2 -1
- package/dist/utils/toolExecutor.js +1 -2
- package/dist/utils/usageLogger.js +18 -3
- package/package.json +2 -1
|
@@ -37,7 +37,7 @@ export async function handleConversationWithTools(options) {
|
|
|
37
37
|
const mcpTools = await collectAllMCPTools();
|
|
38
38
|
// Build conversation history with TODO context as pinned user message
|
|
39
39
|
let conversationMessages = [
|
|
40
|
-
{ role: 'system', content: getSystemPrompt(
|
|
40
|
+
{ role: 'system', content: getSystemPrompt() },
|
|
41
41
|
];
|
|
42
42
|
// If there are TODOs, add pinned context message at the front
|
|
43
43
|
if (existingTodoList && existingTodoList.todos.length > 0) {
|
|
@@ -430,32 +430,67 @@ export async function handleConversationWithTools(options) {
|
|
|
430
430
|
const firstTool = sensitiveTools[0];
|
|
431
431
|
const allTools = sensitiveTools.length > 1 ? sensitiveTools : undefined;
|
|
432
432
|
const confirmation = await requestToolConfirmation(firstTool, undefined, allTools);
|
|
433
|
-
if (confirmation === 'reject'
|
|
433
|
+
if (confirmation === 'reject' ||
|
|
434
|
+
(typeof confirmation === 'object' &&
|
|
435
|
+
confirmation.type === 'reject_with_reply')) {
|
|
434
436
|
setMessages(prev => prev.filter(msg => !msg.toolPending));
|
|
437
|
+
const rejectMessage = typeof confirmation === 'object'
|
|
438
|
+
? `Tool execution rejected by user: ${confirmation.reason}`
|
|
439
|
+
: 'Error: Tool execution rejected by user';
|
|
440
|
+
// Create UI messages for rejected tools
|
|
441
|
+
const rejectedToolUIMessages = [];
|
|
435
442
|
for (const toolCall of sensitiveTools) {
|
|
436
443
|
const rejectionMessage = {
|
|
437
444
|
role: 'tool',
|
|
438
445
|
tool_call_id: toolCall.id,
|
|
439
|
-
content:
|
|
446
|
+
content: rejectMessage,
|
|
440
447
|
};
|
|
441
448
|
conversationMessages.push(rejectionMessage);
|
|
442
449
|
saveMessage(rejectionMessage).catch(error => {
|
|
443
450
|
console.error('Failed to save tool rejection message:', error);
|
|
444
451
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
452
|
+
// Add UI message for each rejected tool
|
|
453
|
+
const toolDisplay = formatToolCallMessage(toolCall);
|
|
454
|
+
const statusIcon = '✗';
|
|
455
|
+
let statusText = '';
|
|
456
|
+
if (typeof confirmation === 'object' && confirmation.reason) {
|
|
457
|
+
statusText = `\n └─ Rejection reason: ${confirmation.reason}`;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
statusText = `\n └─ ${rejectMessage}`;
|
|
461
|
+
}
|
|
462
|
+
rejectedToolUIMessages.push({
|
|
449
463
|
role: 'assistant',
|
|
450
|
-
content:
|
|
464
|
+
content: `${statusIcon} ${toolDisplay.toolName}${statusText}`,
|
|
451
465
|
streaming: false,
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
// Add rejected tool messages to UI
|
|
469
|
+
if (rejectedToolUIMessages.length > 0) {
|
|
470
|
+
setMessages(prev => [...prev, ...rejectedToolUIMessages]);
|
|
471
|
+
}
|
|
472
|
+
// If reject_with_reply, continue the conversation instead of ending
|
|
473
|
+
if (typeof confirmation === 'object' &&
|
|
474
|
+
confirmation.type === 'reject_with_reply') {
|
|
475
|
+
// Continue to next iteration - AI will see the rejection message and respond
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
// Original reject behavior - end session
|
|
480
|
+
setMessages(prev => [
|
|
481
|
+
...prev,
|
|
482
|
+
{
|
|
483
|
+
role: 'assistant',
|
|
484
|
+
content: 'Tool call rejected, session ended',
|
|
485
|
+
streaming: false,
|
|
486
|
+
},
|
|
487
|
+
]);
|
|
488
|
+
if (options.setIsStreaming) {
|
|
489
|
+
options.setIsStreaming(false);
|
|
490
|
+
}
|
|
491
|
+
freeEncoder();
|
|
492
|
+
return { usage: accumulatedUsage };
|
|
456
493
|
}
|
|
457
|
-
freeEncoder();
|
|
458
|
-
return { usage: accumulatedUsage };
|
|
459
494
|
}
|
|
460
495
|
// Approved, add sensitive tools to approved list
|
|
461
496
|
approvedTools.push(...sensitiveTools);
|
|
@@ -467,32 +502,67 @@ export async function handleConversationWithTools(options) {
|
|
|
467
502
|
? toolsNeedingConfirmation
|
|
468
503
|
: undefined;
|
|
469
504
|
const confirmation = await requestToolConfirmation(firstTool, undefined, allTools);
|
|
470
|
-
if (confirmation === 'reject'
|
|
505
|
+
if (confirmation === 'reject' ||
|
|
506
|
+
(typeof confirmation === 'object' &&
|
|
507
|
+
confirmation.type === 'reject_with_reply')) {
|
|
471
508
|
setMessages(prev => prev.filter(msg => !msg.toolPending));
|
|
509
|
+
const rejectMessage = typeof confirmation === 'object'
|
|
510
|
+
? `Tool execution rejected by user: ${confirmation.reason}`
|
|
511
|
+
: 'Error: Tool execution rejected by user';
|
|
512
|
+
// Create UI messages for rejected tools
|
|
513
|
+
const rejectedToolUIMessages = [];
|
|
472
514
|
for (const toolCall of toolsNeedingConfirmation) {
|
|
473
515
|
const rejectionMessage = {
|
|
474
516
|
role: 'tool',
|
|
475
517
|
tool_call_id: toolCall.id,
|
|
476
|
-
content:
|
|
518
|
+
content: rejectMessage,
|
|
477
519
|
};
|
|
478
520
|
conversationMessages.push(rejectionMessage);
|
|
479
521
|
saveMessage(rejectionMessage).catch(error => {
|
|
480
522
|
console.error('Failed to save tool rejection message:', error);
|
|
481
523
|
});
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
524
|
+
// Add UI message for each rejected tool
|
|
525
|
+
const toolDisplay = formatToolCallMessage(toolCall);
|
|
526
|
+
const statusIcon = '✗';
|
|
527
|
+
let statusText = '';
|
|
528
|
+
if (typeof confirmation === 'object' && confirmation.reason) {
|
|
529
|
+
statusText = `\n └─ Rejection reason: ${confirmation.reason}`;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
statusText = `\n └─ ${rejectMessage}`;
|
|
533
|
+
}
|
|
534
|
+
rejectedToolUIMessages.push({
|
|
486
535
|
role: 'assistant',
|
|
487
|
-
content:
|
|
536
|
+
content: `${statusIcon} ${toolDisplay.toolName}${statusText}`,
|
|
488
537
|
streaming: false,
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// Add rejected tool messages to UI
|
|
541
|
+
if (rejectedToolUIMessages.length > 0) {
|
|
542
|
+
setMessages(prev => [...prev, ...rejectedToolUIMessages]);
|
|
543
|
+
}
|
|
544
|
+
// If reject_with_reply, continue the conversation instead of ending
|
|
545
|
+
if (typeof confirmation === 'object' &&
|
|
546
|
+
confirmation.type === 'reject_with_reply') {
|
|
547
|
+
// Continue to next iteration - AI will see the rejection message and respond
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
// Original reject behavior - end session
|
|
552
|
+
setMessages(prev => [
|
|
553
|
+
...prev,
|
|
554
|
+
{
|
|
555
|
+
role: 'assistant',
|
|
556
|
+
content: 'Tool call rejected, session ended',
|
|
557
|
+
streaming: false,
|
|
558
|
+
},
|
|
559
|
+
]);
|
|
560
|
+
if (options.setIsStreaming) {
|
|
561
|
+
options.setIsStreaming(false);
|
|
562
|
+
}
|
|
563
|
+
freeEncoder();
|
|
564
|
+
return { usage: accumulatedUsage };
|
|
493
565
|
}
|
|
494
|
-
freeEncoder();
|
|
495
|
-
return { usage: accumulatedUsage };
|
|
496
566
|
}
|
|
497
567
|
// If approved_always, add ALL these tools to both global and session-approved sets
|
|
498
568
|
if (confirmation === 'approve_always') {
|
|
@@ -535,7 +605,7 @@ export async function handleConversationWithTools(options) {
|
|
|
535
605
|
name: toolCall.function.name,
|
|
536
606
|
arguments: toolArgs,
|
|
537
607
|
},
|
|
538
|
-
toolDisplay
|
|
608
|
+
// Don't include toolDisplay for sub-agent tools to avoid showing parameters
|
|
539
609
|
toolCallId: toolCall.id,
|
|
540
610
|
toolPending: true,
|
|
541
611
|
subAgent: {
|
|
@@ -711,7 +781,9 @@ export async function handleConversationWithTools(options) {
|
|
|
711
781
|
break;
|
|
712
782
|
}
|
|
713
783
|
// 在工具执行完成后、发送结果到AI前,检查是否需要压缩
|
|
714
|
-
|
|
784
|
+
const config = getOpenAiConfig();
|
|
785
|
+
if (config.enableAutoCompress !== false &&
|
|
786
|
+
options.getCurrentContextPercentage &&
|
|
715
787
|
shouldAutoCompress(options.getCurrentContextPercentage())) {
|
|
716
788
|
try {
|
|
717
789
|
// 显示压缩提示消息
|
|
@@ -763,12 +835,24 @@ export async function handleConversationWithTools(options) {
|
|
|
763
835
|
const isError = result.content.startsWith('Error:');
|
|
764
836
|
const statusIcon = isError ? '✗' : '✓';
|
|
765
837
|
const statusText = isError ? `\n └─ ${result.content}` : '';
|
|
838
|
+
// Parse sub-agent result to extract usage information
|
|
839
|
+
let usage = undefined;
|
|
840
|
+
if (!isError) {
|
|
841
|
+
try {
|
|
842
|
+
const subAgentResult = JSON.parse(result.content);
|
|
843
|
+
usage = subAgentResult.usage;
|
|
844
|
+
}
|
|
845
|
+
catch (e) {
|
|
846
|
+
// Ignore parsing errors
|
|
847
|
+
}
|
|
848
|
+
}
|
|
766
849
|
resultMessages.push({
|
|
767
850
|
role: 'assistant',
|
|
768
851
|
content: `${statusIcon} ${toolCall.function.name}${statusText}`,
|
|
769
852
|
streaming: false,
|
|
770
853
|
// Pass the full result.content for ToolResultPreview to parse
|
|
771
854
|
toolResult: !isError ? result.content : undefined,
|
|
855
|
+
subAgentUsage: usage,
|
|
772
856
|
});
|
|
773
857
|
// Save the tool result to conversation history
|
|
774
858
|
conversationMessages.push(result);
|
|
@@ -852,7 +936,9 @@ export async function handleConversationWithTools(options) {
|
|
|
852
936
|
const pendingMessages = options.getPendingMessages();
|
|
853
937
|
if (pendingMessages.length > 0) {
|
|
854
938
|
// 检查 token 占用,如果 >= 80% 先执行自动压缩
|
|
855
|
-
|
|
939
|
+
const config = getOpenAiConfig();
|
|
940
|
+
if (config.enableAutoCompress !== false &&
|
|
941
|
+
options.getCurrentContextPercentage &&
|
|
856
942
|
shouldAutoCompress(options.getCurrentContextPercentage())) {
|
|
857
943
|
try {
|
|
858
944
|
// 显示压缩提示消息
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useInput } from 'ink';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
import { useI18n } from '../i18n/index.js';
|
|
3
4
|
export function useGlobalExit(onNotification) {
|
|
5
|
+
const { t } = useI18n();
|
|
4
6
|
const [lastCtrlCTime, setLastCtrlCTime] = useState(0);
|
|
5
7
|
const ctrlCTimeout = 1000; // 1 second timeout for double Ctrl+C
|
|
6
8
|
useInput((input, key) => {
|
|
@@ -16,13 +18,13 @@ export function useGlobalExit(onNotification) {
|
|
|
16
18
|
if (onNotification) {
|
|
17
19
|
onNotification({
|
|
18
20
|
show: true,
|
|
19
|
-
message:
|
|
21
|
+
message: t.hooks.pressCtrlCAgain,
|
|
20
22
|
});
|
|
21
23
|
// Hide notification after timeout
|
|
22
24
|
setTimeout(() => {
|
|
23
25
|
onNotification({
|
|
24
26
|
show: false,
|
|
25
|
-
message: ''
|
|
27
|
+
message: '',
|
|
26
28
|
});
|
|
27
29
|
}, ctrlCTimeout);
|
|
28
30
|
}
|
|
@@ -6,6 +6,13 @@ export type RetryStatus = {
|
|
|
6
6
|
remainingSeconds?: number;
|
|
7
7
|
errorMessage?: string;
|
|
8
8
|
};
|
|
9
|
+
export type CodebaseSearchStatus = {
|
|
10
|
+
isSearching: boolean;
|
|
11
|
+
attempt: number;
|
|
12
|
+
maxAttempts: number;
|
|
13
|
+
currentTopN: number;
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
9
16
|
export declare function useStreamingState(): {
|
|
10
17
|
isStreaming: boolean;
|
|
11
18
|
setIsStreaming: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
@@ -21,4 +28,6 @@ export declare function useStreamingState(): {
|
|
|
21
28
|
retryStatus: RetryStatus | null;
|
|
22
29
|
setRetryStatus: import("react").Dispatch<import("react").SetStateAction<RetryStatus | null>>;
|
|
23
30
|
animationFrame: number;
|
|
31
|
+
codebaseSearchStatus: CodebaseSearchStatus | null;
|
|
32
|
+
setCodebaseSearchStatus: import("react").Dispatch<import("react").SetStateAction<CodebaseSearchStatus | null>>;
|
|
24
33
|
};
|
|
@@ -9,6 +9,7 @@ export function useStreamingState() {
|
|
|
9
9
|
const [timerStartTime, setTimerStartTime] = useState(null);
|
|
10
10
|
const [retryStatus, setRetryStatus] = useState(null);
|
|
11
11
|
const [animationFrame, setAnimationFrame] = useState(0);
|
|
12
|
+
const [codebaseSearchStatus, setCodebaseSearchStatus] = useState(null);
|
|
12
13
|
// Animation for streaming/saving indicator
|
|
13
14
|
useEffect(() => {
|
|
14
15
|
if (!isStreaming)
|
|
@@ -98,5 +99,7 @@ export function useStreamingState() {
|
|
|
98
99
|
retryStatus,
|
|
99
100
|
setRetryStatus,
|
|
100
101
|
animationFrame,
|
|
102
|
+
codebaseSearchStatus,
|
|
103
|
+
setCodebaseSearchStatus,
|
|
101
104
|
};
|
|
102
105
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import type { Language, TranslationKeys } from './types.js';
|
|
3
|
+
type I18nContextType = {
|
|
4
|
+
language: Language;
|
|
5
|
+
setLanguage: (lang: Language) => void;
|
|
6
|
+
t: TranslationKeys;
|
|
7
|
+
};
|
|
8
|
+
type Props = {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
defaultLanguage?: Language;
|
|
11
|
+
};
|
|
12
|
+
export declare function I18nProvider({ children, defaultLanguage }: Props): React.JSX.Element;
|
|
13
|
+
export declare function useI18n(): I18nContextType;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { createContext, useState, useCallback } from 'react';
|
|
2
|
+
import { translations } from './translations.js';
|
|
3
|
+
import { getCurrentLanguage, setCurrentLanguage, } from '../utils/languageConfig.js';
|
|
4
|
+
const I18nContext = createContext(undefined);
|
|
5
|
+
export function I18nProvider({ children, defaultLanguage }) {
|
|
6
|
+
// Load saved language on mount or use default
|
|
7
|
+
const [language, setLanguageState] = useState(() => {
|
|
8
|
+
return defaultLanguage || getCurrentLanguage();
|
|
9
|
+
});
|
|
10
|
+
const setLanguage = useCallback((lang) => {
|
|
11
|
+
setLanguageState(lang);
|
|
12
|
+
setCurrentLanguage(lang); // Persist to file system
|
|
13
|
+
}, []);
|
|
14
|
+
// Get translations for current language
|
|
15
|
+
const t = translations[language];
|
|
16
|
+
return (React.createElement(I18nContext.Provider, { value: { language, setLanguage, t } }, children));
|
|
17
|
+
}
|
|
18
|
+
export function useI18n() {
|
|
19
|
+
const context = React.useContext(I18nContext);
|
|
20
|
+
if (!context) {
|
|
21
|
+
throw new Error('useI18n must be used within I18nProvider');
|
|
22
|
+
}
|
|
23
|
+
return context;
|
|
24
|
+
}
|