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.
Files changed (97) hide show
  1. package/dist/agents/codebaseIndexAgent.js +1 -0
  2. package/dist/agents/codebaseReviewAgent.d.ts +61 -0
  3. package/dist/agents/codebaseReviewAgent.js +301 -0
  4. package/dist/agents/promptOptimizeAgent.d.ts +54 -0
  5. package/dist/agents/promptOptimizeAgent.js +268 -0
  6. package/dist/api/anthropic.js +1 -0
  7. package/dist/api/chat.js +1 -0
  8. package/dist/api/embedding.js +1 -0
  9. package/dist/api/gemini.js +2 -1
  10. package/dist/api/responses.js +1 -0
  11. package/dist/api/systemPrompt.d.ts +1 -5
  12. package/dist/api/systemPrompt.js +168 -100
  13. package/dist/app.js +14 -6
  14. package/dist/cli.js +1 -1
  15. package/dist/hooks/useCommandPanel.js +48 -46
  16. package/dist/hooks/useConversation.d.ts +2 -1
  17. package/dist/hooks/useConversation.js +116 -30
  18. package/dist/hooks/useGlobalExit.js +4 -2
  19. package/dist/hooks/useStreamingState.d.ts +9 -0
  20. package/dist/hooks/useStreamingState.js +3 -0
  21. package/dist/i18n/I18nContext.d.ts +14 -0
  22. package/dist/i18n/I18nContext.js +24 -0
  23. package/dist/i18n/index.d.ts +3 -0
  24. package/dist/i18n/index.js +2 -0
  25. package/dist/i18n/lang/en.d.ts +2 -0
  26. package/dist/i18n/lang/en.js +483 -0
  27. package/dist/i18n/lang/es.d.ts +2 -0
  28. package/dist/i18n/lang/es.js +483 -0
  29. package/dist/i18n/lang/ja.d.ts +2 -0
  30. package/dist/i18n/lang/ja.js +483 -0
  31. package/dist/i18n/lang/ko.d.ts +2 -0
  32. package/dist/i18n/lang/ko.js +483 -0
  33. package/dist/i18n/lang/zh-TW.d.ts +2 -0
  34. package/dist/i18n/lang/zh-TW.js +483 -0
  35. package/dist/i18n/lang/zh.d.ts +2 -0
  36. package/dist/i18n/lang/zh.js +483 -0
  37. package/dist/i18n/translations.d.ts +2 -0
  38. package/dist/i18n/translations.js +14 -0
  39. package/dist/i18n/types.d.ts +459 -0
  40. package/dist/i18n/types.js +1 -0
  41. package/dist/mcp/aceCodeSearch.d.ts +17 -48
  42. package/dist/mcp/aceCodeSearch.js +24 -56
  43. package/dist/mcp/bash.js +8 -1
  44. package/dist/mcp/codebaseSearch.d.ts +1 -1
  45. package/dist/mcp/codebaseSearch.js +159 -30
  46. package/dist/mcp/filesystem.d.ts +3 -80
  47. package/dist/mcp/filesystem.js +23 -103
  48. package/dist/mcp/subagent.d.ts +2 -1
  49. package/dist/mcp/subagent.js +54 -5
  50. package/dist/ui/components/ChatInput.js +22 -25
  51. package/dist/ui/components/CommandPanel.d.ts +1 -1
  52. package/dist/ui/components/CommandPanel.js +20 -13
  53. package/dist/ui/components/DiffViewer.d.ts +1 -1
  54. package/dist/ui/components/DiffViewer.js +101 -91
  55. package/dist/ui/components/FileList.js +22 -11
  56. package/dist/ui/components/HelpPanel.js +47 -21
  57. package/dist/ui/components/Menu.js +6 -2
  58. package/dist/ui/components/MessageList.d.ts +6 -0
  59. package/dist/ui/components/MessageList.js +1 -1
  60. package/dist/ui/components/ToolConfirmation.d.ts +4 -1
  61. package/dist/ui/components/ToolConfirmation.js +28 -2
  62. package/dist/ui/components/ToolResultPreview.d.ts +2 -1
  63. package/dist/ui/components/ToolResultPreview.js +41 -25
  64. package/dist/ui/pages/ChatScreen.js +177 -56
  65. package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
  66. package/dist/ui/pages/ConfigScreen.js +138 -98
  67. package/dist/ui/pages/CustomHeadersScreen.js +75 -69
  68. package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
  69. package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
  70. package/dist/ui/pages/ProxyConfigScreen.js +27 -23
  71. package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
  72. package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
  73. package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
  74. package/dist/ui/pages/WelcomeScreen.js +40 -26
  75. package/dist/utils/apiConfig.d.ts +2 -0
  76. package/dist/utils/codebaseConfig.d.ts +1 -5
  77. package/dist/utils/codebaseConfig.js +2 -10
  78. package/dist/utils/codebaseSearchEvents.d.ts +16 -0
  79. package/dist/utils/codebaseSearchEvents.js +13 -0
  80. package/dist/utils/commands/agent.js +2 -2
  81. package/dist/utils/commands/init.js +1 -1
  82. package/dist/utils/configManager.js +26 -5
  83. package/dist/utils/contextCompressor.js +1 -1
  84. package/dist/utils/languageConfig.d.ts +21 -0
  85. package/dist/utils/languageConfig.js +61 -0
  86. package/dist/utils/mcpToolsManager.js +0 -9
  87. package/dist/utils/notebookManager.js +11 -4
  88. package/dist/utils/sessionConverter.js +13 -3
  89. package/dist/utils/sessionManager.d.ts +1 -0
  90. package/dist/utils/subAgentConfig.d.ts +10 -5
  91. package/dist/utils/subAgentConfig.js +112 -19
  92. package/dist/utils/subAgentExecutor.d.ts +9 -1
  93. package/dist/utils/subAgentExecutor.js +122 -9
  94. package/dist/utils/toolExecutor.d.ts +2 -1
  95. package/dist/utils/toolExecutor.js +1 -2
  96. package/dist/utils/usageLogger.js +18 -3
  97. package/package.json +2 -1
@@ -619,6 +619,7 @@ Object.defineProperty(CodebaseIndexAgent, "CODE_EXTENSIONS", {
619
619
  '.kt',
620
620
  '.scala',
621
621
  '.m',
622
+ '.md',
622
623
  '.mm',
623
624
  '.sh',
624
625
  '.bash',
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Codebase Review Agent Service
3
+ *
4
+ * Reviews codebase search results to filter out irrelevant items.
5
+ * Uses basicModel for efficient, low-cost relevance checking.
6
+ * Can also suggest better search keywords if results are not relevant.
7
+ */
8
+ export declare class CodebaseReviewAgent {
9
+ private modelName;
10
+ private requestMethod;
11
+ private initialized;
12
+ private readonly MAX_RETRIES;
13
+ /**
14
+ * Initialize the review agent with current configuration
15
+ */
16
+ private initialize;
17
+ /**
18
+ * Check if review agent is available
19
+ */
20
+ isAvailable(): Promise<boolean>;
21
+ /**
22
+ * Call the model with streaming API and assemble complete response
23
+ */
24
+ private callModel;
25
+ /**
26
+ * Try to parse JSON response with retry logic
27
+ */
28
+ private tryParseJSON;
29
+ /**
30
+ * Review search results with retry mechanism
31
+ */
32
+ private reviewWithRetry;
33
+ /**
34
+ * Sleep utility for retry backoff
35
+ */
36
+ private sleep;
37
+ /**
38
+ * Review search results and filter out irrelevant ones
39
+ * With retry mechanism and graceful degradation
40
+ *
41
+ * @param query - Original search query
42
+ * @param results - Search results to review
43
+ * @param abortSignal - Optional abort signal
44
+ * @returns Object with filtered results and optional suggestions
45
+ */
46
+ reviewResults(query: string, results: Array<{
47
+ rank: number;
48
+ filePath: string;
49
+ startLine: number;
50
+ endLine: number;
51
+ content: string;
52
+ similarityScore: string;
53
+ location: string;
54
+ }>, abortSignal?: AbortSignal): Promise<{
55
+ filteredResults: typeof results;
56
+ removedCount: number;
57
+ suggestions?: string[];
58
+ reviewFailed?: boolean;
59
+ }>;
60
+ }
61
+ export declare const codebaseReviewAgent: CodebaseReviewAgent;
@@ -0,0 +1,301 @@
1
+ import { getOpenAiConfig } from '../utils/apiConfig.js';
2
+ import { logger } from '../utils/logger.js';
3
+ import { createStreamingChatCompletion } from '../api/chat.js';
4
+ import { createStreamingResponse } from '../api/responses.js';
5
+ import { createStreamingGeminiCompletion } from '../api/gemini.js';
6
+ import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
7
+ /**
8
+ * Codebase Review Agent Service
9
+ *
10
+ * Reviews codebase search results to filter out irrelevant items.
11
+ * Uses basicModel for efficient, low-cost relevance checking.
12
+ * Can also suggest better search keywords if results are not relevant.
13
+ */
14
+ export class CodebaseReviewAgent {
15
+ constructor() {
16
+ Object.defineProperty(this, "modelName", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: ''
21
+ });
22
+ Object.defineProperty(this, "requestMethod", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: 'chat'
27
+ });
28
+ Object.defineProperty(this, "initialized", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: false
33
+ });
34
+ Object.defineProperty(this, "MAX_RETRIES", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: 3
39
+ });
40
+ }
41
+ /**
42
+ * Initialize the review agent with current configuration
43
+ */
44
+ async initialize() {
45
+ try {
46
+ const config = getOpenAiConfig();
47
+ if (!config.basicModel) {
48
+ logger.warn('Codebase review agent: Basic model not configured, using advanced model as fallback');
49
+ if (!config.advancedModel) {
50
+ logger.warn('Codebase review agent: No model configured');
51
+ return false;
52
+ }
53
+ this.modelName = config.advancedModel;
54
+ }
55
+ else {
56
+ this.modelName = config.basicModel;
57
+ }
58
+ this.requestMethod = config.requestMethod;
59
+ this.initialized = true;
60
+ return true;
61
+ }
62
+ catch (error) {
63
+ logger.warn('Codebase review agent: Failed to initialize:', error);
64
+ return false;
65
+ }
66
+ }
67
+ /**
68
+ * Check if review agent is available
69
+ */
70
+ async isAvailable() {
71
+ if (!this.initialized) {
72
+ return await this.initialize();
73
+ }
74
+ return true;
75
+ }
76
+ /**
77
+ * Call the model with streaming API and assemble complete response
78
+ */
79
+ async callModel(messages, abortSignal) {
80
+ let streamGenerator;
81
+ switch (this.requestMethod) {
82
+ case 'anthropic':
83
+ streamGenerator = createStreamingAnthropicCompletion({
84
+ model: this.modelName,
85
+ messages,
86
+ includeBuiltinSystemPrompt: false,
87
+ disableThinking: true,
88
+ }, abortSignal);
89
+ break;
90
+ case 'gemini':
91
+ streamGenerator = createStreamingGeminiCompletion({
92
+ model: this.modelName,
93
+ messages,
94
+ includeBuiltinSystemPrompt: false,
95
+ }, abortSignal);
96
+ break;
97
+ case 'responses':
98
+ streamGenerator = createStreamingResponse({
99
+ model: this.modelName,
100
+ messages,
101
+ stream: true,
102
+ includeBuiltinSystemPrompt: false,
103
+ }, abortSignal);
104
+ break;
105
+ case 'chat':
106
+ default:
107
+ streamGenerator = createStreamingChatCompletion({
108
+ model: this.modelName,
109
+ messages,
110
+ stream: true,
111
+ includeBuiltinSystemPrompt: false,
112
+ }, abortSignal);
113
+ break;
114
+ }
115
+ let completeContent = '';
116
+ try {
117
+ for await (const chunk of streamGenerator) {
118
+ if (abortSignal?.aborted) {
119
+ throw new Error('Request aborted');
120
+ }
121
+ if (this.requestMethod === 'chat') {
122
+ if (chunk.choices && chunk.choices[0]?.delta?.content) {
123
+ completeContent += chunk.choices[0].delta.content;
124
+ }
125
+ }
126
+ else {
127
+ if (chunk.type === 'content' && chunk.content) {
128
+ completeContent += chunk.content;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ catch (streamError) {
134
+ logger.error('Codebase review agent: Streaming error:', streamError);
135
+ throw streamError;
136
+ }
137
+ return completeContent;
138
+ }
139
+ /**
140
+ * Try to parse JSON response with retry logic
141
+ */
142
+ tryParseJSON(response) {
143
+ try {
144
+ // Extract JSON from markdown code blocks if present
145
+ let jsonStr = response.trim();
146
+ const jsonMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
147
+ if (jsonMatch) {
148
+ jsonStr = jsonMatch[1].trim();
149
+ }
150
+ const parsed = JSON.parse(jsonStr);
151
+ // Validate structure
152
+ if (!Array.isArray(parsed.relevantIndices)) {
153
+ logger.warn('Codebase review agent: Invalid JSON structure - missing relevantIndices array');
154
+ return null;
155
+ }
156
+ return parsed;
157
+ }
158
+ catch (error) {
159
+ logger.warn('Codebase review agent: JSON parse error:', error);
160
+ return null;
161
+ }
162
+ }
163
+ /**
164
+ * Review search results with retry mechanism
165
+ */
166
+ async reviewWithRetry(query, results, abortSignal) {
167
+ const reviewPrompt = `You are a code search result reviewer. Your task is to analyze search results and determine which ones are truly relevant to the user's query.
168
+
169
+ Search Query: "${query}"
170
+
171
+ Search Results (${results.length} items):
172
+ ${results
173
+ .map((r, idx) => `\n[Result ${idx + 1}]
174
+ File: ${r.filePath}
175
+ Lines: ${r.startLine}-${r.endLine}
176
+ Similarity Score: ${r.similarityScore}%
177
+ Code:
178
+ \`\`\`
179
+ ${r.content}
180
+ \`\`\``)
181
+ .join('\n---')}
182
+
183
+ Your Tasks:
184
+ 1. Identify which results are RELEVANT to the search query
185
+ 2. Mark irrelevant results for removal
186
+ 3. If most results are irrelevant, suggest better search keywords
187
+
188
+ Output in JSON format:
189
+ {
190
+ "relevantIndices": [1, 3, 5],
191
+ "removedIndices": [2, 4],
192
+ "suggestions": ["keyword1", "keyword2"]
193
+ }
194
+
195
+ Guidelines:
196
+ - Be strict but fair: code doesn't need to match exactly, but should be semantically related
197
+ - Consider file paths, code content, and context
198
+ - If a result is marginally relevant, keep it
199
+ - Only suggest new keywords if >50% of results are irrelevant
200
+ - Return ONLY valid JSON, no other text or explanation`;
201
+ const messages = [
202
+ {
203
+ role: 'user',
204
+ content: reviewPrompt,
205
+ },
206
+ ];
207
+ // Retry loop
208
+ for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
209
+ try {
210
+ logger.info(`Codebase review agent: Attempt ${attempt}/${this.MAX_RETRIES}`);
211
+ const response = await this.callModel(messages, abortSignal);
212
+ if (!response || response.trim().length === 0) {
213
+ logger.warn(`Codebase review agent: Empty response on attempt ${attempt}`);
214
+ if (attempt < this.MAX_RETRIES) {
215
+ await this.sleep(500 * attempt); // Exponential backoff
216
+ continue;
217
+ }
218
+ return null;
219
+ }
220
+ // Try to parse JSON
221
+ const parsed = this.tryParseJSON(response);
222
+ if (parsed) {
223
+ logger.info(`Codebase review agent: Successfully parsed on attempt ${attempt}`);
224
+ return { parsed, attempt };
225
+ }
226
+ // If parse failed and we have retries left
227
+ if (attempt < this.MAX_RETRIES) {
228
+ logger.warn(`Codebase review agent: Parse failed on attempt ${attempt}, retrying...`);
229
+ await this.sleep(500 * attempt); // Exponential backoff
230
+ continue;
231
+ }
232
+ return null;
233
+ }
234
+ catch (error) {
235
+ logger.error(`Codebase review agent: Error on attempt ${attempt}:`, error);
236
+ if (attempt < this.MAX_RETRIES) {
237
+ await this.sleep(500 * attempt); // Exponential backoff
238
+ continue;
239
+ }
240
+ return null;
241
+ }
242
+ }
243
+ return null;
244
+ }
245
+ /**
246
+ * Sleep utility for retry backoff
247
+ */
248
+ sleep(ms) {
249
+ return new Promise(resolve => setTimeout(resolve, ms));
250
+ }
251
+ /**
252
+ * Review search results and filter out irrelevant ones
253
+ * With retry mechanism and graceful degradation
254
+ *
255
+ * @param query - Original search query
256
+ * @param results - Search results to review
257
+ * @param abortSignal - Optional abort signal
258
+ * @returns Object with filtered results and optional suggestions
259
+ */
260
+ async reviewResults(query, results, abortSignal) {
261
+ const available = await this.isAvailable();
262
+ if (!available) {
263
+ logger.warn('Codebase review agent: Not available, returning original results');
264
+ return {
265
+ filteredResults: results,
266
+ removedCount: 0,
267
+ reviewFailed: true,
268
+ };
269
+ }
270
+ // Attempt review with retry
271
+ const reviewResult = await this.reviewWithRetry(query, results, abortSignal);
272
+ // If all retries failed, gracefully degrade
273
+ if (!reviewResult) {
274
+ logger.warn('Codebase review agent: All retry attempts failed, returning original results');
275
+ return {
276
+ filteredResults: results,
277
+ removedCount: 0,
278
+ reviewFailed: true,
279
+ };
280
+ }
281
+ // Success - filter results
282
+ const { parsed, attempt } = reviewResult;
283
+ const filteredResults = results.filter((_, idx) => parsed.relevantIndices.includes(idx + 1));
284
+ const removedCount = results.length - filteredResults.length;
285
+ logger.info('Codebase review agent: Review completed', {
286
+ originalCount: results.length,
287
+ filteredCount: filteredResults.length,
288
+ removedCount,
289
+ attempts: attempt,
290
+ hasSuggestions: !!parsed.suggestions?.length,
291
+ });
292
+ return {
293
+ filteredResults,
294
+ removedCount,
295
+ suggestions: parsed.suggestions || undefined,
296
+ reviewFailed: false,
297
+ };
298
+ }
299
+ }
300
+ // Export singleton instance
301
+ export const codebaseReviewAgent = new CodebaseReviewAgent();
@@ -0,0 +1,54 @@
1
+ import { type ChatMessage } from '../api/chat.js';
2
+ /**
3
+ * Prompt Optimization Agent Service
4
+ *
5
+ * Optimizes user prompts for better AI understanding and response quality.
6
+ * This service operates using the basic model for efficient, low-cost optimization.
7
+ *
8
+ * Features:
9
+ * - Uses basicModel for efficient prompt optimization
10
+ * - Follows the same API routing as main flow (chat, responses, gemini, anthropic)
11
+ * - Filters context to only include user->assistant pairs without tool calls
12
+ * - Returns optimized prompt that preserves user intent while improving clarity
13
+ * - Silent execution with error handling to prevent main flow disruption
14
+ */
15
+ export declare class PromptOptimizeAgent {
16
+ private modelName;
17
+ private requestMethod;
18
+ private initialized;
19
+ /**
20
+ * Initialize the prompt optimization agent with current configuration
21
+ * @returns true if initialized successfully, false otherwise
22
+ */
23
+ private initialize;
24
+ /**
25
+ * Check if prompt optimization agent is available
26
+ */
27
+ isAvailable(): Promise<boolean>;
28
+ /**
29
+ * Call the model with streaming API and assemble complete response
30
+ * Uses the same routing logic as main flow for consistency
31
+ *
32
+ * @param messages - Chat messages
33
+ * @param abortSignal - Optional abort signal to cancel the request
34
+ */
35
+ private callModel;
36
+ /**
37
+ * Filter conversation history to only include user->assistant pairs without tool calls
38
+ * This creates a lightweight context for prompt optimization
39
+ *
40
+ * @param messages - Full conversation history
41
+ * @returns Filtered messages containing only user->assistant exchanges
42
+ */
43
+ private filterContextMessages;
44
+ /**
45
+ * Optimize user prompt for better AI understanding
46
+ *
47
+ * @param userPrompt - Original user prompt
48
+ * @param conversationHistory - Full conversation history for context
49
+ * @param abortSignal - Optional abort signal to cancel optimization
50
+ * @returns Optimized prompt, or original prompt if optimization fails
51
+ */
52
+ optimizePrompt(userPrompt: string, conversationHistory: ChatMessage[], abortSignal?: AbortSignal): Promise<string>;
53
+ }
54
+ export declare const promptOptimizeAgent: PromptOptimizeAgent;
@@ -0,0 +1,268 @@
1
+ import { getOpenAiConfig } from '../utils/apiConfig.js';
2
+ import { logger } from '../utils/logger.js';
3
+ import { createStreamingChatCompletion } from '../api/chat.js';
4
+ import { createStreamingResponse } from '../api/responses.js';
5
+ import { createStreamingGeminiCompletion } from '../api/gemini.js';
6
+ import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
7
+ /**
8
+ * Prompt Optimization Agent Service
9
+ *
10
+ * Optimizes user prompts for better AI understanding and response quality.
11
+ * This service operates using the basic model for efficient, low-cost optimization.
12
+ *
13
+ * Features:
14
+ * - Uses basicModel for efficient prompt optimization
15
+ * - Follows the same API routing as main flow (chat, responses, gemini, anthropic)
16
+ * - Filters context to only include user->assistant pairs without tool calls
17
+ * - Returns optimized prompt that preserves user intent while improving clarity
18
+ * - Silent execution with error handling to prevent main flow disruption
19
+ */
20
+ export class PromptOptimizeAgent {
21
+ constructor() {
22
+ Object.defineProperty(this, "modelName", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: ''
27
+ });
28
+ Object.defineProperty(this, "requestMethod", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: 'chat'
33
+ });
34
+ Object.defineProperty(this, "initialized", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: false
39
+ });
40
+ }
41
+ /**
42
+ * Initialize the prompt optimization agent with current configuration
43
+ * @returns true if initialized successfully, false otherwise
44
+ */
45
+ async initialize() {
46
+ try {
47
+ const config = getOpenAiConfig();
48
+ // Check if basic model is configured
49
+ if (!config.basicModel) {
50
+ logger.warn('Prompt optimize agent: Basic model not configured');
51
+ return false;
52
+ }
53
+ this.modelName = config.basicModel;
54
+ this.requestMethod = config.requestMethod;
55
+ this.initialized = true;
56
+ return true;
57
+ }
58
+ catch (error) {
59
+ logger.warn('Prompt optimize agent: Failed to initialize:', error);
60
+ return false;
61
+ }
62
+ }
63
+ /**
64
+ * Check if prompt optimization agent is available
65
+ */
66
+ async isAvailable() {
67
+ if (!this.initialized) {
68
+ return await this.initialize();
69
+ }
70
+ return true;
71
+ }
72
+ /**
73
+ * Call the model with streaming API and assemble complete response
74
+ * Uses the same routing logic as main flow for consistency
75
+ *
76
+ * @param messages - Chat messages
77
+ * @param abortSignal - Optional abort signal to cancel the request
78
+ */
79
+ async callModel(messages, abortSignal) {
80
+ let streamGenerator;
81
+ // Route to appropriate streaming API based on request method
82
+ switch (this.requestMethod) {
83
+ case 'anthropic':
84
+ streamGenerator = createStreamingAnthropicCompletion({
85
+ model: this.modelName,
86
+ messages,
87
+ max_tokens: 1000, // Limited tokens for prompt optimization
88
+ includeBuiltinSystemPrompt: false,
89
+ disableThinking: true, // Agents don't use Extended Thinking
90
+ }, abortSignal);
91
+ break;
92
+ case 'gemini':
93
+ streamGenerator = createStreamingGeminiCompletion({
94
+ model: this.modelName,
95
+ messages,
96
+ includeBuiltinSystemPrompt: false,
97
+ }, abortSignal);
98
+ break;
99
+ case 'responses':
100
+ streamGenerator = createStreamingResponse({
101
+ model: this.modelName,
102
+ messages,
103
+ stream: true,
104
+ includeBuiltinSystemPrompt: false,
105
+ }, abortSignal);
106
+ break;
107
+ case 'chat':
108
+ default:
109
+ streamGenerator = createStreamingChatCompletion({
110
+ model: this.modelName,
111
+ messages,
112
+ stream: true,
113
+ includeBuiltinSystemPrompt: false,
114
+ }, abortSignal);
115
+ break;
116
+ }
117
+ // Assemble complete content from streaming response
118
+ let completeContent = '';
119
+ try {
120
+ for await (const chunk of streamGenerator) {
121
+ // Check abort signal
122
+ if (abortSignal?.aborted) {
123
+ throw new Error('Request aborted');
124
+ }
125
+ // Handle different chunk formats based on request method
126
+ if (this.requestMethod === 'chat') {
127
+ // Chat API uses standard OpenAI format
128
+ if (chunk.choices && chunk.choices[0]?.delta?.content) {
129
+ completeContent += chunk.choices[0].delta.content;
130
+ }
131
+ }
132
+ else {
133
+ // Responses, Gemini, and Anthropic APIs use unified format
134
+ if (chunk.type === 'content' && chunk.content) {
135
+ completeContent += chunk.content;
136
+ }
137
+ }
138
+ }
139
+ }
140
+ catch (streamError) {
141
+ logger.error('Prompt optimize agent: Streaming error:', streamError);
142
+ throw streamError;
143
+ }
144
+ return completeContent;
145
+ }
146
+ /**
147
+ * Filter conversation history to only include user->assistant pairs without tool calls
148
+ * This creates a lightweight context for prompt optimization
149
+ *
150
+ * @param messages - Full conversation history
151
+ * @returns Filtered messages containing only user->assistant exchanges
152
+ */
153
+ filterContextMessages(messages) {
154
+ const filtered = [];
155
+ for (const msg of messages) {
156
+ // Only include user and assistant messages
157
+ if (msg.role === 'user' || msg.role === 'assistant') {
158
+ // For assistant messages, skip if they contain tool calls
159
+ if (msg.role === 'assistant') {
160
+ // Check if message has tool_calls (OpenAI format) or tool_use content (Anthropic format)
161
+ const hasToolCalls = !!msg.tool_calls;
162
+ const hasToolUseContent = Array.isArray(msg.content) &&
163
+ msg.content.some((c) => c.type === 'tool_use' || c.type === 'tool_call');
164
+ if (hasToolCalls || hasToolUseContent) {
165
+ continue; // Skip assistant messages with tool calls
166
+ }
167
+ }
168
+ // Add message to filtered list
169
+ filtered.push(msg);
170
+ }
171
+ }
172
+ return filtered;
173
+ }
174
+ /**
175
+ * Optimize user prompt for better AI understanding
176
+ *
177
+ * @param userPrompt - Original user prompt
178
+ * @param conversationHistory - Full conversation history for context
179
+ * @param abortSignal - Optional abort signal to cancel optimization
180
+ * @returns Optimized prompt, or original prompt if optimization fails
181
+ */
182
+ async optimizePrompt(userPrompt, conversationHistory, abortSignal) {
183
+ const available = await this.isAvailable();
184
+ if (!available) {
185
+ return userPrompt;
186
+ }
187
+ try {
188
+ // Check word count - if prompt > 100 words, skip optimization
189
+ // User likely provided detailed/important original text that should be preserved as-is
190
+ const wordCount = userPrompt.trim().split(/\s+/).length;
191
+ if (wordCount > 100) {
192
+ return userPrompt;
193
+ }
194
+ // Filter conversation history to lightweight context (only user<->assistant, no tool calls)
195
+ const contextMessages = this.filterContextMessages(conversationHistory);
196
+ // Build context summary if there's conversation history
197
+ let contextSummary = '';
198
+ if (contextMessages.length > 0) {
199
+ // Take last 8 messages to keep context focused, but use full content (no truncation)
200
+ const recentContext = contextMessages.slice(-8);
201
+ contextSummary =
202
+ '\n\nRecent conversation context:\n' +
203
+ recentContext
204
+ .map((msg) => {
205
+ const content = typeof msg.content === 'string'
206
+ ? msg.content
207
+ : JSON.stringify(msg.content);
208
+ // Use full message content (no truncation)
209
+ return `${msg.role}: ${content}`;
210
+ })
211
+ .join('\n');
212
+ }
213
+ const optimizationPrompt = `You are a prompt optimization assistant. Your task is to improve user prompts for better AI understanding while maintaining HIGH FIDELITY to the original content.
214
+
215
+ User's original prompt:
216
+ ${userPrompt}${contextSummary}
217
+
218
+ Your optimization goals (in priority order):
219
+ 1. **HIGH FIDELITY REQUIREMENT**: Preserve ALL important information, details, and requirements from the user's original prompt - DO NOT lose or omit any critical content
220
+ 2. Preserve the EXACT SAME LANGUAGE as the user (if Chinese, stay Chinese; if English, stay English)
221
+ 3. Keep the core intent and meaning unchanged
222
+ 4. Make the prompt clearer and more specific ONLY if vague - if already clear, keep it as-is
223
+ 5. Add relevant context if the user is asking follow-up questions
224
+ 6. Break down complex requests into clear requirements without losing details
225
+ 7. Keep the tone natural and conversational
226
+ 8. DO NOT add unnecessary formality or change the user's communication style
227
+ 9. If the prompt is already clear and specific, return it as-is
228
+
229
+ CRITICAL RULES:
230
+ - NEVER remove important details, specific requirements, file paths, code snippets, or technical specifications
231
+ - NEVER simplify the prompt if it means losing user-provided information
232
+ - When in doubt, prefer preserving the original over optimizing
233
+ - The goal is CLARITY, not BREVITY - keep all important content
234
+
235
+ IMPORTANT: Output ONLY the optimized prompt text. No explanations, no meta-commentary, no JSON format. Just the optimized prompt itself.`;
236
+ const messages = [
237
+ {
238
+ role: 'user',
239
+ content: optimizationPrompt,
240
+ },
241
+ ];
242
+ const optimizedPrompt = await this.callModel(messages, abortSignal);
243
+ if (!optimizedPrompt || optimizedPrompt.trim().length === 0) {
244
+ logger.warn('Prompt optimize agent: Empty response, using original prompt');
245
+ return userPrompt;
246
+ }
247
+ // Clean up the response (remove any markdown formatting if present)
248
+ let cleanedPrompt = optimizedPrompt.trim();
249
+ // Remove markdown code blocks if present
250
+ const codeBlockMatch = cleanedPrompt.match(/```[\s\S]*?\n([\s\S]*?)```/);
251
+ if (codeBlockMatch) {
252
+ cleanedPrompt = codeBlockMatch[1].trim();
253
+ }
254
+ // If optimized prompt is suspiciously short or looks like it failed, use original
255
+ if (cleanedPrompt.length < userPrompt.length * 0.3) {
256
+ logger.warn('Prompt optimize agent: Optimized prompt too short, using original');
257
+ return userPrompt;
258
+ }
259
+ return cleanedPrompt;
260
+ }
261
+ catch (error) {
262
+ logger.error('Prompt optimize agent: Failed to optimize prompt', error);
263
+ return userPrompt;
264
+ }
265
+ }
266
+ }
267
+ // Export singleton instance
268
+ export const promptOptimizeAgent = new PromptOptimizeAgent();