micro-models-agent 0.41.2 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +19 -9
  3. package/dist/cli/completer.js +37 -36
  4. package/dist/cli/index.js +2 -2
  5. package/dist/cli/main.js +23 -48
  6. package/dist/cli/repl-commands.js +12 -40
  7. package/dist/cli/repl.js +87 -217
  8. package/dist/cli/security-commands.js +7 -5
  9. package/dist/cli/setup.js +26 -8
  10. package/dist/config/config.js +5 -52
  11. package/dist/config/defaults.js +5 -29
  12. package/dist/config/experts.js +1 -1
  13. package/dist/config/index.js +3 -3
  14. package/dist/config/security.js +10 -3
  15. package/dist/core/agent-moe.js +10 -2
  16. package/dist/core/agent.js +82 -273
  17. package/dist/core/bootstrap.js +13 -80
  18. package/dist/core/index.js +2 -2
  19. package/dist/core/prompt-builder.js +2 -23
  20. package/dist/core/session-logger.js +4 -46
  21. package/dist/i18n/en.json +2 -75
  22. package/dist/i18n/ru.json +1 -74
  23. package/dist/index.js +1 -1
  24. package/dist/llm/image-utils.js +5 -4
  25. package/dist/llm/index.js +4 -4
  26. package/dist/llm/model-loader.js +6 -6
  27. package/dist/llm/openai-compat.js +34 -40
  28. package/dist/llm/orchestrator.js +29 -33
  29. package/dist/llm/response.js +9 -9
  30. package/dist/logger/app-logger.js +1 -1
  31. package/dist/logger/index.js +1 -1
  32. package/dist/main.js +648 -183
  33. package/dist/migration/backup.js +13 -13
  34. package/dist/migration/detect.js +11 -11
  35. package/dist/migration/index.js +2 -2
  36. package/dist/modules/browser/actions.js +4 -34
  37. package/dist/modules/browser/bridge-server.mjs +202 -202
  38. package/dist/modules/browser/cookie-store.js +6 -6
  39. package/dist/modules/browser/index.js +5 -7
  40. package/dist/modules/browser/module.js +7 -8
  41. package/dist/modules/browser/session.js +84 -87
  42. package/dist/modules/browser/snapshot.js +58 -92
  43. package/dist/modules/browser/types.js +1 -4
  44. package/dist/modules/certification/cli.js +4 -2
  45. package/dist/modules/certification/fact-checker.js +3 -1
  46. package/dist/modules/certification/loader.js +9 -3
  47. package/dist/modules/certification/runner.js +4 -1
  48. package/dist/modules/context/index.js +1 -1
  49. package/dist/modules/context/manager.js +86 -160
  50. package/dist/modules/execution/auditor.js +25 -177
  51. package/dist/modules/execution/module.js +544 -201
  52. package/dist/modules/execution/moe-executor.js +0 -25
  53. package/dist/modules/execution/plan-store.js +3 -1
  54. package/dist/modules/execution/plan-validator.js +10 -10
  55. package/dist/modules/execution/planner.js +1 -6
  56. package/dist/modules/execution/stuck-detector.js +10 -173
  57. package/dist/modules/execution/verifier.js +42 -86
  58. package/dist/modules/hallucination/confidence.js +1 -8
  59. package/dist/modules/hallucination/detector.js +5 -2
  60. package/dist/modules/hallucination/factual.js +64 -3
  61. package/dist/modules/hallucination/index.js +1 -1
  62. package/dist/modules/hallucination/js-identifiers.js +0 -190
  63. package/dist/modules/hallucination/llm-judge.js +3 -1
  64. package/dist/modules/indexer/cache.js +7 -9
  65. package/dist/modules/indexer/index.js +3 -3
  66. package/dist/modules/indexer/module.js +42 -95
  67. package/dist/modules/indexer/walker.js +17 -17
  68. package/dist/modules/lsp/client.js +31 -74
  69. package/dist/modules/lsp/config.js +33 -87
  70. package/dist/modules/lsp/index.js +3 -3
  71. package/dist/modules/lsp/module.js +21 -185
  72. package/dist/modules/mcp/module.js +6 -2
  73. package/dist/modules/memory/index.js +1 -1
  74. package/dist/modules/memory/module.js +23 -71
  75. package/dist/modules/memory/search.js +9 -11
  76. package/dist/modules/memory/store.js +13 -13
  77. package/dist/modules/pipelines/engine.js +10 -10
  78. package/dist/modules/pipelines/index.js +3 -3
  79. package/dist/modules/pipelines/parser.js +14 -17
  80. package/dist/modules/pipelines/template.js +1 -1
  81. package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
  82. package/dist/modules/plugins/builtin/notify.js +2 -3
  83. package/dist/modules/plugins/index.js +1 -1
  84. package/dist/modules/plugins/loader.js +17 -59
  85. package/dist/modules/plugins/manager.js +17 -73
  86. package/dist/modules/processes/index.js +1 -1
  87. package/dist/modules/processes/registry.js +46 -135
  88. package/dist/modules/registry.js +2 -4
  89. package/dist/modules/security/audit-notifier.js +39 -39
  90. package/dist/modules/security/command-validator.js +8 -2
  91. package/dist/modules/security/data-sanitizer.js +9 -1
  92. package/dist/modules/security/encryption.js +56 -58
  93. package/dist/modules/security/network-validator.js +9 -1
  94. package/dist/modules/security/path-validator.js +3 -1
  95. package/dist/modules/security/security-policies.js +19 -3
  96. package/dist/modules/security/session-encryption.js +1 -1
  97. package/dist/modules/security/session-isolation.js +8 -8
  98. package/dist/modules/session/index.js +3 -3
  99. package/dist/modules/session/module.js +5 -5
  100. package/dist/modules/session/store.js +9 -3
  101. package/dist/modules/skills/module.js +2 -1
  102. package/dist/modules/updater/checker.js +6 -70
  103. package/dist/modules/updater/index.js +1 -2
  104. package/dist/modules/user-profile/compressor.js +2 -2
  105. package/dist/modules/user-profile/index.js +1 -1
  106. package/dist/modules/user-profile/profile.js +9 -9
  107. package/dist/tools/attach-image.js +1 -1
  108. package/dist/tools/bash.js +19 -178
  109. package/dist/tools/browser.js +29 -46
  110. package/dist/tools/executor.js +5 -4
  111. package/dist/tools/file-info.js +12 -13
  112. package/dist/tools/filter-tools.js +2 -9
  113. package/dist/tools/glob-tool.js +11 -11
  114. package/dist/tools/grep-tool.js +3 -1
  115. package/dist/tools/index.js +2 -13
  116. package/dist/tools/list-dir.js +17 -18
  117. package/dist/tools/load-skill.js +3 -1
  118. package/dist/tools/path-utils.js +4 -4
  119. package/dist/tools/pipeline-run.js +25 -25
  120. package/dist/tools/process-kill.js +11 -11
  121. package/dist/tools/process-list.js +22 -20
  122. package/dist/tools/process-log.js +18 -22
  123. package/dist/tools/question.js +3 -1
  124. package/dist/tools/read-file.js +2 -10
  125. package/dist/tools/recall.js +37 -44
  126. package/dist/tools/registry.js +4 -15
  127. package/dist/tools/remember.js +29 -29
  128. package/dist/tools/scope-check.js +9 -9
  129. package/dist/tools/subagent.js +9 -54
  130. package/dist/tools/user-input.js +1 -1
  131. package/dist/tools/web-browse.js +3 -3
  132. package/dist/tools/web-fetch.js +3 -3
  133. package/dist/tools/web-search.js +3 -3
  134. package/dist/tools/write-file.js +3 -1
  135. package/dist/ui/box.js +5 -1
  136. package/dist/ui/index.js +6 -6
  137. package/dist/ui/md-formatter.js +33 -33
  138. package/dist/ui/output.js +5 -5
  139. package/dist/ui/renderer.js +10 -15
  140. package/dist/ui/table.js +1 -1
  141. package/package.json +48 -48
  142. package/dist/cli/plugin-commands.js +0 -36
  143. package/dist/cli/run-result.js +0 -22
  144. package/dist/core/version.js +0 -24
  145. package/dist/modules/artifacts/store.js +0 -61
  146. package/dist/modules/browser/bridge-client.js +0 -199
  147. package/dist/modules/browser/bridge-path.js +0 -10
  148. package/dist/modules/browser/driver.js +0 -136
  149. package/dist/modules/context/chunk-query.js +0 -100
  150. package/dist/modules/context/fact-extractor.js +0 -162
  151. package/dist/modules/context/history.js +0 -15
  152. package/dist/modules/execution/audit-runners.js +0 -152
  153. package/dist/modules/execution/execution-plugin.js +0 -272
  154. package/dist/modules/execution/plan-tool.js +0 -508
  155. package/dist/modules/execution/windows-commands.js +0 -41
  156. package/dist/modules/indexer/project-profile.js +0 -183
  157. package/dist/modules/lsp/check-tool.js +0 -58
  158. package/dist/modules/lsp/command.js +0 -60
  159. package/dist/modules/lsp/probe.js +0 -76
  160. package/dist/modules/lsp/project-root.js +0 -32
  161. package/dist/modules/lsp/startup-check.js +0 -141
  162. package/dist/modules/processes/detect.js +0 -34
  163. package/dist/modules/skills/matcher.js +0 -27
  164. package/dist/modules/updater/module.js +0 -116
  165. package/dist/tools/chunk-query.js +0 -99
  166. package/dist/tools/download-file.js +0 -116
  167. package/dist/tools/enable-tools.js +0 -58
  168. package/dist/tools/hidden-tools-block.js +0 -37
  169. package/dist/ui/line-editor.js +0 -703
  170. package/dist/ui/line-math.js +0 -69
  171. package/dist/ui/plan-view.js +0 -103
@@ -1,10 +1,10 @@
1
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- import { readFileSync, writeFileSync, existsSync, copyFileSync } from "fs";
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { readFileSync, writeFileSync, existsSync, copyFileSync } from 'fs';
5
5
  const DEFAULT_CONFIG = {
6
- keyPath: join(homedir(), ".mma", ".encryption-key"),
7
- algorithm: "aes-256-gcm",
6
+ keyPath: join(homedir(), '.mma', '.encryption-key'),
7
+ algorithm: 'aes-256-gcm',
8
8
  };
9
9
  const IV_LENGTH = 16;
10
10
  const SALT_LENGTH = 16;
@@ -22,7 +22,7 @@ export function generateEncryptionKey() {
22
22
  export function getOrCreateEncryptionKey(config) {
23
23
  const { keyPath } = { ...DEFAULT_CONFIG, ...config };
24
24
  // Ensure directory exists
25
- const dir = join(keyPath, "..");
25
+ const dir = join(keyPath, '..');
26
26
  if (!existsSync(dir)) {
27
27
  // Note: We can't use mkdirSync here because we're in a pure function
28
28
  // This will be handled by the caller
@@ -30,7 +30,7 @@ export function getOrCreateEncryptionKey(config) {
30
30
  if (!existsSync(keyPath)) {
31
31
  const key = generateEncryptionKey();
32
32
  try {
33
- writeFileSync(keyPath, key.toString("base64"), { mode: 0o600 });
33
+ writeFileSync(keyPath, key.toString('base64'), { mode: 0o600 });
34
34
  return key;
35
35
  }
36
36
  catch {
@@ -40,8 +40,8 @@ export function getOrCreateEncryptionKey(config) {
40
40
  }
41
41
  }
42
42
  try {
43
- const keyBase64 = readFileSync(keyPath, "utf8");
44
- return Buffer.from(keyBase64, "base64");
43
+ const keyBase64 = readFileSync(keyPath, 'utf8');
44
+ return Buffer.from(keyBase64, 'base64');
45
45
  }
46
46
  catch {
47
47
  // Backup the corrupted key file before throwing
@@ -49,9 +49,7 @@ export function getOrCreateEncryptionKey(config) {
49
49
  const backupPath = `${keyPath}.corrupted.${Date.now()}`;
50
50
  copyFileSync(keyPath, backupPath);
51
51
  }
52
- catch {
53
- /* backup failed, continue */
54
- }
52
+ catch { /* backup failed, continue */ }
55
53
  throw new Error(`Encryption key file is corrupted or unreadable: ${keyPath}. ` +
56
54
  `All previously encrypted data will be unrecoverable. ` +
57
55
  `Delete the key file to generate a new one.`);
@@ -70,63 +68,63 @@ export function encryptString(plaintext, key) {
70
68
  const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
71
69
  const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
72
70
  // Encrypt with AES-256-CBC
73
- const cipher = createCipheriv("aes-256-cbc", encKey, iv);
74
- let encrypted = cipher.update(plaintext, "utf8", "base64");
75
- encrypted += cipher.final("base64");
71
+ const cipher = createCipheriv('aes-256-cbc', encKey, iv);
72
+ let encrypted = cipher.update(plaintext, 'utf8', 'base64');
73
+ encrypted += cipher.final('base64');
76
74
  // Create HMAC for authentication
77
- const hmac = createHmac("sha256", hmacKey);
75
+ const hmac = createHmac('sha256', hmacKey);
78
76
  hmac.update(iv);
79
- hmac.update(Buffer.from(encrypted, "base64"));
77
+ hmac.update(Buffer.from(encrypted, 'base64'));
80
78
  const authTag = hmac.digest();
81
79
  // Format: salt:iv:authTag:encrypted
82
- return `${salt.toString("base64")}:${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted}`;
80
+ return `${salt.toString('base64')}:${iv.toString('base64')}:${authTag.toString('base64')}:${encrypted}`;
83
81
  }
84
82
  /**
85
83
  * Decrypt a string using AES-256-CBC + HMAC
86
84
  */
87
85
  export function decryptString(encryptedText, key) {
88
- const parts = encryptedText.split(":");
86
+ const parts = encryptedText.split(':');
89
87
  if (parts.length !== 4) {
90
- throw new Error("Invalid encrypted format");
88
+ throw new Error('Invalid encrypted format');
91
89
  }
92
- const salt = Buffer.from(parts[0], "base64");
93
- const iv = Buffer.from(parts[1], "base64");
94
- const authTag = Buffer.from(parts[2], "base64");
90
+ const salt = Buffer.from(parts[0], 'base64');
91
+ const iv = Buffer.from(parts[1], 'base64');
92
+ const authTag = Buffer.from(parts[2], 'base64');
95
93
  const encrypted = parts[3];
96
94
  // Derive encryption key and HMAC key from the main key
97
95
  const encKey = scryptSync(key, salt, KEY_LENGTH);
98
96
  const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
99
97
  const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
100
98
  // Verify HMAC
101
- const hmac = createHmac("sha256", hmacKey);
99
+ const hmac = createHmac('sha256', hmacKey);
102
100
  hmac.update(iv);
103
- hmac.update(Buffer.from(encrypted, "base64"));
101
+ hmac.update(Buffer.from(encrypted, 'base64'));
104
102
  const computedTag = hmac.digest();
105
103
  if (!computedTag.equals(authTag)) {
106
- throw new Error("Authentication failed: invalid tag");
104
+ throw new Error('Authentication failed: invalid tag');
107
105
  }
108
106
  // Decrypt with AES-256-CBC
109
- const decipher = createDecipheriv("aes-256-cbc", encKey, iv);
110
- let decrypted = decipher.update(encrypted, "base64", "utf8");
111
- decrypted += decipher.final("utf8");
107
+ const decipher = createDecipheriv('aes-256-cbc', encKey, iv);
108
+ let decrypted = decipher.update(encrypted, 'base64', 'utf8');
109
+ decrypted += decipher.final('utf8');
112
110
  return decrypted;
113
111
  }
114
- import { createHmac } from "node:crypto";
112
+ import { createHmac } from 'node:crypto';
115
113
  /**
116
114
  * Check if a string looks like it's encrypted
117
115
  */
118
116
  export function isEncryptedString(value) {
119
117
  // Encrypted strings have the format: salt:iv:authTag:encrypted
120
118
  // Each part is base64 encoded and separated by colons
121
- const parts = value.split(":");
119
+ const parts = value.split(':');
122
120
  if (parts.length !== 4)
123
121
  return false;
124
122
  // Check if all parts are valid base64
125
123
  try {
126
- Buffer.from(parts[0], "base64");
127
- Buffer.from(parts[1], "base64");
128
- Buffer.from(parts[2], "base64");
129
- Buffer.from(parts[3], "base64");
124
+ Buffer.from(parts[0], 'base64');
125
+ Buffer.from(parts[1], 'base64');
126
+ Buffer.from(parts[2], 'base64');
127
+ Buffer.from(parts[3], 'base64');
130
128
  return true;
131
129
  }
132
130
  catch {
@@ -137,28 +135,28 @@ export function isEncryptedString(value) {
137
135
  * Sensitive field names that should be encrypted
138
136
  */
139
137
  export const SENSITIVE_FIELD_NAMES = [
140
- "api_key",
141
- "apikey",
142
- "apiKey",
143
- "token",
144
- "access_token",
145
- "accessToken",
146
- "password",
147
- "passwd",
148
- "pwd",
149
- "secret",
150
- "private_key",
151
- "privateKey",
152
- "auth",
153
- "authorization",
154
- "key",
138
+ 'api_key',
139
+ 'apikey',
140
+ 'apiKey',
141
+ 'token',
142
+ 'access_token',
143
+ 'accessToken',
144
+ 'password',
145
+ 'passwd',
146
+ 'pwd',
147
+ 'secret',
148
+ 'private_key',
149
+ 'privateKey',
150
+ 'auth',
151
+ 'authorization',
152
+ 'key',
155
153
  ];
156
154
  /**
157
155
  * Check if a field name indicates sensitive data
158
156
  */
159
157
  export function isSensitiveFieldName(fieldName) {
160
158
  const lowerFieldName = fieldName.toLowerCase();
161
- return SENSITIVE_FIELD_NAMES.some((name) => lowerFieldName.includes(name));
159
+ return SENSITIVE_FIELD_NAMES.some(name => lowerFieldName.includes(name));
162
160
  }
163
161
  /**
164
162
  * Encrypt sensitive fields in an object
@@ -166,12 +164,12 @@ export function isSensitiveFieldName(fieldName) {
166
164
  export function encryptSensitiveFields(obj, key) {
167
165
  const result = {};
168
166
  for (const [fieldName, value] of Object.entries(obj)) {
169
- if (isSensitiveFieldName(fieldName) && typeof value === "string" && !isEncryptedString(value)) {
167
+ if (isSensitiveFieldName(fieldName) && typeof value === 'string' && !isEncryptedString(value)) {
170
168
  result[fieldName] = encryptString(value, key);
171
169
  }
172
- else if (typeof value === "object" && value !== null) {
170
+ else if (typeof value === 'object' && value !== null) {
173
171
  if (Array.isArray(value)) {
174
- result[fieldName] = value.map((item) => typeof item === "object" && item !== null && !(item instanceof RegExp)
172
+ result[fieldName] = value.map(item => typeof item === 'object' && item !== null && !(item instanceof RegExp)
175
173
  ? encryptSensitiveFields(item, key)
176
174
  : item);
177
175
  }
@@ -194,7 +192,7 @@ export function encryptSensitiveFields(obj, key) {
194
192
  export function decryptSensitiveFields(obj, key) {
195
193
  const result = {};
196
194
  for (const [fieldName, value] of Object.entries(obj)) {
197
- if (isSensitiveFieldName(fieldName) && typeof value === "string" && isEncryptedString(value)) {
195
+ if (isSensitiveFieldName(fieldName) && typeof value === 'string' && isEncryptedString(value)) {
198
196
  try {
199
197
  result[fieldName] = decryptString(value, key);
200
198
  }
@@ -203,9 +201,9 @@ export function decryptSensitiveFields(obj, key) {
203
201
  result[fieldName] = value;
204
202
  }
205
203
  }
206
- else if (typeof value === "object" && value !== null) {
204
+ else if (typeof value === 'object' && value !== null) {
207
205
  if (Array.isArray(value)) {
208
- result[fieldName] = value.map((item) => typeof item === "object" && item !== null && !(item instanceof RegExp)
206
+ result[fieldName] = value.map(item => typeof item === 'object' && item !== null && !(item instanceof RegExp)
209
207
  ? decryptSensitiveFields(item, key)
210
208
  : item);
211
209
  }
@@ -48,7 +48,15 @@ export function isUrlAllowed(url, securityConfig) {
48
48
  export function sanitizeUrl(url) {
49
49
  try {
50
50
  const urlObj = new URL(url);
51
- const sensitiveKeys = ["api_key", "token", "password", "secret", "access_token", "auth", "key"];
51
+ const sensitiveKeys = [
52
+ "api_key",
53
+ "token",
54
+ "password",
55
+ "secret",
56
+ "access_token",
57
+ "auth",
58
+ "key",
59
+ ];
52
60
  const sanitizedParams = new URLSearchParams(urlObj.search);
53
61
  for (const key of sensitiveKeys) {
54
62
  if (sanitizedParams.has(key)) {
@@ -199,7 +199,9 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
199
199
  .replace(/\?/g, ".")
200
200
  .replace(/\./g, "\\.");
201
201
  const regex = new RegExp(`^${regexPattern}$`);
202
- const relativePath = resolvedTargetPath.slice(resolvedBaseDir.length + 1).replace(/\\/g, "/");
202
+ const relativePath = resolvedTargetPath
203
+ .slice(resolvedBaseDir.length + 1)
204
+ .replace(/\\/g, "/");
203
205
  return regex.test(relativePath);
204
206
  }
205
207
  // Exact match
@@ -1,4 +1,4 @@
1
- import { mergeSecurityConfig } from "../../config/security";
1
+ import { mergeSecurityConfig, } from "../../config/security";
2
2
  /**
3
3
  * Strict security policy - maximum security, minimum flexibility
4
4
  * Recommended for: production environments, sensitive data, multi-user systems
@@ -70,7 +70,18 @@ export const STRICT_POLICY = {
70
70
  "-R",
71
71
  "--no-clobber",
72
72
  ],
73
- dangerousOperators: [">", ">>", "2>", "2>>", "|", "&&", "||", ";", "&", "`"],
73
+ dangerousOperators: [
74
+ ">",
75
+ ">>",
76
+ "2>",
77
+ "2>>",
78
+ "|",
79
+ "&&",
80
+ "||",
81
+ ";",
82
+ "&",
83
+ "`",
84
+ ],
74
85
  logCommands: true,
75
86
  },
76
87
  paths: {
@@ -288,7 +299,12 @@ export const BALANCED_POLICY = {
288
299
  auditNotifier: {
289
300
  enabled: false,
290
301
  minSeverity: "medium",
291
- eventTypes: ["security_block", "bash_command", "file_operation", "network_request"],
302
+ eventTypes: [
303
+ "security_block",
304
+ "bash_command",
305
+ "file_operation",
306
+ "network_request",
307
+ ],
292
308
  maxRetries: 3,
293
309
  },
294
310
  },
@@ -1,4 +1,4 @@
1
- import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync } from "fs";
1
+ import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync, } from "fs";
2
2
  import { join } from "path";
3
3
  import { homedir } from "os";
4
4
  import { encryptString, decryptString, isEncryptedString, ConfigEncryptor, } from "./encryption";
@@ -1,7 +1,7 @@
1
- import { join, resolve } from "path";
2
- import { homedir } from "os";
3
- import { mkdirSync, existsSync } from "fs";
4
- import { DEFAULT_SECURITY_CONFIG } from "../../config/security";
1
+ import { join, resolve } from 'path';
2
+ import { homedir } from 'os';
3
+ import { mkdirSync, existsSync } from 'fs';
4
+ import { DEFAULT_SECURITY_CONFIG } from '../../config/security';
5
5
  /**
6
6
  * Default session isolation configuration
7
7
  */
@@ -16,8 +16,8 @@ export const DEFAULT_SESSION_ISOLATION = {
16
16
  export function createSessionContext(sessionId, projectDir, isolationConfig, securityOverrides) {
17
17
  const config = { ...DEFAULT_SESSION_ISOLATION, ...isolationConfig };
18
18
  // Create session-specific temporary directory
19
- const baseDir = config.baseDir || join(homedir(), ".mma", "sessions", sessionId);
20
- const tempDir = join(baseDir, "temp");
19
+ const baseDir = config.baseDir || join(homedir(), '.mma', 'sessions', sessionId);
20
+ const tempDir = join(baseDir, 'temp');
21
21
  if (config.isolateTempFiles && !existsSync(tempDir)) {
22
22
  try {
23
23
  mkdirSync(tempDir, { recursive: true, mode: 0o700 });
@@ -84,11 +84,11 @@ export function isPathInSessionScope(sessionContext, path) {
84
84
  const workingDir = resolve(sessionContext.workingDir);
85
85
  const tempDir = resolve(sessionContext.tempDir);
86
86
  // Check if path is within working directory
87
- if (resolvedPath.startsWith(workingDir + "/") || resolvedPath.startsWith(workingDir + "\\")) {
87
+ if (resolvedPath.startsWith(workingDir + '/') || resolvedPath.startsWith(workingDir + '\\')) {
88
88
  return true;
89
89
  }
90
90
  // Check if path is within temp directory
91
- if (resolvedPath.startsWith(tempDir + "/") || resolvedPath.startsWith(tempDir + "\\")) {
91
+ if (resolvedPath.startsWith(tempDir + '/') || resolvedPath.startsWith(tempDir + '\\')) {
92
92
  return true;
93
93
  }
94
94
  return false;
@@ -1,3 +1,3 @@
1
- export { SessionStore } from "./store";
2
- export { SessionManager } from "./manager";
3
- export { SessionModule } from "./module";
1
+ export { SessionStore } from './store';
2
+ export { SessionManager } from './manager';
3
+ export { SessionModule } from './module';
@@ -1,6 +1,6 @@
1
- import { t } from "../../i18n/index";
1
+ import { t } from '../../i18n/index';
2
2
  export class SessionModule {
3
- name = "session";
3
+ name = 'session';
4
4
  manager;
5
5
  constructor(manager) {
6
6
  this.manager = manager;
@@ -10,15 +10,15 @@ export class SessionModule {
10
10
  if (!meta || meta.messageCount === 0)
11
11
  return null;
12
12
  return {
13
- content: t("session.info", { name: meta.name, id: meta.id, count: meta.messageCount }),
14
- priority: "low",
13
+ content: t('session.info', { name: meta.name, id: meta.id, count: meta.messageCount }),
14
+ priority: 'low',
15
15
  essential: false,
16
16
  estimatedTokens: 20,
17
17
  };
18
18
  }
19
19
  getPlugin() {
20
20
  return {
21
- name: "session",
21
+ name: 'session',
22
22
  };
23
23
  }
24
24
  }
@@ -72,7 +72,9 @@ export class SessionStore {
72
72
  return null;
73
73
  try {
74
74
  const raw = readFileSync(path, "utf-8");
75
- const content = this.encryptor ? this.encryptor.decryptFileContent(raw) : raw;
75
+ const content = this.encryptor
76
+ ? this.encryptor.decryptFileContent(raw)
77
+ : raw;
76
78
  const meta = JSON.parse(content);
77
79
  this._metaCache.set(id, meta);
78
80
  return meta;
@@ -127,7 +129,9 @@ export class SessionStore {
127
129
  .map(parseLine)
128
130
  .filter((m) => m !== null);
129
131
  }
130
- return lines.map(parseLine).filter((m) => m !== null);
132
+ return lines
133
+ .map(parseLine)
134
+ .filter((m) => m !== null);
131
135
  }
132
136
  catch {
133
137
  return [];
@@ -173,7 +177,9 @@ export class SessionStore {
173
177
  .map(parseLine)
174
178
  .filter((e) => e !== null);
175
179
  }
176
- return lines.map(parseLine).filter((e) => e !== null);
180
+ return lines
181
+ .map(parseLine)
182
+ .filter((e) => e !== null);
177
183
  }
178
184
  catch {
179
185
  return [];
@@ -38,7 +38,8 @@ export class SkillsModule {
38
38
  }
39
39
  search(query) {
40
40
  const q = query.toLowerCase();
41
- return this.availableSkills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q));
41
+ return this.availableSkills.filter((s) => s.name.toLowerCase().includes(q) ||
42
+ s.description.toLowerCase().includes(q));
42
43
  }
43
44
  getBudget() {
44
45
  return {
@@ -1,96 +1,32 @@
1
- import { platform } from "os";
2
- import { resolveSpawnCommand } from "../lsp/command";
3
- /** Numeric semver comparison (x.y.z, prerelease ignored). */
4
- export function semverGt(a, b) {
5
- const pa = a
6
- .replace(/^v/, "")
7
- .split(/[.-]/)
8
- .map((p) => Number.parseInt(p, 10) || 0);
9
- const pb = b
10
- .replace(/^v/, "")
11
- .split(/[.-]/)
12
- .map((p) => Number.parseInt(p, 10) || 0);
13
- for (let i = 0; i < 3; i++) {
14
- const na = pa[i] ?? 0;
15
- const nb = pb[i] ?? 0;
16
- if (na !== nb)
17
- return na > nb;
18
- }
19
- return false;
20
- }
21
- const defaultRunner = async (command, args, options) => {
22
- const { execFile } = await import("child_process");
23
- return new Promise((resolve) => {
24
- execFile(command, args, options, (err) => resolve({ error: err?.message }));
25
- });
26
- };
27
1
  export class Updater {
28
2
  currentVersion;
29
3
  packageName;
30
- runner;
31
- constructor(currentVersion, packageName, runner) {
4
+ constructor(currentVersion, packageName) {
32
5
  this.currentVersion = currentVersion;
33
6
  this.packageName = packageName;
34
- this.runner = runner ?? defaultRunner;
35
7
  }
36
8
  getCurrentVersion() {
37
9
  return this.currentVersion;
38
10
  }
39
- getPackageName() {
40
- return this.packageName;
41
- }
42
11
  async check() {
43
12
  try {
44
13
  const response = await fetch(`https://registry.npmjs.org/${this.packageName}`, {
45
- headers: { Accept: "application/vnd.npm.install-v1+json" },
14
+ headers: { Accept: 'application/vnd.npm.install-v1+json' },
46
15
  signal: AbortSignal.timeout(5000),
47
16
  });
48
17
  if (!response.ok) {
49
- return {
50
- updateAvailable: false,
51
- current: this.currentVersion,
52
- error: `HTTP ${response.status}`,
53
- };
18
+ return { updateAvailable: false, current: this.currentVersion, error: `HTTP ${response.status}` };
54
19
  }
55
20
  const data = await response.json();
56
- const latest = data["dist-tags"]?.latest;
21
+ const latest = data['dist-tags']?.latest;
57
22
  if (!latest) {
58
- return { updateAvailable: false, current: this.currentVersion, error: "No latest tag" };
23
+ return { updateAvailable: false, current: this.currentVersion, error: 'No latest tag' };
59
24
  }
60
- const updateAvailable = semverGt(latest, this.currentVersion);
25
+ const updateAvailable = latest !== this.currentVersion;
61
26
  return { updateAvailable, current: this.currentVersion, latest };
62
27
  }
63
28
  catch (e) {
64
29
  return { updateAvailable: false, current: this.currentVersion, error: e.message };
65
30
  }
66
31
  }
67
- /**
68
- * Install the latest version globally via `npm install -g`.
69
- * Returns an InstallResult (never throws).
70
- */
71
- async install(latest) {
72
- try {
73
- const { command, args } = this.buildInstallCommand(latest);
74
- const res = await this.runner(command, args, {
75
- timeout: 120000,
76
- windowsHide: true,
77
- });
78
- if (res.error)
79
- return { success: false, error: res.error };
80
- return { success: true, installed: latest };
81
- }
82
- catch (e) {
83
- const msg = e?.message ?? String(e);
84
- return { success: false, error: msg.split("\n")[0] };
85
- }
86
- }
87
- buildInstallCommand(latest) {
88
- const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
89
- const command = resolveSpawnCommand("npm");
90
- if (platform() === "win32" && /\.(cmd|bat)$/i.test(command)) {
91
- // Node cannot spawn .cmd/.bat shims directly (spawn EINVAL) — route via cmd.exe.
92
- return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
93
- }
94
- return { command, args: npmArgs };
95
- }
96
32
  }
@@ -1,2 +1 @@
1
- export { Updater } from "./checker";
2
- export { UpdaterModule } from "./module";
1
+ export { Updater } from './checker';
@@ -8,9 +8,9 @@ export class ProfileCompressor {
8
8
  if (Object.keys(info.preferences).length > 0) {
9
9
  const prefs = Object.entries(info.preferences)
10
10
  .map(([k, v]) => `${k}=${v}`)
11
- .join(",");
11
+ .join(',');
12
12
  parts.push(`Prefs: ${prefs}`);
13
13
  }
14
- return parts.join(", ");
14
+ return parts.join(', ');
15
15
  }
16
16
  }
@@ -1 +1 @@
1
- export { UserProfile } from "./profile";
1
+ export { UserProfile } from './profile';
@@ -1,8 +1,8 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
- import { join } from "path";
3
- import { homedir, hostname, platform, type } from "os";
4
- import { env } from "process";
5
- import { ProfileCompressor } from "./compressor";
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir, hostname, platform, type } from 'os';
4
+ import { env } from 'process';
5
+ import { ProfileCompressor } from './compressor';
6
6
  export class UserProfile {
7
7
  profileDir;
8
8
  info = null;
@@ -15,7 +15,7 @@ export class UserProfile {
15
15
  platform: platform(),
16
16
  os: `${type()} ${hostname()}`,
17
17
  hostname: hostname(),
18
- shell: env.SHELL || env.ComSpec || "unknown",
18
+ shell: env.SHELL || env.ComSpec || 'unknown',
19
19
  home: homedir(),
20
20
  nodeVersion: process.version,
21
21
  preferences: { ...this.preferences },
@@ -26,14 +26,14 @@ export class UserProfile {
26
26
  if (!existsSync(this.profileDir)) {
27
27
  mkdirSync(this.profileDir, { recursive: true });
28
28
  }
29
- writeFileSync(join(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
29
+ writeFileSync(join(this.profileDir, 'profile.json'), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), 'utf-8');
30
30
  }
31
31
  load() {
32
- const path = join(this.profileDir, "profile.json");
32
+ const path = join(this.profileDir, 'profile.json');
33
33
  if (!existsSync(path))
34
34
  return null;
35
35
  try {
36
- const data = JSON.parse(readFileSync(path, "utf-8"));
36
+ const data = JSON.parse(readFileSync(path, 'utf-8'));
37
37
  this.info = {
38
38
  platform: data.platform,
39
39
  os: data.os,
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "fs";
2
2
  import { resolve } from "path";
3
3
  import { t } from "../i18n/index";
4
- import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl, } from "../llm/image-utils";
4
+ import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl } from "../llm/image-utils";
5
5
  import { logSecurityBlock } from "../modules/security/audit-log";
6
6
  export const attachImageTool = {
7
7
  name: "attach_image",