micro-models-agent 0.55.0 → 0.56.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 (232) hide show
  1. package/README.md +358 -358
  2. package/dist/certification/certifications.json +493 -493
  3. package/dist/main.js +2301 -1213
  4. package/dist/modules/browser/bridge-server.mjs +219 -202
  5. package/dist/skills/builtin/git.md +36 -36
  6. package/dist/skills/builtin/typescript.md +35 -35
  7. package/package.json +1 -1
  8. package/dist/cli/commands.js +0 -447
  9. package/dist/cli/completer.js +0 -167
  10. package/dist/cli/index.js +0 -2
  11. package/dist/cli/main.js +0 -153
  12. package/dist/cli/plugin-commands.js +0 -36
  13. package/dist/cli/repl-commands.js +0 -761
  14. package/dist/cli/repl.js +0 -702
  15. package/dist/cli/run-result.js +0 -33
  16. package/dist/cli/security-commands.js +0 -164
  17. package/dist/cli/setup.js +0 -237
  18. package/dist/config/config.js +0 -276
  19. package/dist/config/defaults.js +0 -141
  20. package/dist/config/domains.js +0 -179
  21. package/dist/config/experts.js +0 -15
  22. package/dist/config/index.js +0 -4
  23. package/dist/config/security.js +0 -213
  24. package/dist/config/types.js +0 -1
  25. package/dist/core/agent-moe.js +0 -102
  26. package/dist/core/agent.js +0 -1018
  27. package/dist/core/bootstrap.js +0 -481
  28. package/dist/core/crash-handler.js +0 -51
  29. package/dist/core/environment.js +0 -199
  30. package/dist/core/index.js +0 -2
  31. package/dist/core/prompt-builder.js +0 -76
  32. package/dist/core/session-logger.js +0 -251
  33. package/dist/core/types.js +0 -1
  34. package/dist/core/version.js +0 -26
  35. package/dist/core/workspace.js +0 -76
  36. package/dist/i18n/en.json +0 -679
  37. package/dist/i18n/index.js +0 -46
  38. package/dist/i18n/ru.json +0 -679
  39. package/dist/index.js +0 -22
  40. package/dist/llm/image-utils.js +0 -143
  41. package/dist/llm/index.js +0 -4
  42. package/dist/llm/model-loader.js +0 -78
  43. package/dist/llm/openai-compat.js +0 -497
  44. package/dist/llm/orchestrator.js +0 -200
  45. package/dist/llm/provider.js +0 -10
  46. package/dist/llm/response.js +0 -39
  47. package/dist/llm/token-counter.js +0 -39
  48. package/dist/llm/types.js +0 -1
  49. package/dist/logger/app-logger.js +0 -189
  50. package/dist/logger/file-log.js +0 -151
  51. package/dist/logger/index.js +0 -1
  52. package/dist/migration/backup.js +0 -45
  53. package/dist/migration/detect.js +0 -50
  54. package/dist/migration/index.js +0 -2
  55. package/dist/modules/artifacts/store.js +0 -61
  56. package/dist/modules/browser/actions.js +0 -76
  57. package/dist/modules/browser/bridge-client.js +0 -199
  58. package/dist/modules/browser/bridge-path.js +0 -10
  59. package/dist/modules/browser/cookie-store.js +0 -24
  60. package/dist/modules/browser/driver.js +0 -136
  61. package/dist/modules/browser/index.js +0 -7
  62. package/dist/modules/browser/module.js +0 -29
  63. package/dist/modules/browser/session.js +0 -342
  64. package/dist/modules/browser/snapshot.js +0 -148
  65. package/dist/modules/browser/types.js +0 -12
  66. package/dist/modules/certification/cli.js +0 -213
  67. package/dist/modules/certification/fact-checker.js +0 -82
  68. package/dist/modules/certification/loader.js +0 -106
  69. package/dist/modules/certification/manifest.js +0 -58
  70. package/dist/modules/certification/runner.js +0 -245
  71. package/dist/modules/certification/scenarios.js +0 -407
  72. package/dist/modules/certification/types.js +0 -1
  73. package/dist/modules/context/chunk-query.js +0 -100
  74. package/dist/modules/context/fact-extractor.js +0 -168
  75. package/dist/modules/context/history.js +0 -15
  76. package/dist/modules/context/index.js +0 -1
  77. package/dist/modules/context/manager.js +0 -440
  78. package/dist/modules/execution/audit-runners.js +0 -206
  79. package/dist/modules/execution/auditor.js +0 -218
  80. package/dist/modules/execution/execution-plugin.js +0 -431
  81. package/dist/modules/execution/index.js +0 -8
  82. package/dist/modules/execution/module.js +0 -625
  83. package/dist/modules/execution/moe-executor.js +0 -304
  84. package/dist/modules/execution/plan-coverage.js +0 -68
  85. package/dist/modules/execution/plan-persister.js +0 -46
  86. package/dist/modules/execution/plan-store.js +0 -196
  87. package/dist/modules/execution/plan-tool.js +0 -677
  88. package/dist/modules/execution/plan-validator.js +0 -153
  89. package/dist/modules/execution/planner.js +0 -94
  90. package/dist/modules/execution/stuck-detector.js +0 -746
  91. package/dist/modules/execution/tracker.js +0 -69
  92. package/dist/modules/execution/types.js +0 -1
  93. package/dist/modules/execution/verifier.js +0 -235
  94. package/dist/modules/execution/windows-commands.js +0 -41
  95. package/dist/modules/hallucination/confidence.js +0 -66
  96. package/dist/modules/hallucination/consistency.js +0 -26
  97. package/dist/modules/hallucination/detector.js +0 -47
  98. package/dist/modules/hallucination/factual.js +0 -169
  99. package/dist/modules/hallucination/index.js +0 -5
  100. package/dist/modules/hallucination/js-identifiers.js +0 -262
  101. package/dist/modules/hallucination/llm-judge.js +0 -101
  102. package/dist/modules/index.js +0 -5
  103. package/dist/modules/indexer/cache.js +0 -40
  104. package/dist/modules/indexer/index.js +0 -3
  105. package/dist/modules/indexer/module.js +0 -246
  106. package/dist/modules/indexer/project-profile.js +0 -183
  107. package/dist/modules/indexer/walker.js +0 -101
  108. package/dist/modules/lsp/check-tool.js +0 -58
  109. package/dist/modules/lsp/client.js +0 -389
  110. package/dist/modules/lsp/command.js +0 -60
  111. package/dist/modules/lsp/config.js +0 -135
  112. package/dist/modules/lsp/index.js +0 -3
  113. package/dist/modules/lsp/module.js +0 -260
  114. package/dist/modules/lsp/probe.js +0 -86
  115. package/dist/modules/lsp/project-root.js +0 -32
  116. package/dist/modules/lsp/startup-check.js +0 -144
  117. package/dist/modules/lsp/types.js +0 -1
  118. package/dist/modules/mcp/client.js +0 -399
  119. package/dist/modules/mcp/index.js +0 -3
  120. package/dist/modules/mcp/module.js +0 -142
  121. package/dist/modules/mcp/registry.js +0 -15
  122. package/dist/modules/memory/index.js +0 -1
  123. package/dist/modules/memory/module.js +0 -96
  124. package/dist/modules/memory/search.js +0 -42
  125. package/dist/modules/memory/store.js +0 -69
  126. package/dist/modules/pipelines/engine.js +0 -60
  127. package/dist/modules/pipelines/index.js +0 -3
  128. package/dist/modules/pipelines/parser.js +0 -56
  129. package/dist/modules/pipelines/template.js +0 -14
  130. package/dist/modules/plugins/builtin/lint-on-write.js +0 -334
  131. package/dist/modules/plugins/builtin/notify.js +0 -9
  132. package/dist/modules/plugins/index.js +0 -1
  133. package/dist/modules/plugins/loader.js +0 -70
  134. package/dist/modules/plugins/manager.js +0 -261
  135. package/dist/modules/plugins/types.js +0 -1
  136. package/dist/modules/pricing/index.js +0 -61
  137. package/dist/modules/pricing/prices.js +0 -129
  138. package/dist/modules/processes/detect.js +0 -34
  139. package/dist/modules/processes/index.js +0 -2
  140. package/dist/modules/processes/registry.js +0 -327
  141. package/dist/modules/processes/runner.js +0 -23
  142. package/dist/modules/providers/create.js +0 -22
  143. package/dist/modules/providers/fallback.js +0 -79
  144. package/dist/modules/providers/health.js +0 -46
  145. package/dist/modules/providers/index.js +0 -5
  146. package/dist/modules/providers/manager.js +0 -161
  147. package/dist/modules/providers/presets.js +0 -128
  148. package/dist/modules/providers/registry.js +0 -22
  149. package/dist/modules/providers/types.js +0 -1
  150. package/dist/modules/registry.js +0 -48
  151. package/dist/modules/security/audit-log.js +0 -136
  152. package/dist/modules/security/audit-notifier.js +0 -292
  153. package/dist/modules/security/command-validator.js +0 -219
  154. package/dist/modules/security/content-scanner.js +0 -53
  155. package/dist/modules/security/data-sanitizer.js +0 -89
  156. package/dist/modules/security/encryption.js +0 -242
  157. package/dist/modules/security/index.js +0 -14
  158. package/dist/modules/security/network-validator.js +0 -88
  159. package/dist/modules/security/path-validator.js +0 -203
  160. package/dist/modules/security/rate-limiter.js +0 -119
  161. package/dist/modules/security/security-policies.js +0 -531
  162. package/dist/modules/security/session-encryption.js +0 -210
  163. package/dist/modules/security/session-isolation.js +0 -95
  164. package/dist/modules/session/index.js +0 -3
  165. package/dist/modules/session/manager.js +0 -172
  166. package/dist/modules/session/module.js +0 -24
  167. package/dist/modules/session/store.js +0 -222
  168. package/dist/modules/session/types.js +0 -1
  169. package/dist/modules/skills/index.js +0 -2
  170. package/dist/modules/skills/loader.js +0 -72
  171. package/dist/modules/skills/matcher.js +0 -27
  172. package/dist/modules/skills/module.js +0 -129
  173. package/dist/modules/types.js +0 -1
  174. package/dist/modules/updater/checker.js +0 -96
  175. package/dist/modules/updater/index.js +0 -2
  176. package/dist/modules/updater/module.js +0 -116
  177. package/dist/modules/user-profile/compressor.js +0 -16
  178. package/dist/modules/user-profile/index.js +0 -1
  179. package/dist/modules/user-profile/profile.js +0 -68
  180. package/dist/tools/approve.js +0 -33
  181. package/dist/tools/attach-image.js +0 -101
  182. package/dist/tools/bash.js +0 -519
  183. package/dist/tools/browser.js +0 -115
  184. package/dist/tools/chunk-query.js +0 -100
  185. package/dist/tools/create-dir.js +0 -56
  186. package/dist/tools/delete-file.js +0 -63
  187. package/dist/tools/download-file.js +0 -117
  188. package/dist/tools/edit-file.js +0 -80
  189. package/dist/tools/enable-tools.js +0 -59
  190. package/dist/tools/executor.js +0 -154
  191. package/dist/tools/file-info.js +0 -47
  192. package/dist/tools/filter-tools.js +0 -17
  193. package/dist/tools/glob-tool.js +0 -27
  194. package/dist/tools/grep-tool.js +0 -125
  195. package/dist/tools/hidden-tools-block.js +0 -37
  196. package/dist/tools/index.js +0 -78
  197. package/dist/tools/list-dir.js +0 -49
  198. package/dist/tools/load-skill.js +0 -43
  199. package/dist/tools/mcp-call.js +0 -69
  200. package/dist/tools/move-file.js +0 -86
  201. package/dist/tools/path-utils.js +0 -101
  202. package/dist/tools/pipeline-run.js +0 -145
  203. package/dist/tools/preview.js +0 -2
  204. package/dist/tools/process-kill.js +0 -40
  205. package/dist/tools/process-list.js +0 -37
  206. package/dist/tools/process-log.js +0 -54
  207. package/dist/tools/question.js +0 -141
  208. package/dist/tools/read-file.js +0 -179
  209. package/dist/tools/recall.js +0 -118
  210. package/dist/tools/registry.js +0 -47
  211. package/dist/tools/remember.js +0 -68
  212. package/dist/tools/scope-check.js +0 -32
  213. package/dist/tools/search-history.js +0 -85
  214. package/dist/tools/subagent.js +0 -196
  215. package/dist/tools/types.js +0 -1
  216. package/dist/tools/user-input.js +0 -123
  217. package/dist/tools/web-browse.js +0 -87
  218. package/dist/tools/web-fetch.js +0 -119
  219. package/dist/tools/web-search.js +0 -105
  220. package/dist/tools/write-file.js +0 -82
  221. package/dist/ui/box.js +0 -77
  222. package/dist/ui/colors.js +0 -4
  223. package/dist/ui/diff.js +0 -178
  224. package/dist/ui/index.js +0 -6
  225. package/dist/ui/line-editor.js +0 -822
  226. package/dist/ui/line-math.js +0 -73
  227. package/dist/ui/md-formatter.js +0 -212
  228. package/dist/ui/output.js +0 -13
  229. package/dist/ui/plan-view.js +0 -103
  230. package/dist/ui/renderer.js +0 -259
  231. package/dist/ui/spinner.js +0 -70
  232. package/dist/ui/table.js +0 -144
@@ -1,69 +0,0 @@
1
- import { PlanCreator, getPlanProgress } from "./planner";
2
- export class PlanTracker {
3
- plan;
4
- currentStepIndex = 0;
5
- constructor(plan) {
6
- this.plan = plan;
7
- }
8
- getPlan() {
9
- return this.plan;
10
- }
11
- getCurrentStepIndex() {
12
- return this.currentStepIndex;
13
- }
14
- getCurrentStep() {
15
- return this.plan.steps[this.currentStepIndex];
16
- }
17
- getStep(id) {
18
- return this.plan.steps.find((s) => s.id === id);
19
- }
20
- updateStepStatus(id, status) {
21
- const step = this.getStep(id);
22
- if (step)
23
- step.status = status;
24
- }
25
- addNote(id, note) {
26
- const step = this.getStep(id);
27
- if (step)
28
- step.note = note;
29
- }
30
- advance() {
31
- if (this.currentStepIndex < this.plan.steps.length - 1) {
32
- this.currentStepIndex++;
33
- this.plan.steps[this.currentStepIndex].status = "in_progress";
34
- return true;
35
- }
36
- return false;
37
- }
38
- /**
39
- * Advance the pointer past any done/skipped steps without mutating their
40
- * status. Keeps currentStepIndex in sync when the model marks steps done via
41
- * the `plan update` tool (which never calls advance()). Without this,
42
- * getCurrentStep() stays on the first step forever and the stuck detector
43
- * accumulates false-positive "no progress" warnings.
44
- */
45
- syncCurrentStep() {
46
- while (this.currentStepIndex < this.plan.steps.length - 1 &&
47
- (this.plan.steps[this.currentStepIndex].status === "done" ||
48
- this.plan.steps[this.currentStepIndex].status === "skipped")) {
49
- this.currentStepIndex++;
50
- }
51
- }
52
- isComplete() {
53
- return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
54
- }
55
- getProgressString() {
56
- const p = getPlanProgress(this.plan);
57
- const total = p.total;
58
- const settled = p.settled;
59
- const pct = total > 0 ? Math.round((settled / total) * 100) : 0;
60
- const barWidth = 10;
61
- const filled = Math.round((settled / total) * barWidth);
62
- const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
63
- const skippedPart = p.skipped > 0 ? ` (${p.skipped} skipped)` : "";
64
- return `[${this.plan.id}] ${this.plan.title} ${settled}/${total}${skippedPart} ${bar} ${pct}%`;
65
- }
66
- toPromptBlock() {
67
- return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
68
- }
69
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,235 +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, kind) {
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
- const exists = !!resolved;
132
- if (kind === "delete") {
133
- // For delete steps: file NOT existing is a pass
134
- results.push({
135
- passed: !exists,
136
- message: exists
137
- ? t("verify.file_still_exists", { path: filePath })
138
- : t("verify.file_not_found", { path: filePath }),
139
- });
140
- // Don't syntax-check a deleted file
141
- continue;
142
- }
143
- // Create steps: file must exist + valid syntax
144
- if (!exists) {
145
- results.push({ passed: false, message: t("verify.file_not_found", { path: filePath }) });
146
- continue;
147
- }
148
- results.push({ passed: true, message: t("verify.file_exists", { path: filePath }) });
149
- if (!(await this.validateSyntax(resolved))) {
150
- syntaxValid = false;
151
- results.push({ passed: false, message: t("verify.syntax_error", { path: filePath }) });
152
- }
153
- }
154
- return {
155
- passed: results.every((r) => r.passed),
156
- syntaxValid,
157
- failed: results.filter((r) => !r.passed),
158
- noFiles: false,
159
- };
160
- }
161
- async validateSyntax(filePath) {
162
- const ext = extname(filePath);
163
- if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
164
- try {
165
- await this.runAsync(`npx tsc --noEmit --skipLibCheck ${filePath}`, this.baseDir, 10000);
166
- return true;
167
- }
168
- catch (err) {
169
- if (err.status === 127 ||
170
- err.message.includes("not found") ||
171
- err.message.includes("ENOENT")) {
172
- return true;
173
- }
174
- const stderr = err.stderr?.toString() || "";
175
- if (stderr.includes("error TS") && !stderr.includes("Cannot find module")) {
176
- return false;
177
- }
178
- return true;
179
- }
180
- }
181
- if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
182
- try {
183
- await this.runAsync(`node --check ${filePath}`, this.baseDir, 5000);
184
- return true;
185
- }
186
- catch {
187
- return false;
188
- }
189
- }
190
- return true;
191
- }
192
- /**
193
- * Run a command asynchronously (non-blocking event loop) and resolve when it
194
- * exits. Rejects with the captured stderr/stdout on non-zero exit or timeout.
195
- */
196
- runAsync(command, cwd, timeoutMs) {
197
- return new Promise((resolve, reject) => {
198
- const child = spawn(command, {
199
- cwd,
200
- shell: true,
201
- windowsHide: true,
202
- stdio: ["ignore", "pipe", "pipe"],
203
- });
204
- let stdout = "";
205
- let stderr = "";
206
- child.stdout?.on("data", (d) => {
207
- stdout += d.toString();
208
- });
209
- child.stderr?.on("data", (d) => {
210
- stderr += d.toString();
211
- });
212
- const timer = setTimeout(() => {
213
- child.kill();
214
- reject(new Error(`Command timed out after ${timeoutMs}ms`));
215
- }, timeoutMs);
216
- child.on("error", (err) => {
217
- clearTimeout(timer);
218
- reject(err);
219
- });
220
- child.on("close", (code) => {
221
- clearTimeout(timer);
222
- if (code === 0) {
223
- resolve({ stdout, stderr });
224
- }
225
- else {
226
- const err = new Error(`Command failed with exit code ${code}`);
227
- err.stdout = stdout;
228
- err.stderr = stderr;
229
- err.status = code;
230
- reject(err);
231
- }
232
- });
233
- });
234
- }
235
- }
@@ -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,47 +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
- /** Add files known from compaction summaries to the factual checker. */
23
- addKnownFiles(files) {
24
- this.factual?.addKnownFiles(files);
25
- }
26
- async validate(response) {
27
- const confidenceResult = this.confidence.validate(response);
28
- if (confidenceResult.status === "retry" || confidenceResult.status === "block") {
29
- return confidenceResult;
30
- }
31
- const factualResult = this.factual?.validate(response);
32
- if (factualResult && factualResult.status !== "pass") {
33
- return factualResult;
34
- }
35
- // LLM-as-judge consistency: fast path (no decisions) skips any LLM call.
36
- const judgeResult = this.judge ? await this.judge.validate(response, this.consistency) : null;
37
- const warnings = [];
38
- if (judgeResult && judgeResult.status === "warn")
39
- warnings.push(judgeResult.reason || "");
40
- if (confidenceResult.status === "warn")
41
- warnings.push(confidenceResult.reason || "");
42
- if (warnings.length > 0) {
43
- return { status: "warn", reason: warnings.join("; ") };
44
- }
45
- return { status: "pass" };
46
- }
47
- }
@@ -1,169 +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
- /** Files known from compaction summaries — survive context resets. */
24
- knownFiles = new Set();
25
- constructor(baseDir) {
26
- this.baseDir = baseDir;
27
- }
28
- /** Add file paths known from compaction summaries. These are checked
29
- * before the filesystem scan so bare filenames from compacted turns
30
- * are recognized even when the directory is in IGNORED_DIRS. */
31
- addKnownFiles(files) {
32
- for (const f of files)
33
- this.knownFiles.add(f);
34
- }
35
- validate(response) {
36
- const filePaths = this.extractFilePaths(response);
37
- if (filePaths.length === 0)
38
- return { status: "pass" };
39
- const nonExistent = [];
40
- for (const fp of filePaths) {
41
- if (!this.pathExists(fp)) {
42
- nonExistent.push(fp);
43
- }
44
- }
45
- if (nonExistent.length > 0) {
46
- return {
47
- status: "warn",
48
- reason: t("hall.unknown_paths", {
49
- paths: nonExistent.slice(0, 5).join(", "),
50
- }),
51
- };
52
- }
53
- return { status: "pass" };
54
- }
55
- /**
56
- * Bare filenames ("bikes.json") and relative paths ("src/data/bikes.json")
57
- * both land on the project tree. Absolute paths are checked as-is.
58
- */
59
- pathExists(fp) {
60
- if (isAbsolute(fp))
61
- return existsSync(fp);
62
- // Check known files from compaction summaries first
63
- if (this.knownFiles.has(fp))
64
- return true;
65
- for (const cand of this.dotfileVariants(fp)) {
66
- if (this.knownFiles.has(cand))
67
- return true;
68
- if (existsSync(resolve(this.baseDir, cand)))
69
- return true;
70
- }
71
- // "foo.json" has no directory part — treat it as a bare name that may
72
- // live anywhere under baseDir (e.g. src/data/bikes.json). Resolving it
73
- // against the workspace root alone produced false "unknown file" warnings.
74
- if (!fp.includes("/") && !fp.includes("\\")) {
75
- return this.bareNameExists(fp);
76
- }
77
- return false;
78
- }
79
- /**
80
- * The token regex cannot anchor a leading dot (`\b` never matches between a
81
- * non-word char and `.`), so dotfiles (.prettierrc.json, .env, …) are
82
- * extracted as "prettierrc.json". Try the dot-prefixed basename too before
83
- * declaring the file missing. "src/config.json" also yields
84
- * "src/.config.json".
85
- */
86
- dotfileVariants(fp) {
87
- const idx = Math.max(fp.lastIndexOf("/"), fp.lastIndexOf("\\"));
88
- const base = idx >= 0 ? fp.slice(idx + 1) : fp;
89
- if (base.startsWith("."))
90
- return [fp];
91
- return idx >= 0 ? [fp, `${fp.slice(0, idx + 1)}.${base}`] : [fp, `.${fp}`];
92
- }
93
- bareNameExists(name) {
94
- for (const cand of this.dotfileVariants(name)) {
95
- if (existsSync(resolve(this.baseDir, cand)))
96
- return true;
97
- if (this.indexHas(cand))
98
- return true;
99
- // Check known files — a bare name may match a full path from compaction
100
- for (const known of this.knownFiles) {
101
- if (known.endsWith(`/${cand}`) || known.endsWith(`\\${cand}`) || known === cand) {
102
- return true;
103
- }
104
- }
105
- }
106
- // A write_file from this session may have created the file after the
107
- // index was built. Do a one-off rescan (rate-limited) before warning.
108
- const now = Date.now();
109
- if (now - this.lastRescan < FactualCheck.RESCAN_COOLDOWN_MS)
110
- return false;
111
- this.lastRescan = now;
112
- this.nameCache = null;
113
- this.nameCacheTime = 0;
114
- return this.indexHas(name);
115
- }
116
- indexHas(name) {
117
- return (this.getNameIndex().get(name)?.length ?? 0) > 0;
118
- }
119
- /**
120
- * Basename → paths index, rebuilt every NAME_CACHE_TTL_MS so newly created
121
- * files (write_file) are picked up without a full scan on every response.
122
- */
123
- getNameIndex() {
124
- const now = Date.now();
125
- if (this.nameCache && now - this.nameCacheTime < FactualCheck.NAME_CACHE_TTL_MS) {
126
- return this.nameCache;
127
- }
128
- const index = new Map();
129
- const scanned = this.scanDir(this.baseDir, index, 0);
130
- if (scanned > 0 || index.size > 0) {
131
- this.nameCache = index;
132
- this.nameCacheTime = now;
133
- }
134
- return index;
135
- }
136
- scanDir(dir, index, count) {
137
- if (count >= FactualCheck.MAX_INDEXED_FILES)
138
- return count;
139
- let entries;
140
- try {
141
- entries = readdirSync(dir, { withFileTypes: true });
142
- }
143
- catch {
144
- return count;
145
- }
146
- for (const entry of entries) {
147
- if (count >= FactualCheck.MAX_INDEXED_FILES)
148
- break;
149
- const full = join(dir, entry.name);
150
- if (entry.isDirectory()) {
151
- if (!IGNORED_DIRS.has(entry.name)) {
152
- count = this.scanDir(full, index, count);
153
- }
154
- }
155
- else if (entry.isFile()) {
156
- const list = index.get(entry.name);
157
- if (list)
158
- list.push(full);
159
- else
160
- index.set(entry.name, [full]);
161
- count++;
162
- }
163
- }
164
- return count;
165
- }
166
- extractFilePaths(text) {
167
- return extractFileLikeTokens(stripUrls(text));
168
- }
169
- }
@@ -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";