micro-models-agent 0.46.2 → 0.47.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 (218) hide show
  1. package/README.md +312 -312
  2. package/dist/main.js +397 -353
  3. package/dist/modules/browser/bridge-server.mjs +202 -202
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +48 -48
  7. package/dist/cli/commands.js +0 -323
  8. package/dist/cli/completer.js +0 -167
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -165
  11. package/dist/cli/plugin-commands.js +0 -36
  12. package/dist/cli/repl-commands.js +0 -661
  13. package/dist/cli/repl.js +0 -616
  14. package/dist/cli/run-result.js +0 -22
  15. package/dist/cli/security-commands.js +0 -164
  16. package/dist/cli/setup.js +0 -231
  17. package/dist/config/config.js +0 -249
  18. package/dist/config/defaults.js +0 -124
  19. package/dist/config/experts.js +0 -15
  20. package/dist/config/index.js +0 -3
  21. package/dist/config/security.js +0 -193
  22. package/dist/config/types.js +0 -1
  23. package/dist/core/agent-moe.js +0 -102
  24. package/dist/core/agent.js +0 -886
  25. package/dist/core/bootstrap.js +0 -404
  26. package/dist/core/index.js +0 -2
  27. package/dist/core/prompt-builder.js +0 -76
  28. package/dist/core/session-logger.js +0 -197
  29. package/dist/core/types.js +0 -1
  30. package/dist/core/version.js +0 -24
  31. package/dist/core/workspace.js +0 -76
  32. package/dist/i18n/en.json +0 -598
  33. package/dist/i18n/index.js +0 -46
  34. package/dist/i18n/ru.json +0 -598
  35. package/dist/index.js +0 -22
  36. package/dist/llm/image-utils.js +0 -143
  37. package/dist/llm/index.js +0 -4
  38. package/dist/llm/model-loader.js +0 -78
  39. package/dist/llm/openai-compat.js +0 -359
  40. package/dist/llm/orchestrator.js +0 -198
  41. package/dist/llm/provider.js +0 -10
  42. package/dist/llm/response.js +0 -39
  43. package/dist/llm/token-counter.js +0 -39
  44. package/dist/llm/types.js +0 -1
  45. package/dist/logger/app-logger.js +0 -143
  46. package/dist/logger/file-log.js +0 -151
  47. package/dist/logger/index.js +0 -1
  48. package/dist/migration/backup.js +0 -45
  49. package/dist/migration/detect.js +0 -50
  50. package/dist/migration/index.js +0 -2
  51. package/dist/modules/artifacts/store.js +0 -61
  52. package/dist/modules/browser/actions.js +0 -76
  53. package/dist/modules/browser/bridge-client.js +0 -199
  54. package/dist/modules/browser/bridge-path.js +0 -10
  55. package/dist/modules/browser/cookie-store.js +0 -24
  56. package/dist/modules/browser/driver.js +0 -136
  57. package/dist/modules/browser/index.js +0 -7
  58. package/dist/modules/browser/module.js +0 -29
  59. package/dist/modules/browser/session.js +0 -338
  60. package/dist/modules/browser/snapshot.js +0 -148
  61. package/dist/modules/browser/types.js +0 -12
  62. package/dist/modules/certification/cli.js +0 -174
  63. package/dist/modules/certification/fact-checker.js +0 -82
  64. package/dist/modules/certification/loader.js +0 -105
  65. package/dist/modules/certification/manifest.js +0 -50
  66. package/dist/modules/certification/runner.js +0 -159
  67. package/dist/modules/certification/scenarios.js +0 -124
  68. package/dist/modules/certification/types.js +0 -1
  69. package/dist/modules/context/chunk-query.js +0 -100
  70. package/dist/modules/context/fact-extractor.js +0 -162
  71. package/dist/modules/context/history.js +0 -15
  72. package/dist/modules/context/index.js +0 -1
  73. package/dist/modules/context/manager.js +0 -423
  74. package/dist/modules/execution/audit-runners.js +0 -152
  75. package/dist/modules/execution/auditor.js +0 -218
  76. package/dist/modules/execution/execution-plugin.js +0 -272
  77. package/dist/modules/execution/index.js +0 -8
  78. package/dist/modules/execution/module.js +0 -436
  79. package/dist/modules/execution/moe-executor.js +0 -291
  80. package/dist/modules/execution/plan-coverage.js +0 -68
  81. package/dist/modules/execution/plan-persister.js +0 -46
  82. package/dist/modules/execution/plan-store.js +0 -157
  83. package/dist/modules/execution/plan-tool.js +0 -508
  84. package/dist/modules/execution/plan-validator.js +0 -153
  85. package/dist/modules/execution/planner.js +0 -90
  86. package/dist/modules/execution/stuck-detector.js +0 -510
  87. package/dist/modules/execution/tracker.js +0 -67
  88. package/dist/modules/execution/types.js +0 -1
  89. package/dist/modules/execution/verifier.js +0 -222
  90. package/dist/modules/execution/windows-commands.js +0 -41
  91. package/dist/modules/hallucination/confidence.js +0 -66
  92. package/dist/modules/hallucination/consistency.js +0 -26
  93. package/dist/modules/hallucination/detector.js +0 -43
  94. package/dist/modules/hallucination/factual.js +0 -129
  95. package/dist/modules/hallucination/index.js +0 -5
  96. package/dist/modules/hallucination/js-identifiers.js +0 -262
  97. package/dist/modules/hallucination/llm-judge.js +0 -101
  98. package/dist/modules/index.js +0 -5
  99. package/dist/modules/indexer/cache.js +0 -40
  100. package/dist/modules/indexer/index.js +0 -3
  101. package/dist/modules/indexer/module.js +0 -245
  102. package/dist/modules/indexer/project-profile.js +0 -183
  103. package/dist/modules/indexer/walker.js +0 -101
  104. package/dist/modules/lsp/check-tool.js +0 -58
  105. package/dist/modules/lsp/client.js +0 -278
  106. package/dist/modules/lsp/command.js +0 -60
  107. package/dist/modules/lsp/config.js +0 -135
  108. package/dist/modules/lsp/index.js +0 -3
  109. package/dist/modules/lsp/module.js +0 -232
  110. package/dist/modules/lsp/probe.js +0 -76
  111. package/dist/modules/lsp/project-root.js +0 -32
  112. package/dist/modules/lsp/startup-check.js +0 -141
  113. package/dist/modules/lsp/types.js +0 -1
  114. package/dist/modules/mcp/client.js +0 -399
  115. package/dist/modules/mcp/index.js +0 -3
  116. package/dist/modules/mcp/module.js +0 -142
  117. package/dist/modules/mcp/registry.js +0 -15
  118. package/dist/modules/memory/index.js +0 -1
  119. package/dist/modules/memory/module.js +0 -96
  120. package/dist/modules/memory/search.js +0 -42
  121. package/dist/modules/memory/store.js +0 -69
  122. package/dist/modules/pipelines/engine.js +0 -60
  123. package/dist/modules/pipelines/index.js +0 -3
  124. package/dist/modules/pipelines/parser.js +0 -56
  125. package/dist/modules/pipelines/template.js +0 -14
  126. package/dist/modules/plugins/builtin/lint-on-write.js +0 -231
  127. package/dist/modules/plugins/builtin/notify.js +0 -9
  128. package/dist/modules/plugins/index.js +0 -1
  129. package/dist/modules/plugins/loader.js +0 -70
  130. package/dist/modules/plugins/manager.js +0 -217
  131. package/dist/modules/plugins/types.js +0 -1
  132. package/dist/modules/processes/detect.js +0 -34
  133. package/dist/modules/processes/index.js +0 -2
  134. package/dist/modules/processes/registry.js +0 -327
  135. package/dist/modules/processes/runner.js +0 -23
  136. package/dist/modules/registry.js +0 -47
  137. package/dist/modules/security/audit-log.js +0 -136
  138. package/dist/modules/security/audit-notifier.js +0 -292
  139. package/dist/modules/security/command-validator.js +0 -205
  140. package/dist/modules/security/content-scanner.js +0 -53
  141. package/dist/modules/security/data-sanitizer.js +0 -89
  142. package/dist/modules/security/encryption.js +0 -242
  143. package/dist/modules/security/index.js +0 -14
  144. package/dist/modules/security/network-validator.js +0 -71
  145. package/dist/modules/security/path-validator.js +0 -207
  146. package/dist/modules/security/rate-limiter.js +0 -119
  147. package/dist/modules/security/security-policies.js +0 -531
  148. package/dist/modules/security/session-encryption.js +0 -210
  149. package/dist/modules/security/session-isolation.js +0 -95
  150. package/dist/modules/session/index.js +0 -3
  151. package/dist/modules/session/manager.js +0 -172
  152. package/dist/modules/session/module.js +0 -24
  153. package/dist/modules/session/store.js +0 -222
  154. package/dist/modules/session/types.js +0 -1
  155. package/dist/modules/skills/index.js +0 -2
  156. package/dist/modules/skills/loader.js +0 -72
  157. package/dist/modules/skills/matcher.js +0 -27
  158. package/dist/modules/skills/module.js +0 -129
  159. package/dist/modules/types.js +0 -1
  160. package/dist/modules/updater/checker.js +0 -96
  161. package/dist/modules/updater/index.js +0 -2
  162. package/dist/modules/updater/module.js +0 -116
  163. package/dist/modules/user-profile/compressor.js +0 -16
  164. package/dist/modules/user-profile/index.js +0 -1
  165. package/dist/modules/user-profile/profile.js +0 -68
  166. package/dist/tools/approve.js +0 -32
  167. package/dist/tools/attach-image.js +0 -89
  168. package/dist/tools/bash.js +0 -496
  169. package/dist/tools/browser.js +0 -114
  170. package/dist/tools/chunk-query.js +0 -99
  171. package/dist/tools/create-dir.js +0 -55
  172. package/dist/tools/delete-file.js +0 -62
  173. package/dist/tools/download-file.js +0 -116
  174. package/dist/tools/edit-file.js +0 -79
  175. package/dist/tools/enable-tools.js +0 -58
  176. package/dist/tools/executor.js +0 -144
  177. package/dist/tools/file-info.js +0 -46
  178. package/dist/tools/filter-tools.js +0 -17
  179. package/dist/tools/glob-tool.js +0 -26
  180. package/dist/tools/grep-tool.js +0 -84
  181. package/dist/tools/hidden-tools-block.js +0 -37
  182. package/dist/tools/index.js +0 -78
  183. package/dist/tools/list-dir.js +0 -48
  184. package/dist/tools/load-skill.js +0 -42
  185. package/dist/tools/mcp-call.js +0 -68
  186. package/dist/tools/move-file.js +0 -85
  187. package/dist/tools/path-utils.js +0 -51
  188. package/dist/tools/pipeline-run.js +0 -144
  189. package/dist/tools/preview.js +0 -2
  190. package/dist/tools/process-kill.js +0 -29
  191. package/dist/tools/process-list.js +0 -36
  192. package/dist/tools/process-log.js +0 -45
  193. package/dist/tools/question.js +0 -140
  194. package/dist/tools/read-file.js +0 -91
  195. package/dist/tools/recall.js +0 -117
  196. package/dist/tools/registry.js +0 -47
  197. package/dist/tools/remember.js +0 -67
  198. package/dist/tools/scope-check.js +0 -30
  199. package/dist/tools/search-history.js +0 -84
  200. package/dist/tools/subagent.js +0 -196
  201. package/dist/tools/types.js +0 -1
  202. package/dist/tools/user-input.js +0 -123
  203. package/dist/tools/web-browse.js +0 -86
  204. package/dist/tools/web-fetch.js +0 -98
  205. package/dist/tools/web-search.js +0 -78
  206. package/dist/tools/write-file.js +0 -81
  207. package/dist/ui/box.js +0 -77
  208. package/dist/ui/colors.js +0 -4
  209. package/dist/ui/diff.js +0 -178
  210. package/dist/ui/index.js +0 -6
  211. package/dist/ui/line-editor.js +0 -703
  212. package/dist/ui/line-math.js +0 -69
  213. package/dist/ui/md-formatter.js +0 -212
  214. package/dist/ui/output.js +0 -13
  215. package/dist/ui/plan-view.js +0 -103
  216. package/dist/ui/renderer.js +0 -209
  217. package/dist/ui/spinner.js +0 -70
  218. package/dist/ui/table.js +0 -144
@@ -1,222 +0,0 @@
1
- import { existsSync } from "fs";
2
- import { resolve, extname, join } from "path";
3
- import { spawn } from "child_process";
4
- import { t } from "../../i18n/index";
5
- import { validateExpertConfig } from "../../config/experts";
6
- import { findProjectRoot } from "../lsp/project-root";
7
- import { extractFileLikeTokens, stripUrls } from "../hallucination/js-identifiers";
8
- import { findExistingFile } from "./auditor";
9
- export class StepVerifier {
10
- baseDir;
11
- constructor(baseDir) {
12
- this.baseDir = baseDir;
13
- }
14
- async checkFileExists(path) {
15
- const resolved = resolve(this.baseDir, path);
16
- const exists = existsSync(resolved);
17
- return {
18
- passed: exists,
19
- message: exists ? t("verify.file_exists", { path }) : t("verify.file_not_found", { path }),
20
- };
21
- }
22
- async runScript(scriptName) {
23
- try {
24
- await this.runAsync(`bun run ${scriptName}`, this.baseDir, 60_000);
25
- return { passed: true, message: t("verify.script_passed", { script: scriptName }) };
26
- }
27
- catch (e) {
28
- return {
29
- passed: false,
30
- message: t("verify.script_failed", { script: scriptName, message: e.message }),
31
- };
32
- }
33
- }
34
- async runTypeCheck() {
35
- const tsconfigPath = resolve(this.baseDir, "tsconfig.json");
36
- if (!existsSync(tsconfigPath)) {
37
- return { passed: true, message: "No tsconfig.json found — skipping type check" };
38
- }
39
- try {
40
- await this.runAsync("npx tsc --noEmit", this.baseDir, 60_000);
41
- return { passed: true, message: "TypeScript type check passed" };
42
- }
43
- catch (e) {
44
- const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
45
- return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
46
- }
47
- }
48
- /**
49
- * Type-check a specific file, resolving the project root from the FILE's
50
- * location (not the agent baseDir). For nested layouts (`baseDir/proj/` with
51
- * its own tsconfig.json) the old baseDir-based check silently skipped every
52
- * type error (observed in ses_mst2r0r5: zero type feedback for the whole
53
- * session). Markers mirror the TypeScript LSP server config.
54
- */
55
- async runTypeCheckForFile(filePath) {
56
- const projectRoot = findProjectRoot(filePath, this.baseDir, ["tsconfig.json", "package.json"]);
57
- const tsconfigPath = join(projectRoot, "tsconfig.json");
58
- if (!existsSync(tsconfigPath)) {
59
- return { passed: true, message: "No tsconfig.json found — skipping type check" };
60
- }
61
- try {
62
- await this.runAsync("npx tsc --noEmit --skipLibCheck", projectRoot, 60_000);
63
- return { passed: true, message: "TypeScript type check passed" };
64
- }
65
- catch (e) {
66
- const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
67
- return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
68
- }
69
- }
70
- async runTests() {
71
- const pkgPath = resolve(this.baseDir, "package.json");
72
- if (!existsSync(pkgPath)) {
73
- return { passed: true, message: "No package.json found — skipping tests" };
74
- }
75
- try {
76
- const pkg = JSON.parse(require("fs").readFileSync(pkgPath, "utf-8"));
77
- if (!pkg.scripts?.test) {
78
- return { passed: true, message: "No test script defined — skipping tests" };
79
- }
80
- }
81
- catch {
82
- /* fall through — attempt to run */
83
- }
84
- return this.runScript("test");
85
- }
86
- async verifyArtifactFiles(files) {
87
- return Promise.all(files.map((f) => this.checkFileExists(f)));
88
- }
89
- async verifyMoEManifest(plan, config, allToolTags) {
90
- const errors = [];
91
- const warnings = [];
92
- const details = [];
93
- const expertErrors = validateExpertConfig(config, allToolTags);
94
- for (const err of expertErrors) {
95
- errors.push(err);
96
- details.push({ passed: false, message: err });
97
- }
98
- const tscResult = await this.runTypeCheck();
99
- details.push(tscResult);
100
- if (!tscResult.passed) {
101
- errors.push(tscResult.message);
102
- }
103
- for (const sub of plan.subtasks) {
104
- const fileChecks = await this.verifyArtifactFiles(sub.allowed_files || []);
105
- for (const check of fileChecks) {
106
- details.push(check);
107
- if (!check.passed) {
108
- errors.push(check.message);
109
- }
110
- }
111
- }
112
- return {
113
- success: errors.length === 0,
114
- errors,
115
- warnings,
116
- details,
117
- };
118
- }
119
- async verifyStep(stepDescription) {
120
- const fileMatches = extractFileLikeTokens(stripUrls(stepDescription));
121
- // No named files — this step cannot be verified by file existence. Do NOT
122
- // trivially pass (empty every() = true); signal noFiles so the tool guides
123
- // the model to run a real check instead.
124
- if (fileMatches.length === 0) {
125
- return { passed: false, syntaxValid: true, failed: [], noFiles: true };
126
- }
127
- const results = [];
128
- let syntaxValid = true;
129
- for (const filePath of fileMatches) {
130
- const resolved = findExistingFile(this.baseDir, filePath);
131
- if (!resolved) {
132
- results.push({ passed: false, message: t("verify.file_not_found", { path: filePath }) });
133
- continue;
134
- }
135
- results.push({ passed: true, message: t("verify.file_exists", { path: filePath }) });
136
- if (!(await this.validateSyntax(resolved))) {
137
- syntaxValid = false;
138
- results.push({ passed: false, message: t("verify.syntax_error", { path: filePath }) });
139
- }
140
- }
141
- return {
142
- passed: results.every((r) => r.passed),
143
- syntaxValid,
144
- failed: results.filter((r) => !r.passed),
145
- noFiles: false,
146
- };
147
- }
148
- async validateSyntax(filePath) {
149
- const ext = extname(filePath);
150
- if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
151
- try {
152
- await this.runAsync(`npx tsc --noEmit --skipLibCheck ${filePath}`, this.baseDir, 10000);
153
- return true;
154
- }
155
- catch (err) {
156
- if (err.status === 127 ||
157
- err.message.includes("not found") ||
158
- err.message.includes("ENOENT")) {
159
- return true;
160
- }
161
- const stderr = err.stderr?.toString() || "";
162
- if (stderr.includes("error TS") && !stderr.includes("Cannot find module")) {
163
- return false;
164
- }
165
- return true;
166
- }
167
- }
168
- if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
169
- try {
170
- await this.runAsync(`node --check ${filePath}`, this.baseDir, 5000);
171
- return true;
172
- }
173
- catch {
174
- return false;
175
- }
176
- }
177
- return true;
178
- }
179
- /**
180
- * Run a command asynchronously (non-blocking event loop) and resolve when it
181
- * exits. Rejects with the captured stderr/stdout on non-zero exit or timeout.
182
- */
183
- runAsync(command, cwd, timeoutMs) {
184
- return new Promise((resolve, reject) => {
185
- const child = spawn(command, {
186
- cwd,
187
- shell: true,
188
- windowsHide: true,
189
- stdio: ["ignore", "pipe", "pipe"],
190
- });
191
- let stdout = "";
192
- let stderr = "";
193
- child.stdout?.on("data", (d) => {
194
- stdout += d.toString();
195
- });
196
- child.stderr?.on("data", (d) => {
197
- stderr += d.toString();
198
- });
199
- const timer = setTimeout(() => {
200
- child.kill();
201
- reject(new Error(`Command timed out after ${timeoutMs}ms`));
202
- }, timeoutMs);
203
- child.on("error", (err) => {
204
- clearTimeout(timer);
205
- reject(err);
206
- });
207
- child.on("close", (code) => {
208
- clearTimeout(timer);
209
- if (code === 0) {
210
- resolve({ stdout, stderr });
211
- }
212
- else {
213
- const err = new Error(`Command failed with exit code ${code}`);
214
- err.stdout = stdout;
215
- err.stderr = stderr;
216
- err.status = code;
217
- reject(err);
218
- }
219
- });
220
- });
221
- }
222
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Unix commands that do not exist (or have incompatible semantics) in Windows
3
- * cmd.exe. The 9B model keeps reaching for grep/sed/ls/find via the bash tool;
4
- * each failure returns a hint, but the model retries.
5
- *
6
- * This is a session-level, module-side companion to the bash tool's own
7
- * per-command hard-stop (`UNIX_TO_WIN_HINTS` / `FAILING_FIRST_WORDS` /
8
- * `HARD_BLOCK_THRESHOLD` in src/tools/bash.ts). That mechanism injects
9
- * "STOP using X" INTO the tool output, which a 9B model tends to ignore; this
10
- * one, gated to win32 like bash.ts, queues a <system-summary> that lands
11
- * before the next model call. Keep the two command lists consistent.
12
- *
13
- * This is a command allow/deny list (like the security module's command
14
- * blacklist), NOT task classification (rule #10).
15
- */
16
- const FORBIDDEN_COMMANDS = new Set([
17
- "grep",
18
- "sed",
19
- "ls",
20
- "find",
21
- "rm",
22
- "touch",
23
- "which",
24
- "diff",
25
- "cp",
26
- "mv",
27
- ]);
28
- function firstWord(command) {
29
- const segs = command.trim().split(/[\s;|&]+/);
30
- return (segs[0] || "").replace(/[^\w-]/g, "").toLowerCase();
31
- }
32
- /** The forbidden command name if `command` starts with one (handles `cd x && …`). */
33
- export function forbiddenWindowsCommand(command) {
34
- const m = command.match(/^\s*cd\s+\S+\s*&&\s*(\S+)/);
35
- if (m)
36
- return forbiddenWindowsCommand(m[1]);
37
- const word = firstWord(command);
38
- if (word && FORBIDDEN_COMMANDS.has(word))
39
- return word;
40
- return null;
41
- }
@@ -1,66 +0,0 @@
1
- import { t } from "../../i18n/index";
2
- const MIN_CHARS = 1;
3
- const MIN_WORDS = 5;
4
- export class ConfidenceCheck {
5
- previousResponse = "";
6
- setPreviousResponse(response) {
7
- this.previousResponse = response;
8
- }
9
- validate(response) {
10
- if (!response || response.length < MIN_CHARS) {
11
- return {
12
- status: "retry",
13
- kind: "short",
14
- reason: t("hall.short_response"),
15
- };
16
- }
17
- // Language-agnostic: very short response with no structured content
18
- const wordCount = response.split(/\s+/).filter(Boolean).length;
19
- const hasStructure = /```|^\s*[-*]\s|^\s*\d+\.\s|<[^>]+>/m.test(response);
20
- if (wordCount < MIN_WORDS && !hasStructure) {
21
- return {
22
- status: "warn",
23
- kind: "short",
24
- reason: t("hall.short_response"),
25
- };
26
- }
27
- // Language-agnostic: repetition detection via word overlap
28
- if (this.previousResponse) {
29
- const overlap = this.calculateOverlap(response, this.previousResponse);
30
- if (overlap > 0.5) {
31
- return {
32
- status: "retry",
33
- kind: "repetition",
34
- reason: t("hall.repetitive", { pct: Math.round(overlap * 100) }),
35
- };
36
- }
37
- }
38
- // Language-agnostic: very low word diversity (same words repeated)
39
- const words = response
40
- .toLowerCase()
41
- .split(/\s+/)
42
- .filter((w) => w.length > 2);
43
- if (words.length >= 10) {
44
- const unique = new Set(words);
45
- const diversity = unique.size / words.length;
46
- if (diversity < 0.25) {
47
- return {
48
- status: "warn",
49
- kind: "diversity",
50
- reason: t("hall.repetitive", {
51
- pct: Math.round((1 - diversity) * 100),
52
- }),
53
- };
54
- }
55
- }
56
- return { status: "pass" };
57
- }
58
- calculateOverlap(a, b) {
59
- const wordsA = new Set(a.toLowerCase().split(/\s+/));
60
- const wordsB = b.toLowerCase().split(/\s+/);
61
- if (wordsB.length === 0)
62
- return 0;
63
- const matches = wordsB.filter((w) => wordsA.has(w));
64
- return matches.length / wordsB.length;
65
- }
66
- }
@@ -1,26 +0,0 @@
1
- export class ConsistencyCheck {
2
- decisions = [];
3
- createdFiles = new Set();
4
- deletedFiles = new Set();
5
- trackDecision(decision, location) {
6
- this.decisions.push({ decision, location });
7
- }
8
- trackCreatedFile(path) {
9
- this.createdFiles.add(path);
10
- }
11
- trackDeletedFile(path) {
12
- this.deletedFiles.add(path);
13
- }
14
- getCreatedFiles() {
15
- return Array.from(this.createdFiles);
16
- }
17
- getDeletedFiles() {
18
- return Array.from(this.deletedFiles);
19
- }
20
- getDecisions() {
21
- return this.decisions;
22
- }
23
- hasDecisions() {
24
- return this.decisions.length > 0;
25
- }
26
- }
@@ -1,43 +0,0 @@
1
- import { ConsistencyCheck } from "./consistency";
2
- import { ConfidenceCheck } from "./confidence";
3
- import { FactualCheck } from "./factual";
4
- import { LLMJudge } from "./llm-judge";
5
- export class HallucinationDetector {
6
- consistency;
7
- confidence;
8
- factual;
9
- judge;
10
- constructor(baseDir, llmProvider) {
11
- this.consistency = new ConsistencyCheck();
12
- this.confidence = new ConfidenceCheck();
13
- this.factual = baseDir ? new FactualCheck(baseDir) : null;
14
- this.judge = llmProvider ? new LLMJudge(llmProvider) : null;
15
- }
16
- getConsistencyCheck() {
17
- return this.consistency;
18
- }
19
- getConfidenceCheck() {
20
- return this.confidence;
21
- }
22
- async validate(response) {
23
- const confidenceResult = this.confidence.validate(response);
24
- if (confidenceResult.status === "retry" || confidenceResult.status === "block") {
25
- return confidenceResult;
26
- }
27
- const factualResult = this.factual?.validate(response);
28
- if (factualResult && factualResult.status !== "pass") {
29
- return factualResult;
30
- }
31
- // LLM-as-judge consistency: fast path (no decisions) skips any LLM call.
32
- const judgeResult = this.judge ? await this.judge.validate(response, this.consistency) : null;
33
- const warnings = [];
34
- if (judgeResult && judgeResult.status === "warn")
35
- warnings.push(judgeResult.reason || "");
36
- if (confidenceResult.status === "warn")
37
- warnings.push(confidenceResult.reason || "");
38
- if (warnings.length > 0) {
39
- return { status: "warn", reason: warnings.join("; ") };
40
- }
41
- return { status: "pass" };
42
- }
43
- }
@@ -1,129 +0,0 @@
1
- import { existsSync, readdirSync } from "fs";
2
- import { resolve, isAbsolute, join } from "path";
3
- import { t } from "../../i18n/index";
4
- import { extractFileLikeTokens, stripUrls } 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",
14
- ]);
15
- export class FactualCheck {
16
- baseDir;
17
- nameCache = null;
18
- nameCacheTime = 0;
19
- lastRescan = 0;
20
- static NAME_CACHE_TTL_MS = 5_000;
21
- static RESCAN_COOLDOWN_MS = 2_000;
22
- static MAX_INDEXED_FILES = 20_000;
23
- constructor(baseDir) {
24
- this.baseDir = baseDir;
25
- }
26
- validate(response) {
27
- const filePaths = this.extractFilePaths(response);
28
- if (filePaths.length === 0)
29
- return { status: "pass" };
30
- const nonExistent = [];
31
- for (const fp of filePaths) {
32
- if (!this.pathExists(fp)) {
33
- nonExistent.push(fp);
34
- }
35
- }
36
- if (nonExistent.length > 0) {
37
- return {
38
- status: "warn",
39
- reason: t("hall.unknown_paths", {
40
- paths: nonExistent.slice(0, 5).join(", "),
41
- }),
42
- };
43
- }
44
- return { status: "pass" };
45
- }
46
- /**
47
- * Bare filenames ("bikes.json") and relative paths ("src/data/bikes.json")
48
- * both land on the project tree. Absolute paths are checked as-is.
49
- */
50
- pathExists(fp) {
51
- if (isAbsolute(fp))
52
- return existsSync(fp);
53
- // "foo.json" has no directory part — treat it as a bare name that may
54
- // live anywhere under baseDir (e.g. src/data/bikes.json). Resolving it
55
- // against the workspace root alone produced false "unknown file" warnings.
56
- if (!fp.includes("/") && !fp.includes("\\")) {
57
- return this.bareNameExists(fp);
58
- }
59
- return existsSync(resolve(this.baseDir, fp));
60
- }
61
- bareNameExists(name) {
62
- if (existsSync(resolve(this.baseDir, name)))
63
- return true;
64
- if (this.indexHas(name))
65
- return true;
66
- // A write_file from this session may have created the file after the
67
- // index was built. Do a one-off rescan (rate-limited) before warning.
68
- const now = Date.now();
69
- if (now - this.lastRescan < FactualCheck.RESCAN_COOLDOWN_MS)
70
- return false;
71
- this.lastRescan = now;
72
- this.nameCache = null;
73
- this.nameCacheTime = 0;
74
- return this.indexHas(name);
75
- }
76
- indexHas(name) {
77
- return (this.getNameIndex().get(name)?.length ?? 0) > 0;
78
- }
79
- /**
80
- * Basename → paths index, rebuilt every NAME_CACHE_TTL_MS so newly created
81
- * files (write_file) are picked up without a full scan on every response.
82
- */
83
- getNameIndex() {
84
- const now = Date.now();
85
- if (this.nameCache && now - this.nameCacheTime < FactualCheck.NAME_CACHE_TTL_MS) {
86
- return this.nameCache;
87
- }
88
- const index = new Map();
89
- const scanned = this.scanDir(this.baseDir, index, 0);
90
- if (scanned > 0 || index.size > 0) {
91
- this.nameCache = index;
92
- this.nameCacheTime = now;
93
- }
94
- return index;
95
- }
96
- scanDir(dir, index, count) {
97
- if (count >= FactualCheck.MAX_INDEXED_FILES)
98
- return count;
99
- let entries;
100
- try {
101
- entries = readdirSync(dir, { withFileTypes: true });
102
- }
103
- catch {
104
- return count;
105
- }
106
- for (const entry of entries) {
107
- if (count >= FactualCheck.MAX_INDEXED_FILES)
108
- break;
109
- const full = join(dir, entry.name);
110
- if (entry.isDirectory()) {
111
- if (!IGNORED_DIRS.has(entry.name)) {
112
- count = this.scanDir(full, index, count);
113
- }
114
- }
115
- else if (entry.isFile()) {
116
- const list = index.get(entry.name);
117
- if (list)
118
- list.push(full);
119
- else
120
- index.set(entry.name, [full]);
121
- count++;
122
- }
123
- }
124
- return count;
125
- }
126
- extractFilePaths(text) {
127
- return extractFileLikeTokens(stripUrls(text));
128
- }
129
- }
@@ -1,5 +0,0 @@
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";