@pheem49/mint 1.5.5 → 1.6.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 (222) hide show
  1. package/.codex +0 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/.github/workflows/ci.yml +45 -0
  4. package/.github/workflows/release.yml +79 -0
  5. package/Cargo.lock +5792 -0
  6. package/Cargo.toml +32 -0
  7. package/README.md +387 -353
  8. package/assets/icon.png +0 -0
  9. package/bin/mint +0 -0
  10. package/crates/mint-cli/Cargo.toml +23 -0
  11. package/crates/mint-cli/src/agent.rs +851 -0
  12. package/crates/mint-cli/src/gmail.rs +216 -0
  13. package/crates/mint-cli/src/image.rs +142 -0
  14. package/crates/mint-cli/src/main.rs +2837 -0
  15. package/crates/mint-cli/src/mcp.rs +63 -0
  16. package/crates/mint-cli/src/onboard.rs +1149 -0
  17. package/crates/mint-cli/src/setup.rs +390 -0
  18. package/crates/mint-cli/src/skills.rs +8 -0
  19. package/crates/mint-cli/src/updater.rs +279 -0
  20. package/crates/mint-core/Cargo.toml +22 -0
  21. package/crates/mint-core/src/agent_loop.rs +94 -0
  22. package/crates/mint-core/src/api_server.rs +991 -0
  23. package/crates/mint-core/src/channels.rs +248 -0
  24. package/crates/mint-core/src/chat.rs +895 -0
  25. package/crates/mint-core/src/code_tools.rs +729 -0
  26. package/crates/mint-core/src/config.rs +368 -0
  27. package/crates/mint-core/src/files.rs +159 -0
  28. package/crates/mint-core/src/knowledge.rs +541 -0
  29. package/crates/mint-core/src/lib.rs +84 -0
  30. package/crates/mint-core/src/mcp.rs +273 -0
  31. package/crates/mint-core/src/memory.rs +673 -0
  32. package/crates/mint-core/src/orchestration.rs +2157 -0
  33. package/crates/mint-core/src/pictures.rs +314 -0
  34. package/crates/mint-core/src/plugins.rs +727 -0
  35. package/crates/mint-core/src/safety.rs +416 -0
  36. package/crates/mint-core/src/semantic.rs +254 -0
  37. package/crates/mint-core/src/shell.rs +317 -0
  38. package/crates/mint-core/src/skills.rs +71 -0
  39. package/crates/mint-core/src/symbols.rs +157 -0
  40. package/crates/mint-core/src/tasks.rs +308 -0
  41. package/crates/mint-core/src/tts.rs +92 -0
  42. package/crates/mint-core/src/weather.rs +93 -0
  43. package/crates/mint-core/src/web_search.rs +200 -0
  44. package/crates/mint-core/src/workflows.rs +81 -0
  45. package/crates/mint-core/tests/mcp_stdio.rs +45 -0
  46. package/crates/mint-core/tests/memory_persistence.rs +172 -0
  47. package/crates/mint-core/tests/pictures_storage.rs +14 -0
  48. package/crates/mint-core/tests/task_lifecycle.rs +87 -0
  49. package/package.json +35 -99
  50. package/src/bin/index.js +16 -0
  51. package/src/renderer/index-web.html +17 -0
  52. package/src/renderer/index.html +17 -0
  53. package/src/renderer/public/Live2DCubismCore.js +9 -0
  54. package/src/renderer/public/assets/icon.png +0 -0
  55. package/src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.model3.json +36 -0
  56. package/src/renderer/src/App.tsx +33 -0
  57. package/src/renderer/src/calculator.ts +47 -0
  58. package/src/renderer/src/components/ChatPanel.tsx +1598 -0
  59. package/src/renderer/src/components/DashboardSidebar.tsx +358 -0
  60. package/src/renderer/src/components/Live2DStage.tsx +374 -0
  61. package/src/renderer/src/components/MintDashboard.tsx +950 -0
  62. package/src/renderer/src/components/ModelPanel.tsx +154 -0
  63. package/src/renderer/src/components/PicturesLibrary.tsx +46 -0
  64. package/src/renderer/src/components/ProactiveGlow.tsx +19 -0
  65. package/src/renderer/src/components/ScreenPicker.tsx +579 -0
  66. package/src/renderer/src/components/SettingsWindow.tsx +1467 -0
  67. package/src/renderer/src/components/SpotlightWindow.tsx +280 -0
  68. package/src/renderer/src/components/WidgetWindow.tsx +36 -0
  69. package/src/renderer/src/components/WorkspacePanel.tsx +268 -0
  70. package/src/{UI → renderer/src/css}/settings.css +69 -16
  71. package/src/renderer/src/css/spotlight.css +113 -0
  72. package/src/renderer/src/css/styles.css +3722 -0
  73. package/src/renderer/src/css/widget.css +185 -0
  74. package/src/renderer/src/env.d.ts +116 -0
  75. package/src/renderer/src/index.css +379 -0
  76. package/src/renderer/src/main.tsx +13 -0
  77. package/src/renderer/src/tauri.ts +996 -0
  78. package/src/renderer/src-web/App.tsx +25 -0
  79. package/src/renderer/src-web/calculator.ts +47 -0
  80. package/src/renderer/src-web/components/ChatPanel.tsx +1662 -0
  81. package/src/renderer/src-web/components/DashboardSidebar.tsx +242 -0
  82. package/src/renderer/src-web/components/MintDashboard.tsx +763 -0
  83. package/src/renderer/src-web/components/PicturesLibrary.tsx +73 -0
  84. package/src/renderer/src-web/components/SettingsWindow.tsx +1500 -0
  85. package/src/renderer/src-web/css/settings.css +1100 -0
  86. package/src/{UI → renderer/src-web/css}/spotlight.css +4 -4
  87. package/src/{UI → renderer/src-web/css}/styles.css +1055 -159
  88. package/src/{UI → renderer/src-web/css}/widget.css +2 -2
  89. package/src/renderer/src-web/env.d.ts +107 -0
  90. package/src/renderer/src-web/index.css +379 -0
  91. package/src/renderer/src-web/main.tsx +13 -0
  92. package/src/renderer/src-web/tauri.ts +983 -0
  93. package/tsconfig.json +30 -0
  94. package/vite.config.ts +33 -0
  95. package/vite.config.web.ts +51 -0
  96. package/GUIDE_TH.md +0 -125
  97. package/assets/Agent_Mint.png +0 -0
  98. package/assets/CLI_Screen.png +0 -0
  99. package/assets/Settings.png +0 -0
  100. package/benchmark_ai.js +0 -71
  101. package/install.ps1 +0 -64
  102. package/install.sh +0 -54
  103. package/main.js +0 -139
  104. package/mint-cli-logic.js +0 -3
  105. package/mint-cli.js +0 -410
  106. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +0 -47
  107. 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 +0 -23
  108. package/preload-picker.js +0 -11
  109. package/preload-settings.js +0 -11
  110. package/preload.js +0 -41
  111. package/scripts/install_linux_desktop_entry.js +0 -48
  112. package/src/AI_Brain/Gemini_API.js +0 -813
  113. package/src/AI_Brain/agent_orchestrator.js +0 -73
  114. package/src/AI_Brain/autonomous_brain.js +0 -179
  115. package/src/AI_Brain/behavior_memory.js +0 -135
  116. package/src/AI_Brain/headless_agent.js +0 -143
  117. package/src/AI_Brain/knowledge_base.js +0 -349
  118. package/src/AI_Brain/memory_store.js +0 -662
  119. package/src/AI_Brain/proactive_engine.js +0 -172
  120. package/src/AI_Brain/provider_adapter.js +0 -365
  121. package/src/Automation_Layer/browser_automation.js +0 -149
  122. package/src/Automation_Layer/file_operations.js +0 -286
  123. package/src/Automation_Layer/open_app.js +0 -85
  124. package/src/Automation_Layer/open_website.js +0 -38
  125. package/src/CLI/approval_handler.js +0 -47
  126. package/src/CLI/chat_router.js +0 -247
  127. package/src/CLI/chat_ui.js +0 -1159
  128. package/src/CLI/cli_colors.js +0 -115
  129. package/src/CLI/cli_formatters.js +0 -94
  130. package/src/CLI/code_agent.js +0 -1667
  131. package/src/CLI/code_session_memory.js +0 -62
  132. package/src/CLI/gmail_auth.js +0 -210
  133. package/src/CLI/image_input.js +0 -90
  134. package/src/CLI/intent_detectors.js +0 -181
  135. package/src/CLI/interactive_chat.js +0 -658
  136. package/src/CLI/list_features.js +0 -64
  137. package/src/CLI/onboarding.js +0 -416
  138. package/src/CLI/repo_summarizer.js +0 -282
  139. package/src/CLI/semantic_code_search.js +0 -312
  140. package/src/CLI/skill_manager.js +0 -41
  141. package/src/CLI/slash_command_handler.js +0 -418
  142. package/src/CLI/symbol_indexer.js +0 -231
  143. package/src/CLI/updater.js +0 -230
  144. package/src/CLI/workspace_manager.js +0 -90
  145. package/src/Channels/brave_search_bridge.js +0 -35
  146. package/src/Channels/discord_bridge.js +0 -66
  147. package/src/Channels/google_search_bridge.js +0 -38
  148. package/src/Channels/line_bridge.js +0 -60
  149. package/src/Channels/slack_bridge.js +0 -48
  150. package/src/Channels/telegram_bridge.js +0 -41
  151. package/src/Channels/whatsapp_bridge.js +0 -57
  152. package/src/Command_Parser/parser.js +0 -45
  153. package/src/Plugins/dev_tools.js +0 -41
  154. package/src/Plugins/discord.js +0 -20
  155. package/src/Plugins/docker.js +0 -47
  156. package/src/Plugins/gmail.js +0 -251
  157. package/src/Plugins/google_calendar.js +0 -252
  158. package/src/Plugins/mcp_manager.js +0 -95
  159. package/src/Plugins/notion.js +0 -256
  160. package/src/Plugins/obsidian.js +0 -54
  161. package/src/Plugins/plugin_manager.js +0 -81
  162. package/src/Plugins/spotify.js +0 -173
  163. package/src/Plugins/system_metrics.js +0 -31
  164. package/src/Plugins/system_monitor.js +0 -72
  165. package/src/System/action_executor.js +0 -178
  166. package/src/System/bridge_manager.js +0 -76
  167. package/src/System/chat_history_manager.js +0 -83
  168. package/src/System/config_manager.js +0 -194
  169. package/src/System/custom_workflows.js +0 -163
  170. package/src/System/daemon_manager.js +0 -67
  171. package/src/System/google_tts_urls.js +0 -51
  172. package/src/System/granular_automation.js +0 -157
  173. package/src/System/ipc_handlers.js +0 -332
  174. package/src/System/notifications.js +0 -23
  175. package/src/System/optional_require.js +0 -23
  176. package/src/System/picture_store.js +0 -109
  177. package/src/System/proactive_loop.js +0 -153
  178. package/src/System/safety_manager.js +0 -273
  179. package/src/System/sandbox_runner.js +0 -182
  180. package/src/System/screen_capture.js +0 -175
  181. package/src/System/smart_context.js +0 -227
  182. package/src/System/system_automation.js +0 -162
  183. package/src/System/system_events.js +0 -79
  184. package/src/System/system_info.js +0 -125
  185. package/src/System/task_manager.js +0 -222
  186. package/src/System/tool_registry.js +0 -293
  187. package/src/System/window_manager.js +0 -220
  188. package/src/UI/floating.css +0 -80
  189. package/src/UI/floating.html +0 -17
  190. package/src/UI/floating.js +0 -67
  191. package/src/UI/live2d_manager.js +0 -600
  192. package/src/UI/preload-floating.js +0 -7
  193. package/src/UI/preload-spotlight.js +0 -11
  194. package/src/UI/preload-widget.js +0 -5
  195. package/src/UI/proactive-glow.html +0 -42
  196. package/src/UI/renderer.js +0 -2127
  197. package/src/UI/screenPicker.html +0 -214
  198. package/src/UI/screenPicker.js +0 -262
  199. package/src/UI/settings.html +0 -577
  200. package/src/UI/settings.js +0 -770
  201. package/src/UI/spotlight.html +0 -23
  202. package/src/UI/spotlight.js +0 -185
  203. package/src/UI/widget.html +0 -29
  204. package/src/UI/widget.js +0 -10
  205. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  206. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/apron.exp3.json} +0 -0
  207. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/catfilter.exp3.json} +0 -0
  208. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/click.exp3.json} +0 -0
  209. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazed.exp3.json} +0 -0
  210. /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" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazedeyes.exp3.json} +0 -0
  211. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/glasses.exp3.json} +0 -0
  212. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +0 -0
  213. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/pen.exp3.json} +0 -0
  214. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/photo.exp3.json} +0 -0
  215. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_00.png} +0 -0
  216. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_01.png} +0 -0
  217. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_02.png} +0 -0
  218. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_03.png} +0 -0
  219. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.cdi3.json} +0 -0
  220. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.moc3} +0 -0
  221. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.physics3.json} +0 -0
  222. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.vtube.json} +0 -0
@@ -1,662 +0,0 @@
1
- /**
2
- * Mint Long-Term Memory Store
3
- * ---------------------------
4
- * Persists user preferences, session summaries, and usage patterns
5
- * across all Mint sessions using SQLite (same DB as knowledge_base).
6
- *
7
- * Auto-injects a "User Context" block into the system prompt so Mint
8
- * remembers who it's talking to even after restart.
9
- */
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const os = require('os');
14
- const crypto = require('crypto');
15
- const { readConfig } = require('../System/config_manager');
16
-
17
- // ── Electron-safe app path ──────────────────────────────────────────────────
18
- let app;
19
- try {
20
- const electron = require('electron');
21
- app = electron.app;
22
- } catch (_) {
23
- app = null;
24
- }
25
-
26
- function getDbPath() {
27
- const fileName = 'mint-knowledge.sqlite'; // shared DB with knowledge_base
28
- const configDir = path.join(os.homedir(), '.config', 'mint');
29
- const dbPath = path.join(configDir, fileName);
30
-
31
- if (!fs.existsSync(configDir)) {
32
- fs.mkdirSync(configDir, { recursive: true });
33
- }
34
-
35
- // Migration Logic
36
- if (!fs.existsSync(dbPath)) {
37
- const electronDb = app && app.getPath ? path.join(app.getPath('userData'), fileName) : null;
38
- const legacyDb = path.join(os.homedir(), '.mint', fileName);
39
-
40
- if (electronDb && fs.existsSync(electronDb)) {
41
- try {
42
- fs.copyFileSync(electronDb, dbPath);
43
- console.log('[Memory] Migrated database from Electron userData');
44
- } catch (e) { console.error('[Memory] Migration from Electron failed:', e); }
45
- } else if (fs.existsSync(legacyDb)) {
46
- try {
47
- fs.copyFileSync(legacyDb, dbPath);
48
- console.log('[Memory] Migrated database from ~/.mint');
49
- } catch (e) { console.error('[Memory] Migration from ~/.mint failed:', e); }
50
- }
51
- }
52
-
53
- return dbPath;
54
- }
55
-
56
- // ── Lazy DatabaseSync init ─────────────────────────────────────────────────
57
- let DatabaseSync = null;
58
- function getDatabaseSync() {
59
- if (!DatabaseSync) ({ DatabaseSync } = require('node:sqlite'));
60
- return DatabaseSync;
61
- }
62
-
63
- let dbInstance = null;
64
- function getDb() {
65
- if (dbInstance) return dbInstance;
66
- const Database = getDatabaseSync();
67
- dbInstance = new Database(getDbPath());
68
-
69
- // Enable WAL mode for better concurrency
70
- dbInstance.exec('PRAGMA journal_mode = WAL;');
71
- dbInstance.exec('PRAGMA synchronous = NORMAL;');
72
-
73
- dbInstance.exec(`
74
- -- User profile: arbitrary key-value pairs
75
- CREATE TABLE IF NOT EXISTS user_profile (
76
- key TEXT PRIMARY KEY,
77
- value TEXT,
78
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
79
- );
80
-
81
- -- Condensed summaries of past sessions
82
- CREATE TABLE IF NOT EXISTS session_memories (
83
- id INTEGER PRIMARY KEY AUTOINCREMENT,
84
- summary TEXT NOT NULL,
85
- tags TEXT DEFAULT '',
86
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
87
- );
88
-
89
- -- Frequently used topics / commands
90
- CREATE TABLE IF NOT EXISTS usage_patterns (
91
- pattern TEXT PRIMARY KEY,
92
- count INTEGER DEFAULT 1,
93
- last_used DATETIME DEFAULT CURRENT_TIMESTAMP
94
- );
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
-
105
- -- Response Cache: For repetitive exact queries
106
- CREATE TABLE IF NOT EXISTS response_cache (
107
- query_hash TEXT PRIMARY KEY,
108
- response TEXT NOT NULL,
109
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
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
- );
121
- `);
122
-
123
- return dbInstance;
124
- }
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
-
139
- // ── Profile helpers ────────────────────────────────────────────────────────
140
- function setProfile(key, value) {
141
- try {
142
- const db = getDb();
143
- db.prepare(`
144
- INSERT INTO user_profile (key, value, updated_at)
145
- VALUES (?, ?, CURRENT_TIMESTAMP)
146
- ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
147
- `).run(key, String(value));
148
- } catch (err) {
149
- console.error('[Memory] setProfile error:', err.message);
150
- }
151
- }
152
-
153
- function deleteProfile(key) {
154
- try {
155
- getDb().prepare('DELETE FROM user_profile WHERE key = ?').run(key);
156
- } catch (err) {
157
- console.error('[Memory] deleteProfile error:', err.message);
158
- }
159
- }
160
-
161
- function clearConversationScopedProfile() {
162
- deleteProfile('preferred_language');
163
- clearResponseCache();
164
- }
165
-
166
- function getProfile(key, defaultValue = null) {
167
- try {
168
- const row = getDb().prepare('SELECT value FROM user_profile WHERE key = ?').get(key);
169
- return row ? row.value : defaultValue;
170
- } catch (_) {
171
- return defaultValue;
172
- }
173
- }
174
-
175
- function getAllProfile() {
176
- try {
177
- const rows = getDb().prepare('SELECT key, value FROM user_profile').all();
178
- return Object.fromEntries(rows.map(r => [r.key, r.value]));
179
- } catch (_) {
180
- return {};
181
- }
182
- }
183
-
184
- // ── Session memory helpers ─────────────────────────────────────────────────
185
- const MAX_SESSION_MEMORIES = 20; // keep last N summaries
186
-
187
- function addSessionMemory(summary, tags = []) {
188
- try {
189
- const db = getDb();
190
- db.prepare('INSERT INTO session_memories (summary, tags) VALUES (?, ?)').run(
191
- summary.slice(0, 800), // cap length
192
- tags.join(',')
193
- );
194
- // Prune oldest beyond limit
195
- db.exec(`
196
- DELETE FROM session_memories WHERE id NOT IN (
197
- SELECT id FROM session_memories ORDER BY id DESC LIMIT ${MAX_SESSION_MEMORIES}
198
- )
199
- `);
200
- } catch (err) {
201
- console.error('[Memory] addSessionMemory error:', err.message);
202
- }
203
- }
204
-
205
- function getRecentMemories(limit = 5) {
206
- try {
207
- return getDb()
208
- .prepare('SELECT summary, tags, created_at FROM session_memories ORDER BY id DESC LIMIT ?')
209
- .all(limit);
210
- } catch (_) {
211
- return [];
212
- }
213
- }
214
-
215
- // ── Usage pattern helpers ──────────────────────────────────────────────────
216
- function recordPattern(pattern) {
217
- try {
218
- const db = getDb();
219
- db.prepare(`
220
- INSERT INTO usage_patterns (pattern, count, last_used)
221
- VALUES (?, 1, CURRENT_TIMESTAMP)
222
- ON CONFLICT(pattern) DO UPDATE
223
- SET count = count + 1, last_used = CURRENT_TIMESTAMP
224
- `).run(pattern.slice(0, 120));
225
- } catch (_) {}
226
- }
227
-
228
- function getTopPatterns(limit = 8) {
229
- try {
230
- return getDb()
231
- .prepare('SELECT pattern, count FROM usage_patterns ORDER BY count DESC, last_used DESC LIMIT ?')
232
- .all(limit);
233
- } catch (_) {
234
- return [];
235
- }
236
- }
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
-
348
- // ── Simple keyword extractor (no external deps) ────────────────────────────
349
- const STOP_WORDS = new Set([
350
- 'ที่', 'ให้', 'และ', 'ของ', 'กับ', 'ใน', 'บน', 'เป็น', 'อยู่', 'มี', 'ได้', 'the', 'a', 'an',
351
- 'is', 'are', 'was', 'were', 'it', 'in', 'on', 'at', 'for', 'to', 'of', 'with', 'and', 'or',
352
- 'this', 'that', 'i', 'you', 'me', 'my', 'your', 'can', 'do', 'be', 'will', 'please', 'how',
353
- 'what', 'which', 'when', 'where', 'why', 'help', 'want', 'need', 'make', 'create', 'get', 'run'
354
- ]);
355
-
356
- function extractKeywords(text) {
357
- return text
358
- .toLowerCase()
359
- .replace(/[^\w\u0E00-\u0E7F\s]/g, ' ')
360
- .split(/\s+/)
361
- .filter(w => w.length > 2 && !STOP_WORDS.has(w))
362
- .slice(0, 6);
363
- }
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
-
396
- // ── Main public API ────────────────────────────────────────────────────────
397
-
398
- /**
399
- * Called after every successful chat turn.
400
- * Extracts patterns & infers preferences — runs async, non-blocking.
401
- */
402
- function recordInteraction(userMessage, aiResponseText) {
403
- try {
404
- if (!userMessage || !aiResponseText) return;
405
-
406
- // Extract keywords as usage patterns
407
- const keywords = extractKeywords(userMessage);
408
- keywords.forEach(kw => recordPattern(kw));
409
- addInteractionMemory(userMessage, aiResponseText, keywords);
410
-
411
- // Detect preferred language
412
- const thaiRatio = (userMessage.match(/[\u0E00-\u0E7F]/g) || []).length / userMessage.length;
413
- if (thaiRatio > 0.3) setProfile('preferred_language', 'thai');
414
- else setProfile('preferred_language', 'english');
415
-
416
- const userName = extractUserName(userMessage);
417
- if (userName) {
418
- setProfile('user_name', userName);
419
- }
420
-
421
- // Detect coding intent (update project activity)
422
- const codingKeywords = ['code', 'fix', 'debug', 'function', 'class', 'import', 'script',
423
- 'แก้', 'เขียน', 'โค้ด', 'สคริปต์', 'ฟังก์ชัน'];
424
- if (codingKeywords.some(k => userMessage.toLowerCase().includes(k))) {
425
- const cwd = process.cwd();
426
- if (cwd !== os.homedir()) {
427
- setProfile('last_active_project', path.basename(cwd));
428
- setProfile('last_active_project_path', cwd);
429
- }
430
- }
431
-
432
- // Update interaction counter
433
- const count = parseInt(getProfile('total_interactions', '0'), 10);
434
- setProfile('total_interactions', String(count + 1));
435
- setProfile('last_seen', new Date().toISOString());
436
- } catch (err) {
437
- console.error('[Memory] recordInteraction error:', err.message);
438
- }
439
- }
440
-
441
- /**
442
- * Saves a condensed summary of a completed conversation.
443
- * Call this when user clears history or after N turns.
444
- */
445
- function saveSessionSummary(summary, tags = []) {
446
- if (!summary || summary.trim().length < 10) return;
447
- addSessionMemory(summary.trim(), tags);
448
- }
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
-
512
- /**
513
- * Returns a formatted context string to inject into the AI system prompt.
514
- * Lightweight — no async calls.
515
- */
516
- function getUserContext(query = '') {
517
- try {
518
- const profile = getAllProfile();
519
- const patterns = getTopPatterns(6);
520
- const memories = getRecentMemories(3);
521
- const interactions = getRecentInteractions(6);
522
- const relevantInteractions = query ? searchInteractions(query, 5) : [];
523
-
524
- const lines = ['\n\n[LONG-TERM USER CONTEXT — use this to personalize responses]'];
525
-
526
- // Profile info
527
- if (Object.keys(profile).length > 0) {
528
- if (profile.user_name)
529
- lines.push(`• User name: ${profile.user_name}`);
530
- if (profile.preferred_language)
531
- lines.push(`• Previously inferred language: ${profile.preferred_language} (do not override the current user message language)`);
532
- if (profile.last_active_project)
533
- lines.push(`• Last active project: ${profile.last_active_project} (${profile.last_active_project_path || ''})`);
534
- if (profile.total_interactions)
535
- lines.push(`• Total interactions with Mint: ${profile.total_interactions}`);
536
- if (profile.last_seen) {
537
- const d = new Date(profile.last_seen);
538
- lines.push(`• Last session: ${d.toLocaleDateString('th-TH')} ${d.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit' })}`);
539
- }
540
- }
541
-
542
- // Usage patterns
543
- if (patterns.length > 0) {
544
- const topTopics = patterns.map(p => p.pattern).join(', ');
545
- lines.push(`• Frequent topics/tools: ${topTopics}`);
546
- }
547
-
548
- // Past session memories
549
- if (memories.length > 0) {
550
- lines.push('\nRecent session summaries:');
551
- memories.forEach((m, i) => lines.push(` ${i + 1}. ${m.summary}`));
552
- }
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
-
584
- if (lines.length === 1) return ''; // nothing to add
585
- lines.push('[END USER CONTEXT]\n');
586
- return lines.join('\n');
587
- } catch (err) {
588
- console.error('[Memory] getUserContext error:', err.message);
589
- return '';
590
- }
591
- }
592
-
593
- // ── Response Cache helpers ────────────────────────────────────────────────
594
- function getCachedResponse(query) {
595
- try {
596
- const hash = crypto.createHash('md5').update(query.trim().toLowerCase()).digest('hex');
597
- const row = getDb().prepare('SELECT response, created_at FROM response_cache WHERE query_hash = ?').get(hash);
598
- if (row) {
599
- // Optional: check TTL (e.g., 24 hours)
600
- const age = Date.now() - new Date(row.created_at).getTime();
601
- if (age < 24 * 60 * 60 * 1000) {
602
- const parsed = JSON.parse(row.response);
603
- if (parsed && typeof parsed.response === 'string') {
604
- parsed.response = stripRelevantMemoryBlock(parsed.response);
605
- }
606
- return parsed;
607
- }
608
- }
609
- } catch (_) {}
610
- return null;
611
- }
612
-
613
- function cacheResponse(query, responseObj) {
614
- try {
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;
624
- getDb().prepare(`
625
- INSERT INTO response_cache (query_hash, response, created_at)
626
- VALUES (?, ?, CURRENT_TIMESTAMP)
627
- ON CONFLICT(query_hash) DO UPDATE SET response = excluded.response, created_at = CURRENT_TIMESTAMP
628
- `).run(hash, JSON.stringify(sanitized));
629
- } catch (_) {}
630
- }
631
-
632
- function clearResponseCache() {
633
- try {
634
- getDb().prepare('DELETE FROM response_cache').run();
635
- } catch (err) {
636
- console.error('[Memory] clearResponseCache error:', err.message);
637
- }
638
- }
639
-
640
- module.exports = {
641
- recordInteraction,
642
- saveSessionSummary,
643
- getUserContext,
644
- setProfile,
645
- deleteProfile,
646
- clearConversationScopedProfile,
647
- getProfile,
648
- getAllProfile,
649
- addLearnedSkill,
650
- getLearnedSkills,
651
- deleteLearnedSkill,
652
- getTopPatterns,
653
- getRecentInteractions,
654
- searchInteractions,
655
- deleteInteractionMemory,
656
- clearInteractionMemories,
657
- exportMemorySnapshot,
658
- getRecentMemories,
659
- getCachedResponse,
660
- cacheResponse,
661
- clearResponseCache
662
- };