micro-models-agent 0.28.17 → 0.29.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 (94) hide show
  1. package/dist/cli/commands.js +3 -116
  2. package/dist/cli/main.js +8 -35
  3. package/dist/cli/repl.js +611 -110
  4. package/dist/cli/setup.js +12 -32
  5. package/dist/config/config.js +30 -46
  6. package/dist/config/defaults.js +1 -10
  7. package/dist/config/security.js +8 -15
  8. package/dist/core/agent-moe.js +12 -24
  9. package/dist/core/agent.js +47 -281
  10. package/dist/core/bootstrap.js +36 -52
  11. package/dist/core/session-logger.js +2 -35
  12. package/dist/i18n/en.json +15 -79
  13. package/dist/i18n/index.js +9 -12
  14. package/dist/i18n/ru.json +15 -79
  15. package/dist/index.js +13 -13
  16. package/dist/llm/openai-compat.js +10 -39
  17. package/dist/logger/app-logger.js +16 -83
  18. package/dist/main.js +625 -243
  19. package/dist/modules/browser/session.js +60 -108
  20. package/dist/modules/context/history.js +15 -0
  21. package/dist/modules/context/manager.js +10 -119
  22. package/dist/modules/execution/auditor.js +39 -33
  23. package/dist/modules/execution/index.js +6 -8
  24. package/dist/modules/execution/module.js +32 -474
  25. package/dist/modules/execution/moe-executor.js +40 -97
  26. package/dist/modules/execution/planner.js +13 -63
  27. package/dist/modules/execution/stuck-detector.js +39 -252
  28. package/dist/modules/execution/tracker.js +7 -21
  29. package/dist/modules/execution/verifier.js +17 -46
  30. package/dist/modules/hallucination/confidence.js +2 -7
  31. package/dist/modules/hallucination/consistency.js +42 -8
  32. package/dist/modules/hallucination/detector.js +21 -26
  33. package/dist/modules/hallucination/factual.js +150 -170
  34. package/dist/modules/hallucination/index.js +4 -5
  35. package/dist/modules/index.js +5 -5
  36. package/dist/modules/mcp/client.js +2 -8
  37. package/dist/modules/memory/store.js +0 -4
  38. package/dist/modules/plugins/builtin/lint-on-write.js +38 -143
  39. package/dist/modules/processes/detect.js +34 -0
  40. package/dist/modules/processes/index.js +2 -1
  41. package/dist/modules/processes/registry.js +35 -125
  42. package/dist/modules/processes/runner.js +110 -9
  43. package/dist/modules/security/audit-log.js +10 -30
  44. package/dist/modules/security/command-validator.js +16 -42
  45. package/dist/modules/security/content-scanner.js +8 -9
  46. package/dist/modules/security/network-validator.js +2 -2
  47. package/dist/modules/security/path-validator.js +10 -64
  48. package/dist/modules/security/security-policies.js +67 -221
  49. package/dist/modules/security/session-encryption.js +25 -42
  50. package/dist/modules/session/manager.js +10 -15
  51. package/dist/modules/session/store.js +8 -62
  52. package/dist/modules/skills/index.js +3 -2
  53. package/dist/modules/skills/matcher.js +27 -0
  54. package/dist/modules/skills/module.js +23 -10
  55. package/dist/tools/bash.js +90 -287
  56. package/dist/tools/create-dir.js +1 -0
  57. package/dist/tools/delete-file.js +1 -0
  58. package/dist/tools/edit-file.js +8 -10
  59. package/dist/tools/executor.js +7 -57
  60. package/dist/tools/grep-tool.js +29 -51
  61. package/dist/tools/index.js +40 -55
  62. package/dist/tools/load-skill.js +18 -14
  63. package/dist/tools/move-file.js +2 -3
  64. package/dist/tools/pipeline-run.js +1 -1
  65. package/dist/tools/read-file.js +5 -15
  66. package/dist/tools/search-history.js +22 -42
  67. package/dist/tools/subagent.js +12 -21
  68. package/dist/tools/web-browse.js +25 -54
  69. package/dist/tools/web-fetch.js +34 -60
  70. package/dist/tools/web-search.js +20 -39
  71. package/dist/tools/write-file.js +10 -13
  72. package/dist/ui/diff.js +16 -9
  73. package/dist/ui/renderer.js +6 -69
  74. package/package.json +1 -1
  75. package/dist/cli/repl-commands.js +0 -633
  76. package/dist/core/workspace.js +0 -76
  77. package/dist/logger/file-log.js +0 -151
  78. package/dist/modules/certification/cli.js +0 -176
  79. package/dist/modules/certification/fact-checker.js +0 -84
  80. package/dist/modules/certification/loader.js +0 -111
  81. package/dist/modules/certification/manifest.js +0 -50
  82. package/dist/modules/certification/runner.js +0 -162
  83. package/dist/modules/certification/scenarios.js +0 -124
  84. package/dist/modules/certification/types.js +0 -1
  85. package/dist/modules/execution/plan-coverage.js +0 -68
  86. package/dist/modules/execution/plan-persister.js +0 -46
  87. package/dist/modules/execution/plan-store.js +0 -159
  88. package/dist/modules/hallucination/js-identifiers.js +0 -72
  89. package/dist/modules/hallucination/llm-judge.js +0 -103
  90. package/dist/modules/lsp/client.js +0 -235
  91. package/dist/modules/lsp/config.js +0 -81
  92. package/dist/modules/lsp/index.js +0 -3
  93. package/dist/modules/lsp/module.js +0 -68
  94. package/dist/modules/lsp/types.js +0 -1
@@ -1,103 +0,0 @@
1
- import { t } from "../../i18n/index";
2
- const JUDGE_TIMEOUT_MS = 15000;
3
- const MAX_DECISIONS_SHOWN = 6;
4
- const MAX_RESPONSE_CHARS = 2000;
5
- /**
6
- * LLM-as-judge consistency check. Asks a model whether the agent's latest
7
- * response contradicts any of its earlier decisions — replacing the fragile
8
- * word-matching heuristic that only caught exact substring reversals.
9
- *
10
- * Fast path: when no decisions are tracked, returns pass without any LLM call.
11
- * Any judge failure (timeout, provider error, unparseable verdict) degrades to
12
- * pass so the agent loop is never blocked by the judge.
13
- */
14
- export class LLMJudge {
15
- provider;
16
- constructor(provider) {
17
- this.provider = provider;
18
- }
19
- async validate(response, consistency) {
20
- const decisions = consistency.getDecisions();
21
- if (decisions.length === 0) {
22
- return { status: "pass" };
23
- }
24
- const shown = decisions.slice(-MAX_DECISIONS_SHOWN);
25
- const decisionLines = shown
26
- .map((d, i) => `${i + 1}. [${d.location || "agent"}] "${d.decision}"`)
27
- .join("\n");
28
- const system = `You are a consistency checker for an AI coding agent. The agent previously made these decisions:
29
-
30
- ${decisionLines}
31
-
32
- Decide whether the agent's NEW response contradicts any of these decisions. A contradiction means reversing or abandoning a previously stated approach (e.g. switching to a different framework, changing a chosen strategy, or undoing a decision that was explicitly made).
33
-
34
- Reply with ONLY valid JSON, no other text:
35
- - {"contradicts": false}
36
- - {"contradicts": true, "reason": "short explanation"}`;
37
- const user = `Agent's new response:\n\n${response.slice(0, MAX_RESPONSE_CHARS)}`;
38
- try {
39
- const verdictText = await this.askJudge([
40
- { role: "system", content: system },
41
- { role: "user", content: user },
42
- ]);
43
- const verdict = this.parseVerdict(verdictText);
44
- if (verdict.contradicts) {
45
- const reason = verdict.reason?.trim()
46
- ? ` — ${verdict.reason.trim()}`
47
- : "";
48
- return {
49
- status: "warn",
50
- reason: t("hall.contradiction_llm", { reason }),
51
- };
52
- }
53
- return { status: "pass" };
54
- }
55
- catch {
56
- return { status: "pass" };
57
- }
58
- }
59
- async askJudge(messages) {
60
- let text = "";
61
- let timer;
62
- const consume = (async () => {
63
- for await (const chunk of this.provider.chat(messages, [])) {
64
- if (chunk.type === "text" && chunk.content) {
65
- text += chunk.content;
66
- }
67
- }
68
- return text;
69
- })();
70
- const timeout = new Promise((_, reject) => {
71
- timer = setTimeout(() => reject(new Error("LLM judge timed out")), JUDGE_TIMEOUT_MS);
72
- });
73
- try {
74
- return await Promise.race([consume, timeout]);
75
- }
76
- finally {
77
- if (timer)
78
- clearTimeout(timer);
79
- }
80
- }
81
- parseVerdict(text) {
82
- const block = text.match(/\{[\s\S]*\}/);
83
- if (block) {
84
- try {
85
- const obj = JSON.parse(block[0]);
86
- if (typeof obj.contradicts === "boolean") {
87
- return {
88
- contradicts: obj.contradicts,
89
- reason: typeof obj.reason === "string" ? obj.reason : undefined,
90
- };
91
- }
92
- }
93
- catch {
94
- // fall through to lenient parsing
95
- }
96
- }
97
- const lower = text.toLowerCase();
98
- if (lower.includes('"contradicts": true') || /^yes\b/.test(lower.trim())) {
99
- return { contradicts: true, reason: text.slice(0, 200) };
100
- }
101
- return { contradicts: false };
102
- }
103
- }
@@ -1,235 +0,0 @@
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
- }
@@ -1,81 +0,0 @@
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
- }
@@ -1,3 +0,0 @@
1
- export { LspModule } from './module';
2
- export { LspClient } from './client';
3
- export { getServerForFile, DEFAULT_LSP_CONFIG } from './config';
@@ -1,68 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export {};