@pheem49/mint 1.4.2 → 1.5.1
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/GUIDE_TH.md +113 -0
- package/README.md +267 -78
- package/assets/CLI_Screen.png +0 -0
- package/main.js +76 -890
- package/mint-cli-logic.js +3 -107
- package/mint-cli.js +594 -29
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json +15 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
- package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +23 -0
- package/package.json +37 -4
- package/src/AI_Brain/Gemini_API.js +223 -65
- package/src/AI_Brain/autonomous_brain.js +11 -0
- package/src/AI_Brain/behavior_memory.js +26 -5
- package/src/AI_Brain/headless_agent.js +4 -0
- package/src/AI_Brain/knowledge_base.js +61 -8
- package/src/AI_Brain/memory_store.js +354 -10
- package/src/Automation_Layer/file_operations.js +1 -1
- package/src/CLI/chat_router.js +20 -7
- package/src/CLI/chat_ui.js +596 -825
- package/src/CLI/code_agent.js +347 -56
- package/src/CLI/gmail_auth.js +210 -0
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/list_features.js +2 -0
- package/src/CLI/onboarding.js +364 -55
- package/src/CLI/updater.js +210 -0
- package/src/Channels/brave_search_bridge.js +35 -0
- package/src/Channels/discord_bridge.js +68 -0
- package/src/Channels/google_search_bridge.js +38 -0
- package/src/Channels/line_bridge.js +60 -0
- package/src/Channels/slack_bridge.js +53 -0
- package/src/Channels/telegram_bridge.js +49 -0
- package/src/Channels/whatsapp_bridge.js +55 -0
- package/src/Command_Parser/parser.js +12 -1
- package/src/Plugins/gmail.js +251 -0
- package/src/Plugins/google_calendar.js +245 -19
- package/src/Plugins/notion.js +256 -0
- package/src/System/action_executor.js +178 -0
- package/src/System/bridge_manager.js +76 -0
- package/src/System/chat_history_manager.js +23 -5
- package/src/System/config_manager.js +71 -7
- package/src/System/custom_workflows.js +31 -2
- package/src/System/google_tts_urls.js +51 -0
- package/src/System/granular_automation.js +122 -53
- package/src/System/ipc_handlers.js +238 -0
- package/src/System/proactive_loop.js +153 -0
- package/src/System/safety_manager.js +273 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/screen_capture.js +175 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/task_manager.js +15 -5
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +212 -0
- package/src/UI/live2d_manager.js +368 -0
- package/src/UI/renderer.js +208 -24
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +14 -4
- package/src/UI/styles.css +466 -32
- package/.codex +0 -0
- package/docs/assets/Agent_Mint.png +0 -0
- package/docs/assets/CLI_Screen.png +0 -0
- package/docs/assets/Settings.png +0 -0
- package/docs/assets/icon.png +0 -0
- package/docs/index.html +0 -132
- package/docs/style.css +0 -579
- package/index.html +0 -16
- package/src/UI/index.html +0 -126
- package/tech_news.txt +0 -3
- package/test_knowledge.txt +0 -3
- package/tests/agent_orchestrator.test.js +0 -41
- package/tests/chat_router.test.js +0 -42
- package/tests/code_agent.test.js +0 -69
- package/tests/config_manager.test.js +0 -141
- package/tests/docker.test.js +0 -46
- package/tests/file_operations.test.js +0 -57
- package/tests/memory_store.test.js +0 -185
- package/tests/provider_routing.test.js +0 -67
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- package/tests/workspace_manager.test.js +0 -56
package/src/CLI/code_agent.js
CHANGED
|
@@ -6,11 +6,46 @@ const { GoogleGenAI } = require('@google/genai');
|
|
|
6
6
|
const axios = require('axios');
|
|
7
7
|
const cheerio = require('cheerio');
|
|
8
8
|
const { readConfig, getAvailableProviders } = require('../System/config_manager');
|
|
9
|
+
const safetyManager = require('../System/safety_manager');
|
|
10
|
+
const memoryStore = require('../AI_Brain/memory_store');
|
|
9
11
|
const { readWorkspaceSession, writeWorkspaceSession } = require('./code_session_memory');
|
|
10
|
-
const { executeAction } = require('
|
|
12
|
+
const { executeAction } = require('../System/action_executor');
|
|
13
|
+
const toolRegistry = require('../System/tool_registry');
|
|
14
|
+
const sandboxRunner = require('../System/sandbox_runner');
|
|
11
15
|
|
|
12
|
-
async function webSearch(query) {
|
|
16
|
+
async function webSearch(query, onProgress = () => {}) {
|
|
13
17
|
if (!query) throw new Error('Search query required.');
|
|
18
|
+
const config = readConfig();
|
|
19
|
+
|
|
20
|
+
// 1. Try Google Search API if configured
|
|
21
|
+
if (config.googleSearchApiKey && config.googleSearchCx) {
|
|
22
|
+
try {
|
|
23
|
+
const GoogleSearch = require('../Channels/google_search_bridge');
|
|
24
|
+
const google = new GoogleSearch({ apiKey: config.googleSearchApiKey, cx: config.googleSearchCx });
|
|
25
|
+
const results = await google.search(query);
|
|
26
|
+
if (results.length > 0) {
|
|
27
|
+
return results.map(r => `Title: ${r.title}\nSnippet: ${r.snippet}\nURL: ${r.link}`).join('\n\n');
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
onProgress({ phase: 'error', action: 'web_search', message: e.message });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 2. Try Brave Search API if configured
|
|
35
|
+
if (config.braveSearchApiKey) {
|
|
36
|
+
try {
|
|
37
|
+
const BraveSearch = require('../Channels/brave_search_bridge');
|
|
38
|
+
const brave = new BraveSearch({ apiKey: config.braveSearchApiKey });
|
|
39
|
+
const results = await brave.search(query);
|
|
40
|
+
if (results.length > 0) {
|
|
41
|
+
return results.map(r => `Title: ${r.title}\nSnippet: ${r.snippet}\nURL: ${r.link}`).join('\n\n');
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
onProgress({ phase: 'error', action: 'web_search', message: e.message });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 3. Fallback to DuckDuckGo Scraping
|
|
14
49
|
try {
|
|
15
50
|
const response = await axios.get(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
|
|
16
51
|
headers: {
|
|
@@ -28,8 +63,14 @@ async function webSearch(query) {
|
|
|
28
63
|
results.push(`Title: ${title}\nSnippet: ${snippet}\nURL: ${link}`);
|
|
29
64
|
}
|
|
30
65
|
});
|
|
66
|
+
|
|
67
|
+
if (results.length === 0) {
|
|
68
|
+
onProgress({ phase: 'error', action: 'web_search', message: 'DuckDuckGo scraping returned no results. It might be blocking us.' });
|
|
69
|
+
}
|
|
70
|
+
|
|
31
71
|
return results.length > 0 ? results.join('\n\n') : 'No results found.';
|
|
32
72
|
} catch (e) {
|
|
73
|
+
onProgress({ phase: 'error', action: 'web_search', message: `DuckDuckGo fallback failed: ${e.message}` });
|
|
33
74
|
return `Search failed: ${e.message}`;
|
|
34
75
|
}
|
|
35
76
|
}
|
|
@@ -48,6 +89,10 @@ You work in an inspect -> plan -> act -> verify loop.
|
|
|
48
89
|
PERSONALITY & TONE:
|
|
49
90
|
- Gender: Female.
|
|
50
91
|
- Persona: Friendly, energetic, polite, and slightly playful.
|
|
92
|
+
- Language routing is mandatory and based on the user's latest message:
|
|
93
|
+
- If the latest user message contains Thai characters, respond in Thai.
|
|
94
|
+
- If the latest user message is English, ASCII-only, or a short English greeting such as "hi", "hello", "ok", or "thanks", respond in English.
|
|
95
|
+
- Do not use Thai just because your persona mentions Mint/มิ้นท์, previous history was Thai, or app settings use th-TH.
|
|
51
96
|
- Politeness:
|
|
52
97
|
- **WHEN RESPONDING IN THAI:** ALWAYS use female polite particles such as "ค่ะ", "นะคะ", "นะค๊า", "จ้า". Refer to yourself as "มิ้นท์" or "หนู".
|
|
53
98
|
- **WHEN RESPONDING IN ENGLISH:** Use a cheerful, polite, and bubbly tone.
|
|
@@ -127,20 +172,143 @@ function extractJson(text) {
|
|
|
127
172
|
}
|
|
128
173
|
}
|
|
129
174
|
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
175
|
+
function normalizeExecutorAction(action, input = {}) {
|
|
176
|
+
return {
|
|
177
|
+
type: action,
|
|
178
|
+
target: input.target || input.path || input.query || '',
|
|
179
|
+
path: input.path,
|
|
180
|
+
pathType: input.type,
|
|
181
|
+
openAfter: input.openAfter,
|
|
182
|
+
pluginName: input.pluginName,
|
|
183
|
+
server: input.server,
|
|
184
|
+
args: input.args,
|
|
185
|
+
x: input.x,
|
|
186
|
+
y: input.y,
|
|
187
|
+
button: input.button
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function formatActionPreview(action, input = {}) {
|
|
192
|
+
if (input.command) return input.command;
|
|
193
|
+
if (input.path) return input.path;
|
|
194
|
+
if (input.target) return input.target;
|
|
195
|
+
if (input.query) return input.query;
|
|
196
|
+
return action;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function evaluateActionResult(action, toolResult = '') {
|
|
200
|
+
if (!toolRegistry.isImportantAction(action)) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const text = String(toolResult || '');
|
|
205
|
+
if (/^Error:|blocked|denied|failed|exception|not found/i.test(text)) {
|
|
206
|
+
return {
|
|
207
|
+
status: 'failed',
|
|
208
|
+
message: `Evaluator: ${action} may have failed. Review the observation before continuing.`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (action === 'run_shell' && /(ERR!|Error:|FAIL|failed|not found|permission denied)/i.test(text)) {
|
|
213
|
+
return {
|
|
214
|
+
status: 'warning',
|
|
215
|
+
message: 'Evaluator: shell output contains error-like text; verify before claiming success.'
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
status: 'passed',
|
|
221
|
+
message: `Evaluator: ${action} completed without obvious errors.`
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function splitDataUri(dataUri = '') {
|
|
226
|
+
const match = String(dataUri).match(/^data:([^;]+);base64,([\s\S]+)$/);
|
|
227
|
+
if (!match) return null;
|
|
228
|
+
return {
|
|
229
|
+
mimeType: match[1],
|
|
230
|
+
data: match[2]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function contentToText(content) {
|
|
235
|
+
if (content && typeof content === 'object' && !Array.isArray(content)) {
|
|
236
|
+
return String(content.text || '');
|
|
237
|
+
}
|
|
238
|
+
return String(content || '');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function contentToGeminiParts(content) {
|
|
242
|
+
const text = contentToText(content);
|
|
243
|
+
const parts = text ? [{ text }] : [];
|
|
244
|
+
if (content && typeof content === 'object' && content.imageDataUri) {
|
|
245
|
+
const image = splitDataUri(content.imageDataUri);
|
|
246
|
+
if (image) {
|
|
247
|
+
parts.push({ inlineData: { mimeType: image.mimeType, data: image.data } });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return parts.length > 0 ? parts : [{ text: '' }];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function contentToOpenAIContent(content) {
|
|
254
|
+
const text = contentToText(content) || 'Analyze this input.';
|
|
255
|
+
if (content && typeof content === 'object' && content.imageDataUri) {
|
|
256
|
+
return [
|
|
257
|
+
{ type: 'text', text },
|
|
258
|
+
{ type: 'image_url', image_url: { url: content.imageDataUri } }
|
|
259
|
+
];
|
|
134
260
|
}
|
|
261
|
+
return text;
|
|
262
|
+
}
|
|
135
263
|
|
|
264
|
+
function contentToAnthropicContent(content) {
|
|
265
|
+
const text = contentToText(content) || 'Analyze this input.';
|
|
266
|
+
if (content && typeof content === 'object' && content.imageDataUri) {
|
|
267
|
+
const image = splitDataUri(content.imageDataUri);
|
|
268
|
+
if (image) {
|
|
269
|
+
return [
|
|
270
|
+
{ type: 'image', source: { type: 'base64', media_type: image.mimeType, data: image.data } },
|
|
271
|
+
{ type: 'text', text }
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return text;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function getSupportedCodeProviderOrder(config, availableProviders = getAvailableProviders(config || {}), requestedOverride = null) {
|
|
279
|
+
const requestedProvider = requestedOverride || (config && config.aiProvider) || 'gemini';
|
|
136
280
|
const priority = ['anthropic', 'openai', 'gemini', 'local_openai'];
|
|
281
|
+
const ordered = [];
|
|
282
|
+
|
|
283
|
+
if (SUPPORTED_CODE_PROVIDERS.includes(requestedProvider) && availableProviders.includes(requestedProvider)) {
|
|
284
|
+
ordered.push(requestedProvider);
|
|
285
|
+
}
|
|
286
|
+
|
|
137
287
|
for (const provider of priority) {
|
|
138
|
-
if (availableProviders.includes(provider)) {
|
|
139
|
-
|
|
288
|
+
if (availableProviders.includes(provider) && !ordered.includes(provider)) {
|
|
289
|
+
ordered.push(provider);
|
|
140
290
|
}
|
|
141
291
|
}
|
|
142
292
|
|
|
143
|
-
return 'gemini';
|
|
293
|
+
return ordered.length > 0 ? ordered : ['gemini'];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function selectSupportedCodeProvider(config, availableProviders = getAvailableProviders(config || {})) {
|
|
297
|
+
return getSupportedCodeProviderOrder(config, availableProviders)[0];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function getCodeProviderModel(provider, config = {}) {
|
|
301
|
+
switch (provider) {
|
|
302
|
+
case 'anthropic':
|
|
303
|
+
return config.anthropicModel || 'claude-3-5-sonnet-latest';
|
|
304
|
+
case 'openai':
|
|
305
|
+
return config.openaiModel || 'gpt-4o';
|
|
306
|
+
case 'local_openai':
|
|
307
|
+
return config.localModelName || 'local-model';
|
|
308
|
+
case 'gemini':
|
|
309
|
+
default:
|
|
310
|
+
return config.geminiModel || DEFAULT_GEMINI_MODEL;
|
|
311
|
+
}
|
|
144
312
|
}
|
|
145
313
|
|
|
146
314
|
function resolveWorkspacePath(workspaceRoot, targetPath = '.') {
|
|
@@ -301,21 +469,7 @@ async function findPaths(workspaceRoot, query, type = 'any') {
|
|
|
301
469
|
}
|
|
302
470
|
|
|
303
471
|
function assertSafeShell(command) {
|
|
304
|
-
|
|
305
|
-
/\brm\s+-rf\b/,
|
|
306
|
-
/\bgit\s+reset\s+--hard\b/,
|
|
307
|
-
/\bgit\s+checkout\s+--\b/,
|
|
308
|
-
/\bmkfs\b/,
|
|
309
|
-
/\bshutdown\b/,
|
|
310
|
-
/\breboot\b/,
|
|
311
|
-
/>\s*\/dev\//,
|
|
312
|
-
/\bcurl\b.*\|\s*(sh|bash)\b/,
|
|
313
|
-
/\bwget\b.*\|\s*(sh|bash)\b/
|
|
314
|
-
];
|
|
315
|
-
|
|
316
|
-
if (blockedPatterns.some(pattern => pattern.test(command))) {
|
|
317
|
-
throw new Error(`Blocked unsafe command: ${command}`);
|
|
318
|
-
}
|
|
472
|
+
return safetyManager.assertShellCommandAllowed(command);
|
|
319
473
|
}
|
|
320
474
|
|
|
321
475
|
async function runShell(workspaceRoot, command) {
|
|
@@ -323,7 +477,8 @@ async function runShell(workspaceRoot, command) {
|
|
|
323
477
|
throw new Error('Shell command is required.');
|
|
324
478
|
}
|
|
325
479
|
assertSafeShell(command);
|
|
326
|
-
const { stdout, stderr } = await
|
|
480
|
+
const { stdout, stderr } = await sandboxRunner.runShell(command, {
|
|
481
|
+
source: 'code_agent',
|
|
327
482
|
cwd: workspaceRoot,
|
|
328
483
|
maxBuffer: 1024 * 1024 * 4
|
|
329
484
|
});
|
|
@@ -386,34 +541,51 @@ function writeFile(workspaceRoot, targetPath, content) {
|
|
|
386
541
|
}
|
|
387
542
|
|
|
388
543
|
class UnifiedAgentClient {
|
|
389
|
-
constructor(provider, config) {
|
|
544
|
+
constructor(provider, config, providerOrder = [provider]) {
|
|
390
545
|
this.provider = SUPPORTED_CODE_PROVIDERS.includes(provider) ? provider : 'gemini';
|
|
546
|
+
this.providerOrder = providerOrder.length > 0 ? providerOrder : [this.provider];
|
|
391
547
|
this.config = config;
|
|
392
548
|
this.history = [];
|
|
393
549
|
this.systemInstruction = CODE_AGENT_PROMPT;
|
|
550
|
+
this.lastSuccessfulProvider = null;
|
|
394
551
|
}
|
|
395
552
|
|
|
396
553
|
async sendMessage(observation) {
|
|
397
554
|
this.history.push({ role: 'user', content: observation });
|
|
398
555
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
556
|
+
const failures = [];
|
|
557
|
+
for (const provider of this.providerOrder) {
|
|
558
|
+
this.provider = SUPPORTED_CODE_PROVIDERS.includes(provider) ? provider : 'gemini';
|
|
559
|
+
try {
|
|
560
|
+
let responseText = '';
|
|
561
|
+
if (this.provider === 'anthropic') {
|
|
562
|
+
responseText = await this._callAnthropic();
|
|
563
|
+
} else if (this.provider === 'openai' || this.provider === 'local_openai') {
|
|
564
|
+
responseText = await this._callOpenAI();
|
|
565
|
+
} else {
|
|
566
|
+
responseText = await this._callGemini();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
this.history.push({ role: 'assistant', content: responseText });
|
|
570
|
+
this.lastSuccessfulProvider = this.provider;
|
|
571
|
+
return responseText;
|
|
572
|
+
} catch (error) {
|
|
573
|
+
const message = error.message || error.code || 'unknown error';
|
|
574
|
+
failures.push(`${this.provider}: ${message}`);
|
|
575
|
+
if (process.env.MINT_DEBUG === '1') {
|
|
576
|
+
console.error(`[Code Agent Fallback] Provider '${this.provider}' failed: ${message}`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
406
579
|
}
|
|
407
580
|
|
|
408
|
-
|
|
409
|
-
return responseText;
|
|
581
|
+
throw new Error(`All code agent providers failed. ${failures.join(' | ')}`);
|
|
410
582
|
}
|
|
411
583
|
|
|
412
584
|
async _callAnthropic() {
|
|
413
585
|
const apiKey = this.config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
414
586
|
const messages = this.history.map(m => ({
|
|
415
587
|
role: m.role,
|
|
416
|
-
content: m.content
|
|
588
|
+
content: contentToAnthropicContent(m.content)
|
|
417
589
|
}));
|
|
418
590
|
|
|
419
591
|
const response = await axios.post('https://api.anthropic.com/v1/messages', {
|
|
@@ -439,7 +611,10 @@ class UnifiedAgentClient {
|
|
|
439
611
|
|
|
440
612
|
const messages = [
|
|
441
613
|
{ role: 'system', content: this.systemInstruction },
|
|
442
|
-
...this.history
|
|
614
|
+
...this.history.map(m => ({
|
|
615
|
+
role: m.role,
|
|
616
|
+
content: contentToOpenAIContent(m.content)
|
|
617
|
+
}))
|
|
443
618
|
];
|
|
444
619
|
|
|
445
620
|
const response = await axios.post(`${baseUrl.replace(/\/$/, '')}/chat/completions`, {
|
|
@@ -460,13 +635,15 @@ class UnifiedAgentClient {
|
|
|
460
635
|
const model = this.config.geminiModel || DEFAULT_GEMINI_MODEL;
|
|
461
636
|
const ai = new GoogleGenAI({ apiKey });
|
|
462
637
|
|
|
638
|
+
const recentHistory = this.history.slice(-16);
|
|
639
|
+
const priorHistory = recentHistory.slice(0, -1);
|
|
640
|
+
const lastEntry = recentHistory[recentHistory.length - 1] || { content: '' };
|
|
641
|
+
|
|
463
642
|
// Convert history for Gemini, ensuring parts are correctly structured
|
|
464
|
-
const geminiHistory =
|
|
643
|
+
const geminiHistory = priorHistory.map(m => ({
|
|
465
644
|
role: m.role === 'assistant' ? 'model' : 'user',
|
|
466
|
-
parts:
|
|
645
|
+
parts: contentToGeminiParts(m.content)
|
|
467
646
|
}));
|
|
468
|
-
|
|
469
|
-
const lastMessage = String(this.history[this.history.length - 1].content || '');
|
|
470
647
|
|
|
471
648
|
const chat = ai.chats.create({
|
|
472
649
|
model,
|
|
@@ -477,7 +654,7 @@ class UnifiedAgentClient {
|
|
|
477
654
|
history: geminiHistory
|
|
478
655
|
});
|
|
479
656
|
|
|
480
|
-
const response = await chat.sendMessage({ message:
|
|
657
|
+
const response = await chat.sendMessage({ message: contentToGeminiParts(lastEntry.content) });
|
|
481
658
|
return typeof response.text === 'function' ? response.text() : response.text;
|
|
482
659
|
}
|
|
483
660
|
}
|
|
@@ -551,6 +728,7 @@ async function buildInitialObservation(task, workspaceRoot, history = []) {
|
|
|
551
728
|
const session = readWorkspaceSession(workspaceRoot);
|
|
552
729
|
const gitContext = await getGitContext(workspaceRoot);
|
|
553
730
|
const testCommands = detectTestCommands(workspaceRoot);
|
|
731
|
+
const userContext = memoryStore.getUserContext(task);
|
|
554
732
|
|
|
555
733
|
const contextStr = history.length > 0
|
|
556
734
|
? `Recent Context:\n${history.slice(-10).map(m => `${m.sender}: ${m.text}`).join('\n')}\n`
|
|
@@ -571,7 +749,9 @@ async function buildInitialObservation(task, workspaceRoot, history = []) {
|
|
|
571
749
|
session.summary || '(none)',
|
|
572
750
|
`Previous task: ${session.lastTask || '(none)'}`,
|
|
573
751
|
`Previous verification: ${session.lastVerification || '(none)'}`,
|
|
574
|
-
'
|
|
752
|
+
'Long-term user context:',
|
|
753
|
+
userContext || '(none)',
|
|
754
|
+
'If the task is conversational or trivial, finish directly without inspecting the workspace. For code/workspace tasks, inspect before making edits.'
|
|
575
755
|
].join('\n');
|
|
576
756
|
}
|
|
577
757
|
|
|
@@ -579,6 +759,7 @@ async function executeCodeTask(task, options = {}) {
|
|
|
579
759
|
const workspaceRoot = path.resolve(options.cwd || process.cwd());
|
|
580
760
|
const history = options.history || [];
|
|
581
761
|
const onProgress = typeof options.onProgress === 'function' ? options.onProgress : () => {};
|
|
762
|
+
const onFinalSummary = typeof options.onFinalSummary === 'function' ? options.onFinalSummary : null;
|
|
582
763
|
const requestApproval = typeof options.requestApproval === 'function'
|
|
583
764
|
? options.requestApproval
|
|
584
765
|
: async () => true;
|
|
@@ -586,10 +767,29 @@ async function executeCodeTask(task, options = {}) {
|
|
|
586
767
|
? options.askUser
|
|
587
768
|
: async (q) => `User didn't answer: ${q}`;
|
|
588
769
|
const config = readConfig();
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
770
|
+
const availableProviders = getAvailableProviders(config);
|
|
771
|
+
const providerOrder = getSupportedCodeProviderOrder(config, availableProviders, options.provider);
|
|
772
|
+
const provider = providerOrder[0];
|
|
773
|
+
const client = new UnifiedAgentClient(provider, config, providerOrder);
|
|
774
|
+
|
|
775
|
+
const initialObservationText = await buildInitialObservation(task, workspaceRoot, history);
|
|
776
|
+
const relevantMemoryCount = memoryStore.searchInteractions(task, 5).length;
|
|
777
|
+
onProgress({
|
|
778
|
+
phase: 'memory',
|
|
779
|
+
action: 'memory_context',
|
|
780
|
+
message: `Loaded memory: profile + recent history, ${relevantMemoryCount} direct match${relevantMemoryCount === 1 ? '' : 'es'}`
|
|
781
|
+
});
|
|
782
|
+
let observation = options.imageDataUri
|
|
783
|
+
? {
|
|
784
|
+
text: [
|
|
785
|
+
initialObservationText,
|
|
786
|
+
'',
|
|
787
|
+
`[Attached image: ${options.imagePath || 'command-line image'}]`,
|
|
788
|
+
'Use the attached image as visual context when planning and answering.'
|
|
789
|
+
].join('\n'),
|
|
790
|
+
imageDataUri: options.imageDataUri
|
|
791
|
+
}
|
|
792
|
+
: initialObservationText;
|
|
593
793
|
|
|
594
794
|
let finalSummary = '';
|
|
595
795
|
let finalVerification = '';
|
|
@@ -602,6 +802,17 @@ async function executeCodeTask(task, options = {}) {
|
|
|
602
802
|
const decision = await getAgentDecision(client, observation, { onProgress, step });
|
|
603
803
|
const action = decision.action;
|
|
604
804
|
const input = decision.input || {};
|
|
805
|
+
try {
|
|
806
|
+
toolRegistry.validateToolInput(action, input);
|
|
807
|
+
} catch (e) {
|
|
808
|
+
observation = [
|
|
809
|
+
`Previous thought: ${decision.thought || '(none)'}`,
|
|
810
|
+
`Action: ${action || '(none)'}`,
|
|
811
|
+
'Observation:',
|
|
812
|
+
`Error: ${e.message}`
|
|
813
|
+
].join('\n');
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
605
816
|
|
|
606
817
|
// Immediately show the agent's thought/reasoning
|
|
607
818
|
onProgress({
|
|
@@ -615,6 +826,16 @@ async function executeCodeTask(task, options = {}) {
|
|
|
615
826
|
finalSessionSummary = input.sessionSummary || input.summary || task;
|
|
616
827
|
finalSummary = input.summary || 'Task complete.';
|
|
617
828
|
finalVerification = input.verification || 'Not specified.';
|
|
829
|
+
if (onFinalSummary) {
|
|
830
|
+
await onFinalSummary({
|
|
831
|
+
summary: finalSummary,
|
|
832
|
+
verification: finalVerification,
|
|
833
|
+
providerInfo: {
|
|
834
|
+
provider: client.lastSuccessfulProvider || client.provider || provider,
|
|
835
|
+
model: getCodeProviderModel(client.lastSuccessfulProvider || client.provider || provider, config)
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
}
|
|
618
839
|
writeWorkspaceSession(workspaceRoot, {
|
|
619
840
|
summary: finalSessionSummary,
|
|
620
841
|
lastTask: task,
|
|
@@ -627,7 +848,7 @@ async function executeCodeTask(task, options = {}) {
|
|
|
627
848
|
try {
|
|
628
849
|
switch (action) {
|
|
629
850
|
case 'web_search':
|
|
630
|
-
toolResult = await webSearch(input.query);
|
|
851
|
+
toolResult = await webSearch(input.query, onProgress);
|
|
631
852
|
break;
|
|
632
853
|
case 'list_files':
|
|
633
854
|
toolResult = await listFiles(workspaceRoot, input.path || '.');
|
|
@@ -640,6 +861,13 @@ async function executeCodeTask(task, options = {}) {
|
|
|
640
861
|
break;
|
|
641
862
|
case 'find_path':
|
|
642
863
|
toolResult = await findPaths(workspaceRoot, input.query, input.type);
|
|
864
|
+
if (input.openAfter === true) {
|
|
865
|
+
const result = JSON.parse(toolResult);
|
|
866
|
+
if (result.success && result.matches.length === 1) {
|
|
867
|
+
await executeAction({ type: 'open_folder', target: result.matches[0].path });
|
|
868
|
+
toolResult = `Found and opened: ${result.matches[0].path}`;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
643
871
|
break;
|
|
644
872
|
case 'run_shell': {
|
|
645
873
|
const approved = await requestApproval({
|
|
@@ -651,6 +879,12 @@ async function executeCodeTask(task, options = {}) {
|
|
|
651
879
|
toolResult = `User denied shell command: ${input.command}`;
|
|
652
880
|
break;
|
|
653
881
|
}
|
|
882
|
+
safetyManager.appendActionLog({
|
|
883
|
+
source: 'code_agent',
|
|
884
|
+
action: 'run_shell',
|
|
885
|
+
command: input.command,
|
|
886
|
+
approved
|
|
887
|
+
});
|
|
654
888
|
toolResult = await runShell(workspaceRoot, input.command);
|
|
655
889
|
break;
|
|
656
890
|
}
|
|
@@ -665,6 +899,12 @@ async function executeCodeTask(task, options = {}) {
|
|
|
665
899
|
toolResult = `User denied patch for ${patchInput.path}`;
|
|
666
900
|
break;
|
|
667
901
|
}
|
|
902
|
+
safetyManager.appendActionLog({
|
|
903
|
+
source: 'code_agent',
|
|
904
|
+
action: 'apply_patch',
|
|
905
|
+
path: patchInput.path,
|
|
906
|
+
approved
|
|
907
|
+
});
|
|
668
908
|
toolResult = applyPatch(workspaceRoot, patchInput);
|
|
669
909
|
break;
|
|
670
910
|
}
|
|
@@ -678,6 +918,12 @@ async function executeCodeTask(task, options = {}) {
|
|
|
678
918
|
toolResult = `User denied full file write for ${input.path}`;
|
|
679
919
|
break;
|
|
680
920
|
}
|
|
921
|
+
safetyManager.appendActionLog({
|
|
922
|
+
source: 'code_agent',
|
|
923
|
+
action: 'write_file',
|
|
924
|
+
path: input.path,
|
|
925
|
+
approved
|
|
926
|
+
});
|
|
681
927
|
toolResult = writeFile(workspaceRoot, input.path, input.content);
|
|
682
928
|
break;
|
|
683
929
|
}
|
|
@@ -693,10 +939,28 @@ async function executeCodeTask(task, options = {}) {
|
|
|
693
939
|
case 'create_folder':
|
|
694
940
|
case 'system_info':
|
|
695
941
|
case 'system_automation': {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
942
|
+
const executorAction = normalizeExecutorAction(action, input);
|
|
943
|
+
const safety = safetyManager.classifyAction(executorAction);
|
|
944
|
+
let allowDangerous = false;
|
|
945
|
+
let allowApproval = false;
|
|
946
|
+
if (safety.tier === safetyManager.TIERS.APPROVAL || safety.tier === safetyManager.TIERS.DANGEROUS) {
|
|
947
|
+
const approved = await requestApproval({
|
|
948
|
+
type: action,
|
|
949
|
+
label: formatActionPreview(action, input),
|
|
950
|
+
preview: `${action}: ${formatActionPreview(action, input)}\nSafety: ${safety.tier} (${safety.reason})`
|
|
951
|
+
});
|
|
952
|
+
if (!approved) {
|
|
953
|
+
toolResult = `User denied ${action}: ${formatActionPreview(action, input)}`;
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
956
|
+
allowApproval = safety.tier === safetyManager.TIERS.APPROVAL;
|
|
957
|
+
allowDangerous = safety.tier === safetyManager.TIERS.DANGEROUS;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
toolResult = await executeAction(executorAction, {
|
|
961
|
+
source: 'code_agent',
|
|
962
|
+
allowApproval,
|
|
963
|
+
allowDangerous
|
|
700
964
|
});
|
|
701
965
|
break;
|
|
702
966
|
} default:
|
|
@@ -705,6 +969,22 @@ async function executeCodeTask(task, options = {}) {
|
|
|
705
969
|
toolResult = `Error: ${e.message}`;
|
|
706
970
|
}
|
|
707
971
|
|
|
972
|
+
const evaluation = evaluateActionResult(action, toolResult);
|
|
973
|
+
if (evaluation) {
|
|
974
|
+
onProgress({
|
|
975
|
+
step,
|
|
976
|
+
phase: 'evaluating',
|
|
977
|
+
action: 'evaluator',
|
|
978
|
+
message: `${evaluation.status}: ${evaluation.message}`
|
|
979
|
+
});
|
|
980
|
+
toolResult = [
|
|
981
|
+
toolResult,
|
|
982
|
+
'',
|
|
983
|
+
'Evaluation:',
|
|
984
|
+
`${evaluation.status}: ${evaluation.message}`
|
|
985
|
+
].join('\n');
|
|
986
|
+
}
|
|
987
|
+
|
|
708
988
|
// Log the finished step with result
|
|
709
989
|
let resultSummary = '';
|
|
710
990
|
if (action === 'search_code') {
|
|
@@ -718,8 +998,7 @@ async function executeCodeTask(task, options = {}) {
|
|
|
718
998
|
step,
|
|
719
999
|
phase: 'finished',
|
|
720
1000
|
action,
|
|
721
|
-
target: (input.path || input.command || input.query || '') + resultSummary
|
|
722
|
-
thought: decision.thought
|
|
1001
|
+
target: (input.path || input.command || input.query || '') + resultSummary
|
|
723
1002
|
});
|
|
724
1003
|
|
|
725
1004
|
// Format tool result to be more readable and structured for the agent
|
|
@@ -767,10 +1046,16 @@ async function executeCodeTask(task, options = {}) {
|
|
|
767
1046
|
}
|
|
768
1047
|
|
|
769
1048
|
if (finalSummary) {
|
|
1049
|
+
memoryStore.recordInteraction(task, finalSummary);
|
|
1050
|
+
const answeredProvider = client.lastSuccessfulProvider || client.provider || provider;
|
|
770
1051
|
return {
|
|
771
1052
|
summary: finalSummary,
|
|
772
1053
|
verification: finalVerification,
|
|
773
|
-
steps: executedSteps
|
|
1054
|
+
steps: executedSteps,
|
|
1055
|
+
providerInfo: {
|
|
1056
|
+
provider: answeredProvider,
|
|
1057
|
+
model: getCodeProviderModel(answeredProvider, config)
|
|
1058
|
+
}
|
|
774
1059
|
};
|
|
775
1060
|
}
|
|
776
1061
|
|
|
@@ -780,10 +1065,15 @@ async function executeCodeTask(task, options = {}) {
|
|
|
780
1065
|
lastVerification: 'Agent limit reached before explicit completion.'
|
|
781
1066
|
});
|
|
782
1067
|
|
|
1068
|
+
const answeredProvider = client.lastSuccessfulProvider || client.provider || provider;
|
|
783
1069
|
return {
|
|
784
1070
|
summary: 'Stopped after reaching the maximum number of agent steps.',
|
|
785
1071
|
verification: 'Agent limit reached before explicit completion.',
|
|
786
|
-
steps: executedSteps || MAX_AGENT_STEPS
|
|
1072
|
+
steps: executedSteps || MAX_AGENT_STEPS,
|
|
1073
|
+
providerInfo: {
|
|
1074
|
+
provider: answeredProvider,
|
|
1075
|
+
model: getCodeProviderModel(answeredProvider, config)
|
|
1076
|
+
}
|
|
787
1077
|
};
|
|
788
1078
|
}
|
|
789
1079
|
|
|
@@ -792,6 +1082,7 @@ module.exports = {
|
|
|
792
1082
|
_helpers: {
|
|
793
1083
|
extractJson,
|
|
794
1084
|
selectSupportedCodeProvider,
|
|
1085
|
+
getSupportedCodeProviderOrder,
|
|
795
1086
|
findPaths,
|
|
796
1087
|
listFiles,
|
|
797
1088
|
searchCode,
|