@pheem49/mint 1.5.0 → 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.
Files changed (78) hide show
  1. package/README.md +27 -1
  2. package/main.js +28 -14
  3. package/mint-cli-logic.js +3 -119
  4. package/mint-cli.js +497 -23
  5. package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  6. package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
  7. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
  8. 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
  9. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
  10. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
  11. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
  12. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
  13. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
  14. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
  15. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
  16. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
  17. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
  18. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
  19. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
  20. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
  21. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
  22. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
  23. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
  24. 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
  25. package/package.json +26 -1
  26. package/src/AI_Brain/Gemini_API.js +147 -46
  27. package/src/AI_Brain/autonomous_brain.js +2 -1
  28. package/src/AI_Brain/memory_store.js +299 -3
  29. package/src/CLI/chat_router.js +18 -6
  30. package/src/CLI/chat_ui.js +396 -50
  31. package/src/CLI/code_agent.js +203 -14
  32. package/src/CLI/image_input.js +90 -0
  33. package/src/CLI/onboarding.js +72 -15
  34. package/src/CLI/updater.js +6 -4
  35. package/src/System/action_executor.js +59 -10
  36. package/src/System/config_manager.js +31 -1
  37. package/src/System/granular_automation.js +122 -53
  38. package/src/System/proactive_loop.js +19 -3
  39. package/src/System/safety_manager.js +108 -0
  40. package/src/System/sandbox_runner.js +182 -0
  41. package/src/System/system_automation.js +127 -81
  42. package/src/System/system_info.js +70 -0
  43. package/src/System/tool_registry.js +280 -0
  44. package/src/System/window_manager.js +4 -2
  45. package/src/UI/live2d_manager.js +368 -0
  46. package/src/UI/renderer.js +176 -18
  47. package/src/UI/styles.css +452 -31
  48. package/.codex +0 -0
  49. package/docs/assets/Agent_Mint.png +0 -0
  50. package/docs/assets/CLI_Screen.png +0 -0
  51. package/docs/assets/Settings.png +0 -0
  52. package/docs/assets/icon.png +0 -0
  53. package/docs/guide.html +0 -632
  54. package/docs/index.html +0 -133
  55. package/docs/style.css +0 -579
  56. package/index.html +0 -16
  57. package/src/UI/index.html +0 -126
  58. package/tech_news.txt +0 -3
  59. package/test_knowledge.txt +0 -3
  60. package/tests/action_executor_safety.test.js +0 -67
  61. package/tests/agent_orchestrator.test.js +0 -41
  62. package/tests/chat_router.test.js +0 -42
  63. package/tests/code_agent.test.js +0 -69
  64. package/tests/config_manager.test.js +0 -141
  65. package/tests/docker.test.js +0 -46
  66. package/tests/file_operations.test.js +0 -57
  67. package/tests/gmail.test.js +0 -135
  68. package/tests/gmail_auth.test.js +0 -129
  69. package/tests/google_calendar.test.js +0 -113
  70. package/tests/google_tts_urls.test.js +0 -24
  71. package/tests/memory_store.test.js +0 -185
  72. package/tests/notion.test.js +0 -121
  73. package/tests/provider_routing.test.js +0 -83
  74. package/tests/safety_manager.test.js +0 -40
  75. package/tests/spotify.test.js +0 -201
  76. package/tests/system_monitor.test.js +0 -37
  77. package/tests/updater.test.js +0 -32
  78. package/tests/workspace_manager.test.js +0 -56
@@ -93,17 +93,49 @@ function getDb() {
93
93
  last_used DATETIME DEFAULT CURRENT_TIMESTAMP
94
94
  );
95
95
 
96
+ -- Raw episodic memories of user/assistant turns.
97
+ CREATE TABLE IF NOT EXISTS interaction_memories (
98
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
99
+ user_text TEXT NOT NULL,
100
+ ai_text TEXT NOT NULL,
101
+ keywords TEXT DEFAULT '',
102
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
103
+ );
104
+
96
105
  -- Response Cache: For repetitive exact queries
97
106
  CREATE TABLE IF NOT EXISTS response_cache (
98
107
  query_hash TEXT PRIMARY KEY,
99
108
  response TEXT NOT NULL,
100
109
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
101
110
  );
111
+
112
+ -- Learned skill/instruction documents imported from local files.
113
+ CREATE TABLE IF NOT EXISTS learned_skills (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ name TEXT NOT NULL,
116
+ source_path TEXT NOT NULL UNIQUE,
117
+ content TEXT NOT NULL,
118
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
119
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
120
+ );
102
121
  `);
103
122
 
104
123
  return dbInstance;
105
124
  }
106
125
 
126
+ function ensureLearnedSkillsTable() {
127
+ getDb().exec(`
128
+ CREATE TABLE IF NOT EXISTS learned_skills (
129
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
130
+ name TEXT NOT NULL,
131
+ source_path TEXT NOT NULL UNIQUE,
132
+ content TEXT NOT NULL,
133
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
134
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
135
+ );
136
+ `);
137
+ }
138
+
107
139
  // ── Profile helpers ────────────────────────────────────────────────────────
108
140
  function setProfile(key, value) {
109
141
  try {
@@ -203,6 +235,116 @@ function getTopPatterns(limit = 8) {
203
235
  }
204
236
  }
205
237
 
238
+ const MAX_INTERACTION_MEMORIES = 1000;
239
+
240
+ function stripRelevantMemoryBlock(text) {
241
+ return String(text || '')
242
+ .replace(/\n?\[Relevant long-term memory for this user message\][\s\S]*?\[End relevant memory\]\n?/g, '\n')
243
+ .replace(/^\s*\[Relevant long-term memory for this user message\][\s\S]*?\[End relevant memory\]\s*/g, '')
244
+ .replace(/\n?\[LOCAL KNOWLEDGE BASE - USE THIS CONTEXT TO ANSWER\][\s\S]*/g, '')
245
+ .trim();
246
+ }
247
+
248
+ function addInteractionMemory(userMessage, aiResponseText, keywords = []) {
249
+ try {
250
+ const db = getDb();
251
+ db.prepare(`
252
+ INSERT INTO interaction_memories (user_text, ai_text, keywords)
253
+ VALUES (?, ?, ?)
254
+ `).run(
255
+ String(userMessage || '').slice(0, 1200),
256
+ String(aiResponseText || '').slice(0, 1200),
257
+ keywords.join(',')
258
+ );
259
+ db.exec(`
260
+ DELETE FROM interaction_memories WHERE id NOT IN (
261
+ SELECT id FROM interaction_memories ORDER BY id DESC LIMIT ${MAX_INTERACTION_MEMORIES}
262
+ )
263
+ `);
264
+ } catch (err) {
265
+ console.error('[Memory] addInteractionMemory error:', err.message);
266
+ }
267
+ }
268
+
269
+ function getRecentInteractions(limit = 5) {
270
+ try {
271
+ return getDb()
272
+ .prepare('SELECT id, user_text, ai_text, keywords, created_at FROM interaction_memories ORDER BY id DESC LIMIT ?')
273
+ .all(limit);
274
+ } catch (_) {
275
+ return [];
276
+ }
277
+ }
278
+
279
+ function deleteInteractionMemory(id) {
280
+ try {
281
+ const result = getDb().prepare('DELETE FROM interaction_memories WHERE id = ?').run(id);
282
+ return result.changes > 0;
283
+ } catch (err) {
284
+ console.error('[Memory] deleteInteractionMemory error:', err.message);
285
+ return false;
286
+ }
287
+ }
288
+
289
+ function searchInteractions(query, limit = 8) {
290
+ try {
291
+ const keywords = extractKeywords(query);
292
+ const terms = keywords.length > 0 ? keywords : [String(query || '').trim()].filter(Boolean);
293
+ if (terms.length === 0) return [];
294
+
295
+ const rows = [];
296
+ const seen = new Set();
297
+ const stmt = getDb().prepare(`
298
+ SELECT id, user_text, ai_text, keywords, created_at
299
+ FROM interaction_memories
300
+ WHERE user_text LIKE ? OR ai_text LIKE ? OR keywords LIKE ?
301
+ ORDER BY id DESC
302
+ LIMIT ?
303
+ `);
304
+
305
+ for (const term of terms.slice(0, 5)) {
306
+ const like = `%${term}%`;
307
+ for (const row of stmt.all(like, like, like, limit)) {
308
+ if (!seen.has(row.id)) {
309
+ seen.add(row.id);
310
+ rows.push(row);
311
+ if (rows.length >= limit) return rows;
312
+ }
313
+ }
314
+ }
315
+ return rows;
316
+ } catch (_) {
317
+ return [];
318
+ }
319
+ }
320
+
321
+ function clearInteractionMemories() {
322
+ try {
323
+ getDb().prepare('DELETE FROM interaction_memories').run();
324
+ } catch (err) {
325
+ console.error('[Memory] clearInteractionMemories error:', err.message);
326
+ }
327
+ }
328
+
329
+ function exportMemorySnapshot() {
330
+ try {
331
+ return {
332
+ profile: getAllProfile(),
333
+ session_memories: getRecentMemories(MAX_SESSION_MEMORIES),
334
+ usage_patterns: getTopPatterns(50),
335
+ interaction_memories: getRecentInteractions(MAX_INTERACTION_MEMORIES)
336
+ };
337
+ } catch (err) {
338
+ console.error('[Memory] exportMemorySnapshot error:', err.message);
339
+ return {
340
+ profile: {},
341
+ session_memories: [],
342
+ usage_patterns: [],
343
+ interaction_memories: []
344
+ };
345
+ }
346
+ }
347
+
206
348
  // ── Simple keyword extractor (no external deps) ────────────────────────────
207
349
  const STOP_WORDS = new Set([
208
350
  'ที่', 'ให้', 'และ', 'ของ', 'กับ', 'ใน', 'บน', 'เป็น', 'อยู่', 'มี', 'ได้', 'the', 'a', 'an',
@@ -220,6 +362,37 @@ function extractKeywords(text) {
220
362
  .slice(0, 6);
221
363
  }
222
364
 
365
+ function cleanProfileValue(value) {
366
+ return String(value || '')
367
+ .replace(/[.,!?;:()[\]{}"'`“”‘’]+$/g, '')
368
+ .replace(/(นะ|น่ะ|ครับ|ค่ะ|คะ|จ้า|จ๊ะ|ฮะ|ค้าบ|ค่า)+$/u, '')
369
+ .trim();
370
+ }
371
+
372
+ function extractUserName(text) {
373
+ const input = String(text || '').trim();
374
+ const patterns = [
375
+ /(?:ผม|ฉัน|ชั้น|หนู|เรา|ข้า|ดิฉัน)?\s*ชื่อ(?:เล่น)?\s*(?:คือ|ว่า|เป็น)?\s*([A-Za-z\u0E00-\u0E7F][A-Za-z\u0E00-\u0E7F\s]{0,40})/iu,
376
+ /(?:เรียก(?:ผม|ฉัน|ชั้น|หนู|เรา)?ว่า)\s*([A-Za-z\u0E00-\u0E7F][A-Za-z\u0E00-\u0E7F\s]{0,40})/iu,
377
+ /\bmy name is\s+([A-Za-z][A-Za-z\s'-]{0,40})/iu,
378
+ /\bcall me\s+([A-Za-z][A-Za-z\s'-]{0,40})/iu,
379
+ /\bi am\s+([A-Za-z][A-Za-z\s'-]{0,40})/iu,
380
+ /\bi'm\s+([A-Za-z][A-Za-z\s'-]{0,40})/iu
381
+ ];
382
+
383
+ for (const pattern of patterns) {
384
+ const match = input.match(pattern);
385
+ if (match && match[1]) {
386
+ const name = cleanProfileValue(match[1])
387
+ .split(/\s+(?:and|แล้ว|นะ|ครับ|ค่ะ|คะ)\s+/i)[0]
388
+ .trim();
389
+ if (name && name.length <= 40) return name;
390
+ }
391
+ }
392
+
393
+ return '';
394
+ }
395
+
223
396
  // ── Main public API ────────────────────────────────────────────────────────
224
397
 
225
398
  /**
@@ -233,12 +406,18 @@ function recordInteraction(userMessage, aiResponseText) {
233
406
  // Extract keywords as usage patterns
234
407
  const keywords = extractKeywords(userMessage);
235
408
  keywords.forEach(kw => recordPattern(kw));
409
+ addInteractionMemory(userMessage, aiResponseText, keywords);
236
410
 
237
411
  // Detect preferred language
238
412
  const thaiRatio = (userMessage.match(/[\u0E00-\u0E7F]/g) || []).length / userMessage.length;
239
413
  if (thaiRatio > 0.3) setProfile('preferred_language', 'thai');
240
414
  else setProfile('preferred_language', 'english');
241
415
 
416
+ const userName = extractUserName(userMessage);
417
+ if (userName) {
418
+ setProfile('user_name', userName);
419
+ }
420
+
242
421
  // Detect coding intent (update project activity)
243
422
  const codingKeywords = ['code', 'fix', 'debug', 'function', 'class', 'import', 'script',
244
423
  'แก้', 'เขียน', 'โค้ด', 'สคริปต์', 'ฟังก์ชัน'];
@@ -268,20 +447,86 @@ function saveSessionSummary(summary, tags = []) {
268
447
  addSessionMemory(summary.trim(), tags);
269
448
  }
270
449
 
450
+ function addLearnedSkill(name, sourcePath, content) {
451
+ const cleanName = String(name || '').trim() || path.basename(sourcePath || 'skill.md');
452
+ const cleanPath = path.resolve(String(sourcePath || ''));
453
+ const cleanContent = String(content || '').trim();
454
+ if (!cleanContent) {
455
+ throw new Error('Skill file is empty.');
456
+ }
457
+
458
+ const storedContent = cleanContent.slice(0, 12000);
459
+ ensureLearnedSkillsTable();
460
+ const db = getDb();
461
+ db.prepare(`
462
+ INSERT INTO learned_skills (name, source_path, content, created_at, updated_at)
463
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
464
+ ON CONFLICT(source_path) DO UPDATE SET
465
+ name = excluded.name,
466
+ content = excluded.content,
467
+ updated_at = CURRENT_TIMESTAMP
468
+ `).run(cleanName, cleanPath, storedContent);
469
+
470
+ return {
471
+ name: cleanName,
472
+ source_path: cleanPath,
473
+ content_length: cleanContent.length,
474
+ stored_length: storedContent.length
475
+ };
476
+ }
477
+
478
+ function getLearnedSkills(limit = 10) {
479
+ try {
480
+ ensureLearnedSkillsTable();
481
+ return getDb().prepare(`
482
+ SELECT id, name, source_path, content, created_at, updated_at
483
+ FROM learned_skills
484
+ ORDER BY updated_at DESC, id DESC
485
+ LIMIT ?
486
+ `).all(limit);
487
+ } catch (err) {
488
+ console.error('[Memory] getLearnedSkills error:', err.message);
489
+ return [];
490
+ }
491
+ }
492
+
493
+ function deleteLearnedSkill(identifier) {
494
+ try {
495
+ ensureLearnedSkillsTable();
496
+ const input = String(identifier || '').trim();
497
+ if (!input) return 0;
498
+
499
+ const db = getDb();
500
+ if (/^\d+$/.test(input)) {
501
+ return db.prepare('DELETE FROM learned_skills WHERE id = ?').run(Number(input)).changes;
502
+ }
503
+
504
+ const resolved = path.resolve(input);
505
+ return db.prepare('DELETE FROM learned_skills WHERE source_path = ? OR name = ?').run(resolved, input).changes;
506
+ } catch (err) {
507
+ console.error('[Memory] deleteLearnedSkill error:', err.message);
508
+ return 0;
509
+ }
510
+ }
511
+
271
512
  /**
272
513
  * Returns a formatted context string to inject into the AI system prompt.
273
514
  * Lightweight — no async calls.
274
515
  */
275
- function getUserContext() {
516
+ function getUserContext(query = '') {
276
517
  try {
277
518
  const profile = getAllProfile();
278
519
  const patterns = getTopPatterns(6);
279
520
  const memories = getRecentMemories(3);
521
+ const interactions = getRecentInteractions(6);
522
+ const relevantInteractions = query ? searchInteractions(query, 5) : [];
280
523
 
281
524
  const lines = ['\n\n[LONG-TERM USER CONTEXT — use this to personalize responses]'];
282
525
 
283
526
  // Profile info
284
527
  if (Object.keys(profile).length > 0) {
528
+ if (profile.user_name)
529
+ lines.push(`• User name: ${profile.user_name}`);
285
530
  if (profile.preferred_language)
286
531
  lines.push(`• Previously inferred language: ${profile.preferred_language} (do not override the current user message language)`);
287
532
  if (profile.last_active_project)
@@ -306,6 +551,36 @@ function getUserContext() {
306
551
  memories.forEach((m, i) => lines.push(` ${i + 1}. ${m.summary}`));
307
552
  }
308
553
 
554
+ if (interactions.length > 0) {
555
+ lines.push('\nRecent remembered interactions:');
556
+ interactions.forEach((m, i) => {
557
+ lines.push(` ${i + 1}. User: ${m.user_text}`);
558
+ lines.push(` Mint: ${m.ai_text}`);
559
+ });
560
+ }
561
+
562
+ if (relevantInteractions.length > 0) {
563
+ lines.push('\nRelevant remembered interactions for the current request:');
564
+ relevantInteractions.forEach((m, i) => {
565
+ lines.push(` ${i + 1}. User: ${m.user_text}`);
566
+ lines.push(` Mint: ${m.ai_text}`);
567
+ });
568
+ }
569
+
570
+ const learnedSkills = getLearnedSkills(8);
571
+ if (learnedSkills.length > 0) {
572
+ lines.push('\nLearned skill/instruction files:');
573
+ learnedSkills.forEach((skill, i) => {
574
+ lines.push(`\n ${i + 1}. ${skill.name}`);
575
+ lines.push(` Source: ${skill.source_path}`);
576
+ lines.push(' Content:');
577
+ lines.push(skill.content
578
+ .split('\n')
579
+ .map(line => ` ${line}`)
580
+ .join('\n'));
581
+ });
582
+ }
583
+
309
584
  if (lines.length === 1) return ''; // nothing to add
310
585
  lines.push('[END USER CONTEXT]\n');
311
586
  return lines.join('\n');
@@ -324,7 +599,11 @@ function getCachedResponse(query) {
324
599
  // Optional: check TTL (e.g., 24 hours)
325
600
  const age = Date.now() - new Date(row.created_at).getTime();
326
601
  if (age < 24 * 60 * 60 * 1000) {
327
- return JSON.parse(row.response);
602
+ const parsed = JSON.parse(row.response);
603
+ if (parsed && typeof parsed.response === 'string') {
604
+ parsed.response = stripRelevantMemoryBlock(parsed.response);
605
+ }
606
+ return parsed;
328
607
  }
329
608
  }
330
609
  } catch (_) {}
@@ -334,11 +613,19 @@ function getCachedResponse(query) {
334
613
  function cacheResponse(query, responseObj) {
335
614
  try {
336
615
  const hash = crypto.createHash('md5').update(query.trim().toLowerCase()).digest('hex');
616
+ const sanitized = (responseObj && typeof responseObj === 'object')
617
+ ? {
618
+ ...responseObj,
619
+ response: typeof responseObj.response === 'string'
620
+ ? stripRelevantMemoryBlock(responseObj.response)
621
+ : responseObj.response
622
+ }
623
+ : responseObj;
337
624
  getDb().prepare(`
338
625
  INSERT INTO response_cache (query_hash, response, created_at)
339
626
  VALUES (?, ?, CURRENT_TIMESTAMP)
340
627
  ON CONFLICT(query_hash) DO UPDATE SET response = excluded.response, created_at = CURRENT_TIMESTAMP
341
- `).run(hash, JSON.stringify(responseObj));
628
+ `).run(hash, JSON.stringify(sanitized));
342
629
  } catch (_) {}
343
630
  }
344
631
 
@@ -358,7 +645,16 @@ module.exports = {
358
645
  deleteProfile,
359
646
  clearConversationScopedProfile,
360
647
  getProfile,
648
+ getAllProfile,
649
+ addLearnedSkill,
650
+ getLearnedSkills,
651
+ deleteLearnedSkill,
361
652
  getTopPatterns,
653
+ getRecentInteractions,
654
+ searchInteractions,
655
+ deleteInteractionMemory,
656
+ clearInteractionMemories,
657
+ exportMemorySnapshot,
362
658
  getRecentMemories,
363
659
  getCachedResponse,
364
660
  cacheResponse,
@@ -187,6 +187,7 @@ async function runChatRoutedTask(input, context) {
187
187
  }, 1000);
188
188
 
189
189
  try {
190
+ let streamedFinalSummary = false;
190
191
  const result = await executeCodeTask(text, {
191
192
  cwd: process.cwd(),
192
193
  requestApproval,
@@ -199,21 +200,32 @@ async function runChatRoutedTask(input, context) {
199
200
  } else {
200
201
  appendMessage('system', `[Code] ${typeof info === 'string' ? info : (info.action || info.phase)}`);
201
202
  }
203
+ },
204
+ onFinalSummary: async (info) => {
205
+ if (typeof context.streamAssistantSentences !== 'function') {
206
+ return;
207
+ }
208
+ clearInterval(timer);
209
+ setThinking(false);
210
+ streamedFinalSummary = true;
211
+ await context.streamAssistantSentences(info.summary, appendMessage, { providerInfo: info.providerInfo }, context.streamMessage);
202
212
  }
203
213
  });
204
214
  clearInterval(timer);
205
215
  setThinking(false);
206
- appendMessage('assistant', [
207
- `Code Mode finished.`,
208
- result.summary,
209
- `Verification: ${result.verification}`
210
- ].join('\n'), { providerInfo: result.providerInfo });
216
+ if (!streamedFinalSummary) {
217
+ appendMessage('assistant', [
218
+ `Code Mode finished.`,
219
+ result.summary,
220
+ `Verification: ${result.verification}`
221
+ ].join('\n'), { providerInfo: result.providerInfo });
222
+ }
211
223
  } catch (error) {
212
224
  clearInterval(timer);
213
225
  setThinking(false);
214
226
  appendMessage('error', error.message);
215
227
  } finally {
216
- if (setMode) setMode('Chat');
228
+ if (setMode) setMode('Agent');
217
229
  }
218
230
  }
219
231