micro-models-agent 0.39.0 → 0.40.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 (96) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +116 -3
  3. package/dist/cli/main.js +35 -8
  4. package/dist/cli/repl-commands.js +633 -0
  5. package/dist/cli/repl.js +110 -611
  6. package/dist/cli/setup.js +32 -12
  7. package/dist/config/config.js +46 -30
  8. package/dist/config/defaults.js +10 -1
  9. package/dist/config/security.js +15 -8
  10. package/dist/core/agent-moe.js +24 -12
  11. package/dist/core/agent.js +281 -47
  12. package/dist/core/bootstrap.js +52 -36
  13. package/dist/core/session-logger.js +35 -2
  14. package/dist/core/workspace.js +76 -0
  15. package/dist/i18n/en.json +79 -15
  16. package/dist/i18n/index.js +12 -9
  17. package/dist/i18n/ru.json +79 -15
  18. package/dist/index.js +13 -13
  19. package/dist/llm/openai-compat.js +39 -10
  20. package/dist/logger/app-logger.js +83 -16
  21. package/dist/logger/file-log.js +151 -0
  22. package/dist/main.js +537 -284
  23. package/dist/modules/browser/bridge-server.mjs +113 -105
  24. package/dist/modules/browser/session.js +108 -60
  25. package/dist/modules/certification/cli.js +176 -0
  26. package/dist/modules/certification/fact-checker.js +84 -0
  27. package/dist/modules/certification/loader.js +111 -0
  28. package/dist/modules/certification/manifest.js +50 -0
  29. package/dist/modules/certification/runner.js +162 -0
  30. package/dist/modules/certification/scenarios.js +124 -0
  31. package/dist/modules/certification/types.js +1 -0
  32. package/dist/modules/context/manager.js +119 -10
  33. package/dist/modules/execution/auditor.js +33 -39
  34. package/dist/modules/execution/index.js +8 -6
  35. package/dist/modules/execution/module.js +474 -32
  36. package/dist/modules/execution/moe-executor.js +97 -40
  37. package/dist/modules/execution/plan-coverage.js +68 -0
  38. package/dist/modules/execution/plan-persister.js +46 -0
  39. package/dist/modules/execution/plan-store.js +159 -0
  40. package/dist/modules/execution/planner.js +63 -13
  41. package/dist/modules/execution/stuck-detector.js +252 -39
  42. package/dist/modules/execution/tracker.js +21 -7
  43. package/dist/modules/execution/verifier.js +46 -17
  44. package/dist/modules/hallucination/confidence.js +7 -2
  45. package/dist/modules/hallucination/consistency.js +8 -42
  46. package/dist/modules/hallucination/detector.js +26 -21
  47. package/dist/modules/hallucination/factual.js +170 -150
  48. package/dist/modules/hallucination/index.js +5 -4
  49. package/dist/modules/hallucination/js-identifiers.js +72 -0
  50. package/dist/modules/hallucination/llm-judge.js +103 -0
  51. package/dist/modules/index.js +5 -5
  52. package/dist/modules/lsp/client.js +235 -0
  53. package/dist/modules/lsp/config.js +81 -0
  54. package/dist/modules/lsp/index.js +3 -0
  55. package/dist/modules/lsp/module.js +68 -0
  56. package/dist/modules/lsp/types.js +1 -0
  57. package/dist/modules/mcp/client.js +8 -2
  58. package/dist/modules/memory/store.js +4 -0
  59. package/dist/modules/plugins/builtin/lint-on-write.js +143 -38
  60. package/dist/modules/processes/index.js +1 -2
  61. package/dist/modules/processes/registry.js +125 -35
  62. package/dist/modules/processes/runner.js +9 -110
  63. package/dist/modules/security/audit-log.js +30 -10
  64. package/dist/modules/security/command-validator.js +42 -16
  65. package/dist/modules/security/content-scanner.js +9 -8
  66. package/dist/modules/security/network-validator.js +2 -2
  67. package/dist/modules/security/path-validator.js +64 -10
  68. package/dist/modules/security/security-policies.js +221 -67
  69. package/dist/modules/security/session-encryption.js +42 -25
  70. package/dist/modules/session/manager.js +15 -10
  71. package/dist/modules/session/store.js +62 -8
  72. package/dist/modules/skills/index.js +2 -3
  73. package/dist/modules/skills/module.js +10 -23
  74. package/dist/tools/bash.js +287 -90
  75. package/dist/tools/create-dir.js +0 -1
  76. package/dist/tools/delete-file.js +0 -1
  77. package/dist/tools/edit-file.js +10 -8
  78. package/dist/tools/executor.js +57 -7
  79. package/dist/tools/grep-tool.js +51 -29
  80. package/dist/tools/index.js +55 -40
  81. package/dist/tools/load-skill.js +14 -18
  82. package/dist/tools/move-file.js +3 -2
  83. package/dist/tools/pipeline-run.js +1 -1
  84. package/dist/tools/read-file.js +15 -5
  85. package/dist/tools/search-history.js +42 -22
  86. package/dist/tools/subagent.js +21 -12
  87. package/dist/tools/web-browse.js +54 -25
  88. package/dist/tools/web-fetch.js +60 -34
  89. package/dist/tools/web-search.js +39 -20
  90. package/dist/tools/write-file.js +13 -10
  91. package/dist/ui/diff.js +9 -16
  92. package/dist/ui/renderer.js +69 -6
  93. package/package.json +48 -45
  94. package/dist/modules/context/history.js +0 -15
  95. package/dist/modules/processes/detect.js +0 -34
  96. package/dist/modules/skills/matcher.js +0 -27
@@ -0,0 +1,235 @@
1
+ import { spawn, execSync } from "child_process";
2
+ import { resolve } from "path";
3
+ const DEFAULT_TIMEOUT = 15000;
4
+ export class LspClient {
5
+ process = null;
6
+ requestId = 0;
7
+ pending = new Map();
8
+ buffer = "";
9
+ contentLength = -1;
10
+ diagnostics = [];
11
+ diagnosticsResolve = null;
12
+ diagnosticsTimer = null;
13
+ initialized = false;
14
+ async checkFile(filePath, baseDir, config) {
15
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT;
16
+ try {
17
+ await this.startServer(config, baseDir);
18
+ const rootUri = this.pathToUri(resolve(baseDir));
19
+ const initResult = await this.sendRequest("initialize", {
20
+ processId: process.pid,
21
+ rootUri,
22
+ workspaceFolders: [{ uri: rootUri, name: "workspace" }],
23
+ capabilities: { textDocument: { publishDiagnostics: {} } },
24
+ }, timeout);
25
+ this.initialized = true;
26
+ this.sendNotification("initialized", {});
27
+ const uri = this.pathToUri(resolve(filePath));
28
+ const fs = await import("fs");
29
+ const content = fs.readFileSync(filePath, "utf-8");
30
+ const diagPromise = new Promise((resolve) => {
31
+ this.diagnosticsResolve = resolve;
32
+ this.diagnostics = [];
33
+ this.diagnosticsTimer = setTimeout(() => {
34
+ if (this.diagnosticsResolve) {
35
+ this.diagnosticsResolve([]);
36
+ this.diagnosticsResolve = null;
37
+ }
38
+ }, timeout);
39
+ });
40
+ this.sendNotification("textDocument/didOpen", {
41
+ textDocument: {
42
+ uri,
43
+ languageId: this.languageFromPath(filePath),
44
+ version: 1,
45
+ text: content,
46
+ },
47
+ });
48
+ return await diagPromise;
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ finally {
54
+ await this.shutdown();
55
+ }
56
+ }
57
+ async startServer(config, baseDir) {
58
+ if (config.autoInstall === false) {
59
+ try {
60
+ execSync(`where ${config.command}`, { stdio: "pipe", timeout: 3000 });
61
+ }
62
+ catch {
63
+ throw new Error(`${config.command} not found in PATH`);
64
+ }
65
+ }
66
+ return new Promise((resolve, reject) => {
67
+ const args = config.args ?? [];
68
+ const proc = spawn(config.command, args, {
69
+ stdio: ["pipe", "pipe", "pipe"],
70
+ env: { ...process.env, ...config.env },
71
+ cwd: baseDir,
72
+ });
73
+ proc.on("error", reject);
74
+ proc.stdout.on("data", (chunk) => {
75
+ this.handleData(chunk);
76
+ });
77
+ proc.stderr.on("data", () => { });
78
+ proc.once("spawn", () => {
79
+ resolve();
80
+ });
81
+ this.process = proc;
82
+ setTimeout(() => {
83
+ if (!this.initialized && this.process) {
84
+ reject(new Error("LSP server start timeout"));
85
+ }
86
+ }, config.timeout ?? 10000);
87
+ });
88
+ }
89
+ handleData(chunk) {
90
+ this.buffer += chunk.toString();
91
+ this.parseMessages();
92
+ }
93
+ parseMessages() {
94
+ while (true) {
95
+ if (this.contentLength === -1) {
96
+ const headerEnd = this.buffer.indexOf("\r\n\r\n");
97
+ if (headerEnd === -1)
98
+ return;
99
+ const header = this.buffer.slice(0, headerEnd);
100
+ const match = header.match(/Content-Length: (\d+)/i);
101
+ if (!match) {
102
+ this.buffer = this.buffer.slice(headerEnd + 4);
103
+ this.contentLength = -1;
104
+ continue;
105
+ }
106
+ this.contentLength = parseInt(match[1], 10);
107
+ // Guard against malicious/buggy servers sending absurd Content-Length
108
+ if (this.contentLength > 10 * 1024 * 1024) {
109
+ this.buffer = this.buffer.slice(headerEnd + 4);
110
+ this.contentLength = -1;
111
+ continue;
112
+ }
113
+ this.buffer = this.buffer.slice(headerEnd + 4);
114
+ }
115
+ if (this.buffer.length < this.contentLength)
116
+ return;
117
+ const body = this.buffer.slice(0, this.contentLength);
118
+ this.buffer = this.buffer.slice(this.contentLength);
119
+ this.contentLength = -1;
120
+ try {
121
+ const msg = JSON.parse(body);
122
+ this.handleMessage(msg);
123
+ }
124
+ catch { }
125
+ }
126
+ }
127
+ handleMessage(msg) {
128
+ if (msg.method === "textDocument/publishDiagnostics") {
129
+ const params = msg.params;
130
+ if (params?.diagnostics) {
131
+ this.diagnostics = params.diagnostics;
132
+ if (this.diagnosticsTimer) {
133
+ clearTimeout(this.diagnosticsTimer);
134
+ this.diagnosticsTimer = null;
135
+ }
136
+ if (this.diagnosticsResolve) {
137
+ this.diagnosticsResolve(this.diagnostics);
138
+ this.diagnosticsResolve = null;
139
+ }
140
+ }
141
+ return;
142
+ }
143
+ if (msg.id !== undefined && msg.id !== null) {
144
+ const pending = this.pending.get(msg.id);
145
+ if (pending) {
146
+ this.pending.delete(msg.id);
147
+ if (msg.error) {
148
+ pending.reject(new Error(String(msg.error.message ?? "LSP error")));
149
+ }
150
+ else {
151
+ pending.resolve(msg.result);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ sendRequest(method, params, timeout) {
157
+ return new Promise((resolve, reject) => {
158
+ const id = ++this.requestId;
159
+ this.pending.set(id, { resolve, reject });
160
+ const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
161
+ this.write(message);
162
+ setTimeout(() => {
163
+ if (this.pending.has(id)) {
164
+ this.pending.delete(id);
165
+ reject(new Error(`LSP request timeout: ${method}`));
166
+ }
167
+ }, timeout);
168
+ });
169
+ }
170
+ sendNotification(method, params) {
171
+ const message = JSON.stringify({ jsonrpc: "2.0", method, params });
172
+ this.write(message);
173
+ }
174
+ write(message) {
175
+ if (!this.process?.stdin?.writable)
176
+ return;
177
+ const header = `Content-Length: ${Buffer.byteLength(message)}\r\n\r\n`;
178
+ try {
179
+ this.process.stdin.write(header + message);
180
+ }
181
+ catch { }
182
+ }
183
+ async shutdown() {
184
+ try {
185
+ if (this.process && this.initialized && this.process.stdin?.writable) {
186
+ this.sendRequest("shutdown", null, 3000).catch(() => { });
187
+ this.sendNotification("exit", null);
188
+ await new Promise((r) => setTimeout(r, 200));
189
+ }
190
+ }
191
+ catch {
192
+ }
193
+ finally {
194
+ if (this.process) {
195
+ try {
196
+ this.process.kill();
197
+ }
198
+ catch { }
199
+ this.process = null;
200
+ }
201
+ this.initialized = false;
202
+ if (this.diagnosticsTimer) {
203
+ clearTimeout(this.diagnosticsTimer);
204
+ this.diagnosticsTimer = null;
205
+ }
206
+ }
207
+ }
208
+ pathToUri(filePath) {
209
+ const normalized = filePath.replace(/\\/g, "/");
210
+ if (/^[a-zA-Z]:/.test(normalized)) {
211
+ return `file:///${normalized}`;
212
+ }
213
+ return `file://${normalized}`;
214
+ }
215
+ languageFromPath(filePath) {
216
+ const ext = filePath.split(".").pop()?.toLowerCase();
217
+ const map = {
218
+ ts: "typescript",
219
+ tsx: "typescriptreact",
220
+ js: "javascript",
221
+ jsx: "javascriptreact",
222
+ py: "python",
223
+ go: "go",
224
+ rs: "rust",
225
+ json: "json",
226
+ html: "html",
227
+ css: "css",
228
+ scss: "scss",
229
+ md: "markdown",
230
+ yaml: "yaml",
231
+ yml: "yaml",
232
+ };
233
+ return map[ext ?? ""] ?? "plaintext";
234
+ }
235
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * LSP server defaults.
3
+ *
4
+ * Strategy: prefer `npx` (auto-downloads) over globally installed binaries.
5
+ * Servers using system tools (rust-analyzer, gopls) have `autoInstall: false`
6
+ * and are skipped silently when not found.
7
+ */
8
+ export const DEFAULT_LSP_CONFIG = {
9
+ enabled: true,
10
+ timeout: 15000,
11
+ servers: {
12
+ typescript: {
13
+ command: 'npx',
14
+ args: ['typescript-language-server', '--stdio'],
15
+ timeout: 20000,
16
+ autoInstall: true,
17
+ },
18
+ javascript: {
19
+ command: 'npx',
20
+ args: ['typescript-language-server', '--stdio'],
21
+ timeout: 15000,
22
+ autoInstall: true,
23
+ },
24
+ python: {
25
+ command: 'npx',
26
+ args: ['pyright', '--stdio'],
27
+ timeout: 15000,
28
+ autoInstall: true,
29
+ },
30
+ rust: {
31
+ command: 'rust-analyzer',
32
+ timeout: 15000,
33
+ autoInstall: false,
34
+ },
35
+ go: {
36
+ command: 'gopls',
37
+ timeout: 15000,
38
+ autoInstall: false,
39
+ },
40
+ json: {
41
+ command: 'npx',
42
+ args: ['vscode-json-languageserver', '--stdio'],
43
+ timeout: 10000,
44
+ autoInstall: true,
45
+ },
46
+ html: {
47
+ command: 'npx',
48
+ args: ['vscode-html-languageserver', '--stdio'],
49
+ timeout: 10000,
50
+ autoInstall: true,
51
+ },
52
+ css: {
53
+ command: 'npx',
54
+ args: ['vscode-css-languageserver', '--stdio'],
55
+ timeout: 10000,
56
+ autoInstall: true,
57
+ },
58
+ },
59
+ };
60
+ const LANGUAGE_MAP = {
61
+ ts: 'typescript',
62
+ tsx: 'typescript',
63
+ js: 'javascript',
64
+ jsx: 'javascript',
65
+ py: 'python',
66
+ rs: 'rust',
67
+ go: 'go',
68
+ json: 'json',
69
+ html: 'html',
70
+ css: 'css',
71
+ scss: 'css',
72
+ };
73
+ export function getServerForFile(filePath, config) {
74
+ const ext = filePath.split('.').pop()?.toLowerCase();
75
+ if (!ext)
76
+ return null;
77
+ const lang = LANGUAGE_MAP[ext];
78
+ if (!lang)
79
+ return null;
80
+ return config.servers[lang] ?? null;
81
+ }
@@ -0,0 +1,3 @@
1
+ export { LspModule } from './module';
2
+ export { LspClient } from './client';
3
+ export { getServerForFile, DEFAULT_LSP_CONFIG } from './config';
@@ -0,0 +1,68 @@
1
+ import { existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ import { LspClient } from './client';
4
+ import { getServerForFile, DEFAULT_LSP_CONFIG } from './config';
5
+ const severityLabels = {
6
+ 1: 'error',
7
+ 2: 'warning',
8
+ 3: 'info',
9
+ 4: 'hint',
10
+ };
11
+ export class LspModule {
12
+ name = 'lsp';
13
+ config;
14
+ client = new LspClient();
15
+ constructor(config) {
16
+ this.config = { ...DEFAULT_LSP_CONFIG, ...config };
17
+ }
18
+ getPlugin() {
19
+ const self = this;
20
+ return {
21
+ name: 'lsp-check',
22
+ priority: 15,
23
+ onAfterTool: async (_ctx, call, result) => {
24
+ if (!self.config.enabled)
25
+ return;
26
+ if (call.name !== 'write_file' && call.name !== 'edit_file')
27
+ return;
28
+ if (!result.success)
29
+ return;
30
+ const filePath = String(call.arguments.path ?? '');
31
+ if (!filePath)
32
+ return;
33
+ const fullPath = resolve(_ctx.baseDir, filePath);
34
+ if (!existsSync(fullPath))
35
+ return;
36
+ const serverConfig = getServerForFile(fullPath, self.config);
37
+ if (!serverConfig)
38
+ return;
39
+ try {
40
+ const diagnostics = await self.client.checkFile(fullPath, _ctx.baseDir, serverConfig);
41
+ const errors = diagnostics.filter((d) => d.severity === 1);
42
+ const warnings = diagnostics.filter((d) => d.severity === 2);
43
+ if (errors.length > 0) {
44
+ const items = formatDiagnostics(errors, fullPath);
45
+ result.output += `\n\n[LSP errors]:\n${items}`;
46
+ }
47
+ if (warnings.length > 0) {
48
+ const items = formatDiagnostics(warnings, fullPath);
49
+ result.output += `\n\n[LSP warnings]:\n${items}`;
50
+ }
51
+ }
52
+ catch { }
53
+ },
54
+ };
55
+ }
56
+ }
57
+ function formatDiagnostics(diagnostics, filePath) {
58
+ const filename = filePath.replace(/\\/g, '/').split('/').pop() ?? filePath;
59
+ return diagnostics
60
+ .slice(0, 5)
61
+ .map((d) => {
62
+ const line = d.range.start.line + 1;
63
+ const col = d.range.start.character + 1;
64
+ const sev = severityLabels[d.severity] ?? 'unknown';
65
+ return ` ${filename}(${line},${col}): ${sev}: ${d.message}`;
66
+ })
67
+ .join('\n');
68
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -112,19 +112,25 @@ export class MCPClient {
112
112
  env: { ...process.env, ...this.config.env },
113
113
  stdio: ["pipe", "pipe", "pipe"],
114
114
  });
115
+ let _resolved = false;
115
116
  child.on("error", (err) => {
116
117
  this._connected = false;
117
- reject(err);
118
+ if (!_resolved) {
119
+ _resolved = true;
120
+ reject(err);
121
+ }
118
122
  });
119
123
  child.on("exit", (code) => {
120
124
  this._connected = false;
121
- if (code !== 0) {
125
+ if (code !== 0 && !_resolved) {
126
+ _resolved = true;
122
127
  reject(new Error(`MCP server exited with code ${code}`));
123
128
  }
124
129
  });
125
130
  child.on("spawn", () => {
126
131
  this._connected = true;
127
132
  this.process = child;
133
+ _resolved = true;
128
134
  resolve();
129
135
  });
130
136
  });
@@ -62,4 +62,8 @@ export class MemoryStore {
62
62
  writeFileSync(this.prefsPath(), JSON.stringify(prefs, null, 2), 'utf-8');
63
63
  return true;
64
64
  }
65
+ appendRule(category, pattern, cause, solution) {
66
+ const entry = `**pattern:** ${pattern}\n **cause:** ${cause}\n **solution:** ${solution}`;
67
+ this.append(category, entry);
68
+ }
65
69
  }