@pheem49/mint 1.5.0 → 1.5.2

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 (101) hide show
  1. package/README.md +35 -1
  2. package/main.js +28 -14
  3. package/mint-cli-logic.js +3 -119
  4. package/mint-cli.js +201 -500
  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 +40 -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 +15 -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 +40 -17
  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/AI_Brain/proactive_engine.js +12 -2
  30. package/src/Automation_Layer/browser_automation.js +26 -24
  31. package/src/CLI/approval_handler.js +42 -0
  32. package/src/CLI/chat_router.js +18 -6
  33. package/src/CLI/chat_ui.js +583 -52
  34. package/src/CLI/cli_colors.js +32 -0
  35. package/src/CLI/cli_formatters.js +89 -0
  36. package/src/CLI/code_agent.js +369 -71
  37. package/src/CLI/image_input.js +90 -0
  38. package/src/CLI/intent_detectors.js +181 -0
  39. package/src/CLI/interactive_chat.js +479 -0
  40. package/src/CLI/list_features.js +3 -0
  41. package/src/CLI/onboarding.js +72 -15
  42. package/src/CLI/repo_summarizer.js +282 -0
  43. package/src/CLI/semantic_code_search.js +312 -0
  44. package/src/CLI/skill_manager.js +41 -0
  45. package/src/CLI/slash_command_handler.js +418 -0
  46. package/src/CLI/symbol_indexer.js +231 -0
  47. package/src/CLI/updater.js +6 -4
  48. package/src/Channels/discord_bridge.js +11 -13
  49. package/src/Channels/line_bridge.js +10 -10
  50. package/src/Channels/slack_bridge.js +7 -12
  51. package/src/Channels/telegram_bridge.js +6 -14
  52. package/src/Channels/whatsapp_bridge.js +11 -9
  53. package/src/System/action_executor.js +59 -10
  54. package/src/System/chat_history_manager.js +20 -12
  55. package/src/System/config_manager.js +31 -1
  56. package/src/System/granular_automation.js +122 -53
  57. package/src/System/optional_require.js +23 -0
  58. package/src/System/proactive_loop.js +19 -3
  59. package/src/System/safety_manager.js +108 -0
  60. package/src/System/sandbox_runner.js +182 -0
  61. package/src/System/system_automation.js +127 -81
  62. package/src/System/system_info.js +70 -0
  63. package/src/System/tool_registry.js +280 -0
  64. package/src/System/window_manager.js +4 -2
  65. package/src/UI/live2d_manager.js +566 -0
  66. package/src/UI/renderer.js +339 -21
  67. package/src/UI/settings.css +655 -420
  68. package/src/UI/settings.html +478 -432
  69. package/src/UI/settings.js +10 -8
  70. package/src/UI/styles.css +516 -31
  71. package/.codex +0 -0
  72. package/docs/assets/Agent_Mint.png +0 -0
  73. package/docs/assets/CLI_Screen.png +0 -0
  74. package/docs/assets/Settings.png +0 -0
  75. package/docs/assets/icon.png +0 -0
  76. package/docs/guide.html +0 -632
  77. package/docs/index.html +0 -133
  78. package/docs/style.css +0 -579
  79. package/index.html +0 -16
  80. package/src/UI/index.html +0 -126
  81. package/tech_news.txt +0 -3
  82. package/test_knowledge.txt +0 -3
  83. package/tests/action_executor_safety.test.js +0 -67
  84. package/tests/agent_orchestrator.test.js +0 -41
  85. package/tests/chat_router.test.js +0 -42
  86. package/tests/code_agent.test.js +0 -69
  87. package/tests/config_manager.test.js +0 -141
  88. package/tests/docker.test.js +0 -46
  89. package/tests/file_operations.test.js +0 -57
  90. package/tests/gmail.test.js +0 -135
  91. package/tests/gmail_auth.test.js +0 -129
  92. package/tests/google_calendar.test.js +0 -113
  93. package/tests/google_tts_urls.test.js +0 -24
  94. package/tests/memory_store.test.js +0 -185
  95. package/tests/notion.test.js +0 -121
  96. package/tests/provider_routing.test.js +0 -83
  97. package/tests/safety_manager.test.js +0 -40
  98. package/tests/spotify.test.js +0 -201
  99. package/tests/system_monitor.test.js +0 -37
  100. package/tests/updater.test.js +0 -32
  101. package/tests/workspace_manager.test.js +0 -56
@@ -1,9 +1,15 @@
1
- const { Client, GatewayIntentBits, Partials } = require('discord.js');
1
+ 'use strict';
2
+
3
+ const { requireOptional } = require('../System/optional_require');
2
4
  const { handleChat } = require('../AI_Brain/Gemini_API');
3
5
 
4
6
  class DiscordBridge {
5
7
  constructor(token) {
6
8
  this.token = token;
9
+ const { Client, GatewayIntentBits, Partials } = requireOptional(
10
+ 'discord.js',
11
+ 'npm install discord.js'
12
+ );
7
13
  this.client = new Client({
8
14
  intents: [
9
15
  GatewayIntentBits.Guilds,
@@ -21,30 +27,22 @@ class DiscordBridge {
21
27
  });
22
28
 
23
29
  this.client.on('messageCreate', async (message) => {
24
- // Ignore bot messages
25
30
  if (message.author.bot) return;
26
-
27
- // Handle DMs or Mentions
28
31
  const isDM = !message.guild;
29
32
  const isMentioned = message.mentions.has(this.client.user);
30
33
 
31
34
  if (isDM || isMentioned) {
32
35
  try {
33
- // Clean up the message if it's a mention
34
36
  let cleanContent = message.content;
35
37
  if (isMentioned) {
36
- cleanContent = message.content.replace(`<@!${this.client.user.id}>`, '').replace(`<@${this.client.user.id}>`, '').trim();
38
+ cleanContent = message.content
39
+ .replace(`<@!${this.client.user.id}>`, '')
40
+ .replace(`<@${this.client.user.id}>`, '')
41
+ .trim();
37
42
  }
38
-
39
43
  if (!cleanContent) return;
40
-
41
- // Show typing indicator
42
44
  await message.channel.sendTyping();
43
-
44
- // Send to Mint AI Brain
45
45
  const result = await handleChat(cleanContent);
46
-
47
- // Reply to user
48
46
  if (result && result.response) {
49
47
  await message.reply(result.response);
50
48
  }
@@ -1,22 +1,25 @@
1
- const line = require('@line/bot-sdk');
2
- const express = require('express');
1
+ 'use strict';
2
+
3
+ const { requireOptional } = require('../System/optional_require');
3
4
  const { handleChat } = require('../AI_Brain/Gemini_API');
4
5
 
5
6
  class LineBridge {
6
7
  constructor(credentials) {
8
+ this._line = requireOptional('@line/bot-sdk', 'npm install @line/bot-sdk express');
9
+ this._express = requireOptional('express', 'npm install @line/bot-sdk express');
7
10
  this.config = {
8
11
  channelAccessToken: credentials.accessToken,
9
12
  channelSecret: credentials.secret,
10
13
  };
11
- this.port = credentials.port || 3000;
12
- this.client = new line.messagingApi.MessagingApiClient({
14
+ this.port = credentials.port || 3000;
15
+ this.client = new this._line.messagingApi.MessagingApiClient({
13
16
  channelAccessToken: credentials.accessToken
14
17
  });
15
- this.app = express();
18
+ this.app = this._express();
16
19
  }
17
20
 
18
21
  async connect() {
19
- this.app.post('/callback', line.middleware(this.config), (req, res) => {
22
+ this.app.post('/callback', this._line.middleware(this.config), (req, res) => {
20
23
  Promise
21
24
  .all(req.body.events.map(event => this.handleEvent(event)))
22
25
  .then((result) => res.json(result))
@@ -36,7 +39,6 @@ class LineBridge {
36
39
  if (event.type !== 'message' || event.message.type !== 'text') {
37
40
  return Promise.resolve(null);
38
41
  }
39
-
40
42
  try {
41
43
  const result = await handleChat(event.message.text);
42
44
  if (result && result.response) {
@@ -51,9 +53,7 @@ class LineBridge {
51
53
  }
52
54
 
53
55
  async disconnect() {
54
- if (this.server) {
55
- this.server.close();
56
- }
56
+ if (this.server) this.server.close();
57
57
  }
58
58
  }
59
59
 
@@ -1,8 +1,11 @@
1
- const { App } = require('@slack/bolt');
1
+ 'use strict';
2
+
3
+ const { requireOptional } = require('../System/optional_require');
2
4
  const { handleChat } = require('../AI_Brain/Gemini_API');
3
5
 
4
6
  class SlackBridge {
5
7
  constructor(credentials) {
8
+ const { App } = requireOptional('@slack/bolt', 'npm install @slack/bolt');
6
9
  this.app = new App({
7
10
  token: credentials.botToken,
8
11
  appToken: credentials.appToken,
@@ -15,24 +18,18 @@ class SlackBridge {
15
18
  try {
16
19
  const text = event.text.replace(/<@.*?>/g, '').trim();
17
20
  if (!text) return;
18
-
19
21
  const result = await handleChat(text);
20
- if (result && result.response) {
21
- await say(result.response);
22
- }
22
+ if (result && result.response) await say(result.response);
23
23
  } catch (err) {
24
24
  console.error('[Slack Bridge] Error processing app_mention:', err);
25
25
  }
26
26
  });
27
27
 
28
28
  this.app.event('message', async ({ event, say }) => {
29
- // Only respond in DMs
30
29
  if (event.channel_type === 'im') {
31
30
  try {
32
31
  const result = await handleChat(event.text);
33
- if (result && result.response) {
34
- await say(result.response);
35
- }
32
+ if (result && result.response) await say(result.response);
36
33
  } catch (err) {
37
34
  console.error('[Slack Bridge] Error processing message:', err);
38
35
  }
@@ -44,9 +41,7 @@ class SlackBridge {
44
41
  }
45
42
 
46
43
  async disconnect() {
47
- if (this.app) {
48
- await this.app.stop();
49
- }
44
+ if (this.app) await this.app.stop();
50
45
  }
51
46
  }
52
47
 
@@ -1,9 +1,12 @@
1
- const { Telegraf } = require('telegraf');
1
+ 'use strict';
2
+
3
+ const { requireOptional } = require('../System/optional_require');
2
4
  const { handleChat } = require('../AI_Brain/Gemini_API');
3
5
 
4
6
  class TelegramBridge {
5
7
  constructor(token) {
6
8
  this.token = token;
9
+ const { Telegraf } = requireOptional('telegraf', 'npm install telegraf');
7
10
  this.bot = new Telegraf(token);
8
11
  }
9
12
 
@@ -12,19 +15,11 @@ class TelegramBridge {
12
15
 
13
16
  this.bot.on('text', async (ctx) => {
14
17
  try {
15
- // Show typing status
16
18
  await ctx.sendChatAction('typing');
17
-
18
19
  const message = ctx.message.text;
19
20
  if (!message) return;
20
-
21
- // Send to Mint AI Brain
22
21
  const result = await handleChat(message);
23
-
24
- // Reply to user
25
- if (result && result.response) {
26
- await ctx.reply(result.response);
27
- }
22
+ if (result && result.response) await ctx.reply(result.response);
28
23
  } catch (err) {
29
24
  console.error('[Telegram Bridge] Error processing message:', err);
30
25
  await ctx.reply('ขออภัยค่ะ เกิดข้อผิดพลาดบางอย่างในการประมวลผลข้อความ');
@@ -34,15 +29,12 @@ class TelegramBridge {
34
29
  this.bot.launch();
35
30
  console.log('[Telegram Bridge] Bot started!');
36
31
 
37
- // Enable graceful stop
38
32
  process.once('SIGINT', () => this.bot.stop('SIGINT'));
39
33
  process.once('SIGTERM', () => this.bot.stop('SIGTERM'));
40
34
  }
41
35
 
42
36
  async disconnect() {
43
- if (this.bot) {
44
- await this.bot.stop();
45
- }
37
+ if (this.bot) await this.bot.stop();
46
38
  }
47
39
  }
48
40
 
@@ -1,9 +1,16 @@
1
- const { Client, LocalAuth } = require('whatsapp-web.js');
2
- const qrcode = require('qrcode-terminal');
1
+ 'use strict';
2
+
3
+ const { requireOptional } = require('../System/optional_require');
3
4
  const { handleChat } = require('../AI_Brain/Gemini_API');
4
5
 
5
6
  class WhatsappBridge {
6
7
  constructor() {
8
+ // Dynamic require — only loads if user has installed whatsapp-web.js
9
+ const { Client, LocalAuth } = requireOptional(
10
+ 'whatsapp-web.js',
11
+ 'npm install whatsapp-web.js qrcode-terminal'
12
+ );
13
+ this._qrcode = requireOptional('qrcode-terminal', 'npm install qrcode-terminal');
7
14
  this.client = new Client({
8
15
  authStrategy: new LocalAuth({
9
16
  dataPath: require('path').join(require('os').homedir(), '.config', 'mint', 'whatsapp-session')
@@ -17,7 +24,7 @@ class WhatsappBridge {
17
24
  async connect() {
18
25
  this.client.on('qr', (qr) => {
19
26
  console.log('[WhatsApp Bridge] Scan this QR code to login:');
20
- qrcode.generate(qr, { small: true });
27
+ this._qrcode.generate(qr, { small: true });
21
28
  });
22
29
 
23
30
  this.client.on('ready', () => {
@@ -26,13 +33,8 @@ class WhatsappBridge {
26
33
 
27
34
  this.client.on('message', async (msg) => {
28
35
  try {
29
- // Ignore messages from groups unless mentioned (simple implementation)
30
36
  const chat = await msg.getChat();
31
- if (chat.isGroup) {
32
- // For groups, we could add a mention check here if desired
33
- return;
34
- }
35
-
37
+ if (chat.isGroup) return;
36
38
  const result = await handleChat(msg.body);
37
39
  if (result && result.response) {
38
40
  await msg.reply(result.response);
@@ -1,19 +1,33 @@
1
- const { clipboard: electronClipboard } = require('electron');
1
+ let electronClipboard = null;
2
+ try {
3
+ ({ clipboard: electronClipboard } = require('electron'));
4
+ } catch (_) {
5
+ electronClipboard = {
6
+ writeText: () => {}
7
+ };
8
+ }
2
9
  const { openApp } = require('../Automation_Layer/open_app');
3
10
  const { openWebsite, openSearch } = require('../Automation_Layer/open_website');
4
11
  const { performWebAutomation } = require('../Automation_Layer/browser_automation');
5
12
  const { createFolder, openFile, deleteFile, findPath } = require('../Automation_Layer/file_operations');
6
13
  const { indexFile, indexFolder } = require('../AI_Brain/knowledge_base');
14
+ const { getSystemInfo, getWeather } = require('./system_info');
7
15
  const pluginManager = require('../Plugins/plugin_manager');
8
16
  const mcpManager = require('../Plugins/mcp_manager');
9
- const granularAutomation = require('./granular_automation');
10
17
  const SystemAutomation = require('./system_automation');
11
18
  const safetyManager = require('./safety_manager');
19
+ const toolRegistry = require('./tool_registry');
20
+ const os = require('os');
21
+ const path = require('path');
12
22
 
13
23
  async function executeAction(action, options = {}) {
14
- console.log("Executing action:", action);
24
+ if (process.env.MINT_DEBUG === '1') {
25
+ console.log("Executing action:", action);
26
+ }
27
+ toolRegistry.validateToolInput(action.type, action);
15
28
  const clipboard = options.clipboard || electronClipboard;
16
29
  const safety = safetyManager.assertActionAllowed(action, {
30
+ allowApproval: options.allowApproval === true,
17
31
  allowDangerous: options.allowDangerous === true
18
32
  });
19
33
  safetyManager.appendActionLog({
@@ -21,7 +35,7 @@ async function executeAction(action, options = {}) {
21
35
  action: action.type,
22
36
  target: action.target || action.path || '',
23
37
  tier: safety.tier,
24
- approved: options.allowDangerous === true || safety.tier !== safetyManager.TIERS.DANGEROUS
38
+ approved: options.allowApproval === true || options.allowDangerous === true || safety.tier === safetyManager.TIERS.SAFE
25
39
  });
26
40
 
27
41
  switch (action.type) {
@@ -37,17 +51,23 @@ async function executeAction(action, options = {}) {
37
51
  case 'web_automation':
38
52
  return await performWebAutomation(action.target);
39
53
  case 'create_folder':
54
+ safetyManager.assertPathCapability(action.target, 'write', {
55
+ defaultBase: path.join(os.homedir(), 'Desktop')
56
+ });
40
57
  createFolder(action.target);
41
58
  break;
42
59
  case 'open_file': {
60
+ safetyManager.assertPathCapability(action.target, 'read');
43
61
  const fileRes = await openFile(action.target);
44
62
  return fileRes || `Successfully opened file: ${action.target} ✅`;
45
63
  }
46
64
  case 'open_folder': {
65
+ safetyManager.assertPathCapability(action.target, 'read');
47
66
  const folderRes = await openFile(action.target);
48
67
  return folderRes || `Successfully opened folder: ${action.target} ✅`;
49
68
  }
50
69
  case 'delete_file':
70
+ safetyManager.assertPathCapability(action.target, 'write');
51
71
  await deleteFile(action.target);
52
72
  break;
53
73
  case 'find_path':
@@ -56,21 +76,33 @@ async function executeAction(action, options = {}) {
56
76
  clipboard.writeText(action.target);
57
77
  break;
58
78
  case 'learn_file':
79
+ safetyManager.assertPathCapability(action.target, 'read');
59
80
  return await indexFile(action.target);
60
81
  case 'learn_folder':
82
+ safetyManager.assertPathCapability(action.target, 'read');
61
83
  return await indexFolder(action.target);
84
+ case 'system_info':
85
+ return await handleSystemInfo(action.target);
62
86
  case 'mcp_tool': {
63
87
  const mcpResult = await mcpManager.callTool(action.server, action.target, action.args);
64
88
  return JSON.stringify(mcpResult.content);
65
89
  }
66
- case 'mouse_move':
90
+ case 'mouse_move': {
91
+ const granularAutomation = require('./granular_automation');
67
92
  return await granularAutomation.mouseMove(action.x, action.y);
68
- case 'mouse_click':
93
+ }
94
+ case 'mouse_click': {
95
+ const granularAutomation = require('./granular_automation');
69
96
  return await granularAutomation.mouseClick(action.x, action.y, action.button || 1);
70
- case 'type_text':
97
+ }
98
+ case 'type_text': {
99
+ const granularAutomation = require('./granular_automation');
71
100
  return await granularAutomation.typeText(action.target);
72
- case 'key_tap':
101
+ }
102
+ case 'key_tap': {
103
+ const granularAutomation = require('./granular_automation');
73
104
  return await granularAutomation.keyTap(action.target);
105
+ }
74
106
  case 'plugin':
75
107
  return await pluginManager.executePlugin(action.pluginName, action.target);
76
108
  case 'system_automation':
@@ -80,10 +112,27 @@ async function executeAction(action, options = {}) {
80
112
  }
81
113
  }
82
114
 
115
+ async function handleSystemInfo(target = '') {
116
+ const query = String(target || '').trim();
117
+ if (query) {
118
+ const weather = await getWeather(query);
119
+ return JSON.stringify({
120
+ type: 'weather',
121
+ target: query,
122
+ ...weather
123
+ });
124
+ }
125
+ return JSON.stringify({
126
+ type: 'system_info',
127
+ data: getSystemInfo()
128
+ });
129
+ }
130
+
83
131
  async function executeFindPath(action) {
84
132
  const result = findPath(action.target, {
85
133
  type: action.pathType,
86
- maxResults: 10
134
+ maxResults: 10,
135
+ roots: safetyManager.getAllowedRoots('read')
87
136
  });
88
137
  if (!result.success) {
89
138
  return result.message;
@@ -126,4 +175,4 @@ async function handleSystemAutomation(target) {
126
175
  }
127
176
  }
128
177
 
129
- module.exports = { executeAction, handleSystemAutomation };
178
+ module.exports = { executeAction, handleSystemAutomation, handleSystemInfo };
@@ -19,21 +19,29 @@ if (!fs.existsSync(CONFIG_DIR)) {
19
19
 
20
20
  const CHAT_HISTORY_PATH = path.join(CONFIG_DIR, 'mint-chat-history.json');
21
21
 
22
- // Migration Logic: Consolidate from Electron userData or old ~/.mint to ~/.config/mint
22
+ // Migration Logic: Consolidate from various legacy locations to ~/.config/mint/
23
23
  if (!fs.existsSync(CHAT_HISTORY_PATH)) {
24
24
  const electronUserData = app && app.getPath ? path.join(app.getPath('userData'), 'mint-chat-history.json') : null;
25
- const legacyPath = path.join(MINT_DIR, 'mint-chat-history.json');
25
+ const legacyDotMint = path.join(MINT_DIR, 'mint-chat-history.json');
26
+ // Legacy: file was written to the project root (CWD) before v1.5.2
27
+ const legacyProjectRoot = path.join(process.cwd(), 'mint-chat-history.json');
26
28
 
27
- if (electronUserData && fs.existsSync(electronUserData)) {
28
- try {
29
- fs.copyFileSync(electronUserData, CHAT_HISTORY_PATH);
30
- console.log('[History] Migrated chat history from Electron userData');
31
- } catch (e) { console.error('[History] Migration from Electron failed:', e); }
32
- } else if (fs.existsSync(legacyPath)) {
33
- try {
34
- fs.copyFileSync(legacyPath, CHAT_HISTORY_PATH);
35
- console.log('[History] Migrated chat history from ~/.mint');
36
- } catch (e) { console.error('[History] Migration from ~/.mint failed:', e); }
29
+ const candidates = [
30
+ electronUserData,
31
+ legacyDotMint,
32
+ legacyProjectRoot
33
+ ].filter(Boolean);
34
+
35
+ for (const candidate of candidates) {
36
+ if (candidate !== CHAT_HISTORY_PATH && fs.existsSync(candidate)) {
37
+ try {
38
+ fs.copyFileSync(candidate, CHAT_HISTORY_PATH);
39
+ console.log(`[History] Migrated chat history from ${candidate}`);
40
+ } catch (e) {
41
+ console.error('[History] Migration failed:', e);
42
+ }
43
+ break;
44
+ }
37
45
  }
38
46
  }
39
47
 
@@ -99,7 +99,37 @@ const DEFAULT_CONFIG = {
99
99
  enableAgentCollaboration: false,
100
100
  enableAutoUpdate: true,
101
101
  autoUpdateCheckIntervalHours: 24,
102
- lastUpdateCheckAt: ''
102
+ lastUpdateCheckAt: '',
103
+ safetyEnabled: true,
104
+ sandboxMode: 'prefer', // off | prefer | enforce
105
+ sandboxCommand: process.platform === 'darwin' ? 'sandbox-exec' : process.platform === 'linux' ? 'bwrap' : '',
106
+ allowedReadPaths: [
107
+ os.homedir(),
108
+ process.cwd(),
109
+ path.join(os.homedir(), 'Desktop'),
110
+ path.join(os.homedir(), 'Documents'),
111
+ path.join(os.homedir(), 'Downloads'),
112
+ path.join(os.homedir(), 'Pictures'),
113
+ path.join(os.homedir(), 'Music'),
114
+ path.join(os.homedir(), 'Videos')
115
+ ],
116
+ allowedWritePaths: [
117
+ os.homedir(),
118
+ process.cwd(),
119
+ path.join(os.homedir(), 'Desktop'),
120
+ path.join(os.homedir(), 'Documents'),
121
+ path.join(os.homedir(), 'Downloads'),
122
+ path.join(os.homedir(), 'Pictures'),
123
+ path.join(os.homedir(), 'Music'),
124
+ path.join(os.homedir(), 'Videos')
125
+ ],
126
+ blockedPaths: [
127
+ path.join(os.homedir(), '.ssh'),
128
+ path.join(os.homedir(), '.gnupg'),
129
+ path.join(os.homedir(), '.config', 'mint', 'mint-config.json'),
130
+ path.join(os.homedir(), '.mint', 'mint-config.json')
131
+ ],
132
+ blockedFileNames: ['.env', 'id_rsa', 'id_ed25519']
103
133
  };
104
134
 
105
135