micro-models-agent 0.28.17 → 0.29.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 (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 +624 -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,17 +1,17 @@
1
- import { ConsistencyCheck } from "./consistency";
2
- import { ConfidenceCheck } from "./confidence";
3
- import { FactualCheck } from "./factual";
4
- import { LLMJudge } from "./llm-judge";
1
+ import { FactualCheck } from './factual';
2
+ import { ConsistencyCheck } from './consistency';
3
+ import { ConfidenceCheck } from './confidence';
5
4
  export class HallucinationDetector {
5
+ factual;
6
6
  consistency;
7
7
  confidence;
8
- factual;
9
- judge;
10
- constructor(baseDir, llmProvider) {
8
+ constructor() {
9
+ this.factual = new FactualCheck();
11
10
  this.consistency = new ConsistencyCheck();
12
11
  this.confidence = new ConfidenceCheck();
13
- this.factual = baseDir ? new FactualCheck(baseDir) : null;
14
- this.judge = llmProvider ? new LLMJudge(llmProvider) : null;
12
+ }
13
+ getFactualCheck() {
14
+ return this.factual;
15
15
  }
16
16
  getConsistencyCheck() {
17
17
  return this.consistency;
@@ -19,28 +19,23 @@ export class HallucinationDetector {
19
19
  getConfidenceCheck() {
20
20
  return this.confidence;
21
21
  }
22
- async validate(response) {
22
+ validate(response) {
23
23
  const confidenceResult = this.confidence.validate(response);
24
- if (confidenceResult.status === "retry" ||
25
- confidenceResult.status === "block") {
24
+ if (confidenceResult.status === 'retry' || confidenceResult.status === 'block') {
26
25
  return confidenceResult;
27
26
  }
28
- const factualResult = this.factual?.validate(response);
29
- if (factualResult && factualResult.status !== "pass") {
30
- return factualResult;
31
- }
32
- // LLM-as-judge consistency: fast path (no decisions) skips any LLM call.
33
- const judgeResult = this.judge
34
- ? await this.judge.validate(response, this.consistency)
35
- : null;
27
+ const factualResult = this.factual.validate(response);
28
+ const consistencyResult = this.consistency.validate(response);
36
29
  const warnings = [];
37
- if (judgeResult && judgeResult.status === "warn")
38
- warnings.push(judgeResult.reason || "");
39
- if (confidenceResult.status === "warn")
40
- warnings.push(confidenceResult.reason || "");
30
+ if (factualResult.status === 'warn')
31
+ warnings.push(factualResult.reason || '');
32
+ if (consistencyResult.status === 'warn')
33
+ warnings.push(consistencyResult.reason || '');
34
+ if (confidenceResult.status === 'warn')
35
+ warnings.push(confidenceResult.reason || '');
41
36
  if (warnings.length > 0) {
42
- return { status: "warn", reason: warnings.join("; ") };
37
+ return { status: 'warn', reason: warnings.join('; ') };
43
38
  }
44
- return { status: "pass" };
39
+ return { status: 'pass' };
45
40
  }
46
41
  }
@@ -1,190 +1,170 @@
1
- import { existsSync, readdirSync } from "fs";
2
- import { resolve, isAbsolute, join } from "path";
3
1
  import { t } from "../../i18n/index";
4
- import { isJsMemberAccess } from "./js-identifiers";
5
- /** Generated/cached dirs to skip when scanning for bare file names. */
6
- const IGNORED_DIRS = new Set([
7
- "node_modules",
8
- ".git",
9
- "dist",
10
- "build",
11
- "coverage",
12
- ".mma",
13
- "_testing",
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ const FILE_EXTENSIONS = new Set([
5
+ "ts",
6
+ "tsx",
7
+ "js",
8
+ "jsx",
9
+ "mjs",
10
+ "cjs",
11
+ "mts",
12
+ "cts",
13
+ "json",
14
+ "md",
15
+ "yaml",
16
+ "yml",
17
+ "py",
18
+ "rs",
19
+ "go",
20
+ "java",
21
+ "c",
22
+ "cpp",
23
+ "h",
24
+ "hpp",
25
+ "html",
26
+ "css",
27
+ "scss",
28
+ "less",
29
+ "vue",
30
+ "svelte",
31
+ "sh",
32
+ "bash",
33
+ "zsh",
34
+ "ps1",
35
+ "bat",
36
+ "cmd",
37
+ "txt",
38
+ "env",
39
+ "env.local",
40
+ "env.production",
41
+ "gitignore",
42
+ "dockerignore",
43
+ "dockerfile",
44
+ "makefile",
45
+ "cmake",
46
+ "toml",
47
+ "lock",
48
+ "config",
49
+ "log",
50
+ "xml",
51
+ "sql",
52
+ "graphql",
53
+ "proto",
54
+ "wasm",
14
55
  ]);
15
- const NON_FILE_EXTENSIONS = new Set([
16
- "com",
17
- "org",
18
- "net",
19
- "gov",
20
- "edu",
21
- "io",
22
- "dev",
23
- "app",
24
- "co",
25
- "ai",
26
- "ru",
27
- "de",
28
- "fr",
29
- "uk",
30
- "us",
31
- "es",
32
- "it",
33
- "jp",
34
- "cn",
35
- "br",
56
+ const VERSION_PATTERN = /^\d+(\.\d+)*$/;
57
+ const COMMON_WORDS = new Set([
58
+ "node.js", "Node.js",
59
+ "console.log", "console.error", "console.warn", "console.info",
60
+ "Math.floor", "Math.ceil", "Math.round", "Math.max", "Math.min",
61
+ "JSON.parse", "JSON.stringify",
62
+ "Object.keys", "Object.values", "Object.entries",
63
+ "Array.from", "Array.isArray",
64
+ "Date.now", "Date.parse",
65
+ "RegExp", "Promise",
36
66
  ]);
37
- /** Well-known runtime/library names that end in .js/.ts but are NOT files. */
38
- const TECH_NAMES = new Set([
39
- "node.js",
40
- "react.js",
41
- "vue.js",
42
- "next.js",
43
- "angular.js",
44
- "express.js",
45
- "deno.js",
46
- "qwik.js",
47
- "svelte.js",
48
- "jquery.js",
49
- ]);
50
- /**
51
- * JS/TS global identifiers whose member access (`process.env`, `console.log`)
52
- * looks like a file path to the regex but is code, not a file.
53
- */
54
- function looksLikeFilePath(s) {
55
- const lower = s.toLowerCase();
56
- // "Node.js" etc. are technologies, not files — never flag them.
57
- if (TECH_NAMES.has(lower))
58
- return false;
59
- if (!s.includes("/") && !s.includes("\\") && !s.match(/^\w+\.[a-z]{2,4}$/i))
60
- return false;
61
- // process.env / console.log / Math.max — member access on JS globals,
62
- // not file paths. Only applies to bare `word.word` (no slashes).
63
- if (isJsMemberAccess(s))
64
- return false;
65
- const ext = s.split(".").pop()?.toLowerCase() || "";
66
- return !NON_FILE_EXTENSIONS.has(ext);
67
- }
68
67
  export class FactualCheck {
69
- baseDir;
70
- nameCache = null;
71
- nameCacheTime = 0;
72
- lastRescan = 0;
73
- static NAME_CACHE_TTL_MS = 5_000;
74
- static RESCAN_COOLDOWN_MS = 2_000;
75
- static MAX_INDEXED_FILES = 20_000;
76
- constructor(baseDir) {
77
- this.baseDir = baseDir;
68
+ knownPaths = new Set();
69
+ createdPaths = new Set();
70
+ readFiles = new Set();
71
+ baseDir = process.cwd();
72
+ setBaseDir(dir) {
73
+ this.baseDir = dir;
78
74
  }
79
- validate(response) {
80
- const filePaths = this.extractFilePaths(response);
81
- if (filePaths.length === 0)
82
- return { status: "pass" };
83
- const nonExistent = [];
84
- for (const fp of filePaths) {
85
- if (!this.pathExists(fp)) {
86
- nonExistent.push(fp);
87
- }
88
- }
89
- if (nonExistent.length > 0) {
90
- return {
91
- status: "warn",
92
- reason: t("hall.unknown_paths", {
93
- paths: nonExistent.slice(0, 5).join(", "),
94
- }),
95
- };
96
- }
97
- return { status: "pass" };
98
- }
99
- /**
100
- * Bare filenames ("bikes.json") and relative paths ("src/data/bikes.json")
101
- * both land on the project tree. Absolute paths are checked as-is.
102
- */
103
- pathExists(fp) {
104
- if (isAbsolute(fp))
105
- return existsSync(fp);
106
- // "foo.json" has no directory part — treat it as a bare name that may
107
- // live anywhere under baseDir (e.g. src/data/bikes.json). Resolving it
108
- // against the workspace root alone produced false "unknown file" warnings.
109
- if (!fp.includes("/") && !fp.includes("\\")) {
110
- return this.bareNameExists(fp);
111
- }
112
- return existsSync(resolve(this.baseDir, fp));
75
+ trackReadPath(path) {
76
+ this.knownPaths.add(path);
77
+ const base = path.split(/[/\\]/).pop();
78
+ if (base && base !== path)
79
+ this.knownPaths.add(base);
80
+ // Track that this file was actually read by the agent
81
+ this.readFiles.add(base || path);
113
82
  }
114
- bareNameExists(name) {
115
- if (existsSync(resolve(this.baseDir, name)))
116
- return true;
117
- if (this.indexHas(name))
118
- return true;
119
- // A write_file from this session may have created the file after the
120
- // index was built. Do a one-off rescan (rate-limited) before warning.
121
- const now = Date.now();
122
- if (now - this.lastRescan < FactualCheck.RESCAN_COOLDOWN_MS)
123
- return false;
124
- this.lastRescan = now;
125
- this.nameCache = null;
126
- this.nameCacheTime = 0;
127
- return this.indexHas(name);
83
+ trackCreatedPath(path) {
84
+ this.knownPaths.add(path);
85
+ const base = path.split(/[/\\]/).pop();
86
+ if (base && base !== path)
87
+ this.knownPaths.add(base);
88
+ this.createdPaths.add(path);
128
89
  }
129
- indexHas(name) {
130
- return (this.getNameIndex().get(name)?.length ?? 0) > 0;
90
+ trackDeletedPath(path) {
91
+ this.knownPaths.add(path);
92
+ const base = path.split(/[/\\]/).pop();
93
+ if (base && base !== path)
94
+ this.knownPaths.add(base);
95
+ this.createdPaths.delete(path);
131
96
  }
132
97
  /**
133
- * Basename paths index, rebuilt every NAME_CACHE_TTL_MS so newly created
134
- * files (write_file) are picked up without a full scan on every response.
98
+ * Register file paths found in a document (e.g. structure.md, README).
99
+ * Files mentioned in project documentation are not hallucinations.
135
100
  */
136
- getNameIndex() {
137
- const now = Date.now();
138
- if (this.nameCache &&
139
- now - this.nameCacheTime < FactualCheck.NAME_CACHE_TTL_MS) {
140
- return this.nameCache;
141
- }
142
- const index = new Map();
143
- const scanned = this.scanDir(this.baseDir, index, 0);
144
- if (scanned > 0 || index.size > 0) {
145
- this.nameCache = index;
146
- this.nameCacheTime = now;
101
+ trackDocumentContent(content) {
102
+ // Match common path patterns in documentation
103
+ const pathPatterns = /(?:^|\s)([\w\-./]+\.\w{1,10})(?:\s|$|[,;)])/gm;
104
+ let match;
105
+ while ((match = pathPatterns.exec(content)) !== null) {
106
+ const file = match[1];
107
+ if (file.includes('/') || file.includes('\\')) {
108
+ this.knownPaths.add(file);
109
+ }
110
+ const base = file.split(/[/\\]/).pop();
111
+ if (base)
112
+ this.knownPaths.add(base);
147
113
  }
148
- return index;
149
114
  }
150
- scanDir(dir, index, count) {
151
- if (count >= FactualCheck.MAX_INDEXED_FILES)
152
- return count;
153
- let entries;
115
+ pathExistsOnDisk(path) {
154
116
  try {
155
- entries = readdirSync(dir, { withFileTypes: true });
117
+ // Skip absolute paths they're either system paths or outside the project.
118
+ // On Windows, existsSync('/...') can hang on certain paths.
119
+ if (path.startsWith("/") || path.startsWith("~") || /^[A-Za-z]:/.test(path)) {
120
+ return false;
121
+ }
122
+ return existsSync(join(this.baseDir, path));
156
123
  }
157
124
  catch {
158
- return count;
125
+ return false;
159
126
  }
160
- for (const entry of entries) {
161
- if (count >= FactualCheck.MAX_INDEXED_FILES)
162
- break;
163
- const full = join(dir, entry.name);
164
- if (entry.isDirectory()) {
165
- if (!IGNORED_DIRS.has(entry.name)) {
166
- count = this.scanDir(full, index, count);
167
- }
168
- }
169
- else if (entry.isFile()) {
170
- const list = index.get(entry.name);
171
- if (list)
172
- list.push(full);
173
- else
174
- index.set(entry.name, [full]);
175
- count++;
127
+ }
128
+ validate(response) {
129
+ const pathRegex = /[\w\-./]+\.\w+/g;
130
+ const mentionedPaths = response.match(pathRegex) || [];
131
+ const unknownPaths = mentionedPaths.filter((p) => {
132
+ if (this.knownPaths.has(p))
133
+ return false;
134
+ if (VERSION_PATTERN.test(p))
135
+ return false;
136
+ if (COMMON_WORDS.has(p))
137
+ return false;
138
+ // Skip absolute paths — agent responses use relative paths; absolute
139
+ // paths are either system paths or URLs, and existsSync can hang on
140
+ // Windows for root-relative paths like "/page.html".
141
+ if (p.startsWith("/") || p.startsWith("~") || /^[A-Za-z]:/.test(p))
142
+ return false;
143
+ if (this.pathExistsOnDisk(p))
144
+ return false;
145
+ const ext = p.split(".").pop()?.toLowerCase() || "";
146
+ if (!FILE_EXTENSIONS.has(ext))
147
+ return false;
148
+ const basename = p
149
+ .split("/")
150
+ .pop()
151
+ ?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
152
+ if (basename) {
153
+ const urlPattern = new RegExp(`(https?|file)://[^\\s]*${basename}`);
154
+ if (urlPattern.test(response))
155
+ return false;
176
156
  }
157
+ return true;
158
+ });
159
+ if (unknownPaths.length > 0) {
160
+ const uniquePaths = [...new Set(unknownPaths)];
161
+ return {
162
+ status: "warn",
163
+ reason: t("hall.unknown_paths", {
164
+ paths: uniquePaths.slice(0, 3).join(", "),
165
+ }),
166
+ };
177
167
  }
178
- return count;
179
- }
180
- extractFilePaths(text) {
181
- // Capture absolute Windows paths (with drive letter) and relative paths.
182
- // The old pattern started at a word boundary, so "E:\project\src\a.ts"
183
- // only matched "project\src\a.ts" — resolve() then glued that onto
184
- // baseDir producing a bogus path and a false "unknown file" warning
185
- // even for files that actually exist on disk.
186
- const pattern = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{2,6}\b/gi;
187
- const matches = text.match(pattern) || [];
188
- return [...new Set(matches)].filter(looksLikeFilePath);
168
+ return { status: "pass" };
189
169
  }
190
170
  }
@@ -1,5 +1,4 @@
1
- export { HallucinationDetector, } from "./detector";
2
- export { ConsistencyCheck } from "./consistency";
3
- export { ConfidenceCheck } from "./confidence";
4
- export { FactualCheck } from "./factual";
5
- export { LLMJudge } from "./llm-judge";
1
+ export { HallucinationDetector } from './detector';
2
+ export { FactualCheck } from './factual';
3
+ export { ConsistencyCheck } from './consistency';
4
+ export { ConfidenceCheck } from './confidence';
@@ -1,5 +1,5 @@
1
- export { ModuleRegistry } from "./registry";
2
- export { SkillsLoader, SkillsModule } from "./skills";
3
- export { PluginManager } from "./plugins";
4
- export { PluginLoader } from "./plugins/loader";
5
- export { MCPClient, MCPRegistry } from "./mcp";
1
+ export { ModuleRegistry } from './registry';
2
+ export { SkillsLoader, SkillsMatcher, SkillsModule } from './skills';
3
+ export { PluginManager } from './plugins';
4
+ export { PluginLoader } from './plugins/loader';
5
+ export { MCPClient, MCPRegistry } from './mcp';
@@ -112,25 +112,19 @@ export class MCPClient {
112
112
  env: { ...process.env, ...this.config.env },
113
113
  stdio: ["pipe", "pipe", "pipe"],
114
114
  });
115
- let _resolved = false;
116
115
  child.on("error", (err) => {
117
116
  this._connected = false;
118
- if (!_resolved) {
119
- _resolved = true;
120
- reject(err);
121
- }
117
+ reject(err);
122
118
  });
123
119
  child.on("exit", (code) => {
124
120
  this._connected = false;
125
- if (code !== 0 && !_resolved) {
126
- _resolved = true;
121
+ if (code !== 0) {
127
122
  reject(new Error(`MCP server exited with code ${code}`));
128
123
  }
129
124
  });
130
125
  child.on("spawn", () => {
131
126
  this._connected = true;
132
127
  this.process = child;
133
- _resolved = true;
134
128
  resolve();
135
129
  });
136
130
  });
@@ -62,8 +62,4 @@ 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
- }
69
65
  }