micro-models-agent 0.58.2 → 0.60.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 (233) hide show
  1. package/CHANGELOG.md +578 -556
  2. package/README.md +358 -358
  3. package/dist/certification/certifications.json +493 -493
  4. package/dist/i18n/en.json +732 -716
  5. package/dist/i18n/ru.json +732 -716
  6. package/dist/main.js +1243 -448
  7. package/dist/modules/browser/bridge-server.mjs +219 -219
  8. package/dist/skills/builtin/git.md +36 -36
  9. package/dist/skills/builtin/typescript.md +35 -35
  10. package/package.json +51 -51
  11. package/dist/cli/commands.js +0 -447
  12. package/dist/cli/completer.js +0 -167
  13. package/dist/cli/index.js +0 -2
  14. package/dist/cli/main.js +0 -153
  15. package/dist/cli/plugin-commands.js +0 -36
  16. package/dist/cli/repl-commands.js +0 -761
  17. package/dist/cli/repl.js +0 -702
  18. package/dist/cli/run-result.js +0 -33
  19. package/dist/cli/security-commands.js +0 -164
  20. package/dist/cli/setup.js +0 -237
  21. package/dist/config/config.js +0 -276
  22. package/dist/config/defaults.js +0 -141
  23. package/dist/config/domains.js +0 -179
  24. package/dist/config/experts.js +0 -15
  25. package/dist/config/index.js +0 -4
  26. package/dist/config/security.js +0 -213
  27. package/dist/config/types.js +0 -1
  28. package/dist/core/agent-moe.js +0 -102
  29. package/dist/core/agent.js +0 -1018
  30. package/dist/core/bootstrap.js +0 -481
  31. package/dist/core/crash-handler.js +0 -51
  32. package/dist/core/environment.js +0 -199
  33. package/dist/core/index.js +0 -2
  34. package/dist/core/prompt-builder.js +0 -76
  35. package/dist/core/session-logger.js +0 -251
  36. package/dist/core/types.js +0 -1
  37. package/dist/core/version.js +0 -26
  38. package/dist/core/workspace.js +0 -76
  39. package/dist/i18n/index.js +0 -46
  40. package/dist/index.js +0 -22
  41. package/dist/llm/image-utils.js +0 -143
  42. package/dist/llm/index.js +0 -4
  43. package/dist/llm/model-loader.js +0 -78
  44. package/dist/llm/openai-compat.js +0 -497
  45. package/dist/llm/orchestrator.js +0 -200
  46. package/dist/llm/provider.js +0 -10
  47. package/dist/llm/response.js +0 -39
  48. package/dist/llm/token-counter.js +0 -39
  49. package/dist/llm/types.js +0 -1
  50. package/dist/logger/app-logger.js +0 -189
  51. package/dist/logger/file-log.js +0 -151
  52. package/dist/logger/index.js +0 -1
  53. package/dist/migration/backup.js +0 -45
  54. package/dist/migration/detect.js +0 -50
  55. package/dist/migration/index.js +0 -2
  56. package/dist/modules/artifacts/store.js +0 -61
  57. package/dist/modules/browser/actions.js +0 -76
  58. package/dist/modules/browser/bridge-client.js +0 -199
  59. package/dist/modules/browser/bridge-path.js +0 -10
  60. package/dist/modules/browser/cookie-store.js +0 -24
  61. package/dist/modules/browser/driver.js +0 -136
  62. package/dist/modules/browser/index.js +0 -7
  63. package/dist/modules/browser/module.js +0 -29
  64. package/dist/modules/browser/session.js +0 -342
  65. package/dist/modules/browser/snapshot.js +0 -148
  66. package/dist/modules/browser/types.js +0 -12
  67. package/dist/modules/certification/cli.js +0 -213
  68. package/dist/modules/certification/fact-checker.js +0 -82
  69. package/dist/modules/certification/loader.js +0 -106
  70. package/dist/modules/certification/manifest.js +0 -58
  71. package/dist/modules/certification/runner.js +0 -245
  72. package/dist/modules/certification/scenarios.js +0 -407
  73. package/dist/modules/certification/types.js +0 -1
  74. package/dist/modules/context/chunk-query.js +0 -100
  75. package/dist/modules/context/fact-extractor.js +0 -168
  76. package/dist/modules/context/history.js +0 -15
  77. package/dist/modules/context/index.js +0 -1
  78. package/dist/modules/context/manager.js +0 -440
  79. package/dist/modules/execution/audit-runners.js +0 -206
  80. package/dist/modules/execution/auditor.js +0 -218
  81. package/dist/modules/execution/execution-plugin.js +0 -431
  82. package/dist/modules/execution/index.js +0 -8
  83. package/dist/modules/execution/module.js +0 -625
  84. package/dist/modules/execution/moe-executor.js +0 -304
  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 -196
  88. package/dist/modules/execution/plan-tool.js +0 -677
  89. package/dist/modules/execution/plan-validator.js +0 -153
  90. package/dist/modules/execution/planner.js +0 -94
  91. package/dist/modules/execution/stuck-detector.js +0 -746
  92. package/dist/modules/execution/tracker.js +0 -69
  93. package/dist/modules/execution/types.js +0 -1
  94. package/dist/modules/execution/verifier.js +0 -235
  95. package/dist/modules/execution/windows-commands.js +0 -41
  96. package/dist/modules/hallucination/confidence.js +0 -66
  97. package/dist/modules/hallucination/consistency.js +0 -26
  98. package/dist/modules/hallucination/detector.js +0 -47
  99. package/dist/modules/hallucination/factual.js +0 -169
  100. package/dist/modules/hallucination/index.js +0 -5
  101. package/dist/modules/hallucination/js-identifiers.js +0 -262
  102. package/dist/modules/hallucination/llm-judge.js +0 -101
  103. package/dist/modules/index.js +0 -5
  104. package/dist/modules/indexer/cache.js +0 -40
  105. package/dist/modules/indexer/index.js +0 -3
  106. package/dist/modules/indexer/module.js +0 -246
  107. package/dist/modules/indexer/project-profile.js +0 -183
  108. package/dist/modules/indexer/walker.js +0 -101
  109. package/dist/modules/lsp/check-tool.js +0 -58
  110. package/dist/modules/lsp/client.js +0 -389
  111. package/dist/modules/lsp/command.js +0 -60
  112. package/dist/modules/lsp/config.js +0 -135
  113. package/dist/modules/lsp/index.js +0 -3
  114. package/dist/modules/lsp/module.js +0 -260
  115. package/dist/modules/lsp/probe.js +0 -86
  116. package/dist/modules/lsp/project-root.js +0 -32
  117. package/dist/modules/lsp/startup-check.js +0 -144
  118. package/dist/modules/lsp/types.js +0 -1
  119. package/dist/modules/mcp/client.js +0 -399
  120. package/dist/modules/mcp/index.js +0 -3
  121. package/dist/modules/mcp/module.js +0 -142
  122. package/dist/modules/mcp/registry.js +0 -15
  123. package/dist/modules/memory/index.js +0 -1
  124. package/dist/modules/memory/module.js +0 -96
  125. package/dist/modules/memory/search.js +0 -42
  126. package/dist/modules/memory/store.js +0 -69
  127. package/dist/modules/pipelines/engine.js +0 -60
  128. package/dist/modules/pipelines/index.js +0 -3
  129. package/dist/modules/pipelines/parser.js +0 -56
  130. package/dist/modules/pipelines/template.js +0 -14
  131. package/dist/modules/plugins/builtin/lint-on-write.js +0 -334
  132. package/dist/modules/plugins/builtin/notify.js +0 -9
  133. package/dist/modules/plugins/index.js +0 -1
  134. package/dist/modules/plugins/loader.js +0 -70
  135. package/dist/modules/plugins/manager.js +0 -261
  136. package/dist/modules/plugins/types.js +0 -1
  137. package/dist/modules/pricing/index.js +0 -61
  138. package/dist/modules/pricing/prices.js +0 -129
  139. package/dist/modules/processes/detect.js +0 -34
  140. package/dist/modules/processes/index.js +0 -2
  141. package/dist/modules/processes/registry.js +0 -327
  142. package/dist/modules/processes/runner.js +0 -23
  143. package/dist/modules/providers/create.js +0 -22
  144. package/dist/modules/providers/fallback.js +0 -79
  145. package/dist/modules/providers/health.js +0 -46
  146. package/dist/modules/providers/index.js +0 -5
  147. package/dist/modules/providers/manager.js +0 -161
  148. package/dist/modules/providers/presets.js +0 -128
  149. package/dist/modules/providers/registry.js +0 -22
  150. package/dist/modules/providers/types.js +0 -1
  151. package/dist/modules/registry.js +0 -48
  152. package/dist/modules/security/audit-log.js +0 -136
  153. package/dist/modules/security/audit-notifier.js +0 -292
  154. package/dist/modules/security/command-validator.js +0 -219
  155. package/dist/modules/security/content-scanner.js +0 -53
  156. package/dist/modules/security/data-sanitizer.js +0 -89
  157. package/dist/modules/security/encryption.js +0 -242
  158. package/dist/modules/security/index.js +0 -14
  159. package/dist/modules/security/network-validator.js +0 -88
  160. package/dist/modules/security/path-validator.js +0 -203
  161. package/dist/modules/security/rate-limiter.js +0 -119
  162. package/dist/modules/security/security-policies.js +0 -531
  163. package/dist/modules/security/session-encryption.js +0 -210
  164. package/dist/modules/security/session-isolation.js +0 -95
  165. package/dist/modules/session/index.js +0 -3
  166. package/dist/modules/session/manager.js +0 -172
  167. package/dist/modules/session/module.js +0 -24
  168. package/dist/modules/session/store.js +0 -222
  169. package/dist/modules/session/types.js +0 -1
  170. package/dist/modules/skills/index.js +0 -2
  171. package/dist/modules/skills/loader.js +0 -72
  172. package/dist/modules/skills/matcher.js +0 -27
  173. package/dist/modules/skills/module.js +0 -129
  174. package/dist/modules/types.js +0 -1
  175. package/dist/modules/updater/checker.js +0 -96
  176. package/dist/modules/updater/index.js +0 -2
  177. package/dist/modules/updater/module.js +0 -116
  178. package/dist/modules/user-profile/compressor.js +0 -16
  179. package/dist/modules/user-profile/index.js +0 -1
  180. package/dist/modules/user-profile/profile.js +0 -68
  181. package/dist/tools/approve.js +0 -33
  182. package/dist/tools/attach-image.js +0 -101
  183. package/dist/tools/bash.js +0 -519
  184. package/dist/tools/browser.js +0 -115
  185. package/dist/tools/chunk-query.js +0 -100
  186. package/dist/tools/create-dir.js +0 -56
  187. package/dist/tools/delete-file.js +0 -63
  188. package/dist/tools/download-file.js +0 -117
  189. package/dist/tools/edit-file.js +0 -80
  190. package/dist/tools/enable-tools.js +0 -59
  191. package/dist/tools/executor.js +0 -154
  192. package/dist/tools/file-info.js +0 -47
  193. package/dist/tools/filter-tools.js +0 -17
  194. package/dist/tools/glob-tool.js +0 -27
  195. package/dist/tools/grep-tool.js +0 -125
  196. package/dist/tools/hidden-tools-block.js +0 -37
  197. package/dist/tools/index.js +0 -78
  198. package/dist/tools/list-dir.js +0 -49
  199. package/dist/tools/load-skill.js +0 -43
  200. package/dist/tools/mcp-call.js +0 -69
  201. package/dist/tools/move-file.js +0 -86
  202. package/dist/tools/path-utils.js +0 -101
  203. package/dist/tools/pipeline-run.js +0 -145
  204. package/dist/tools/preview.js +0 -2
  205. package/dist/tools/process-kill.js +0 -40
  206. package/dist/tools/process-list.js +0 -37
  207. package/dist/tools/process-log.js +0 -54
  208. package/dist/tools/question.js +0 -141
  209. package/dist/tools/read-file.js +0 -179
  210. package/dist/tools/recall.js +0 -118
  211. package/dist/tools/registry.js +0 -47
  212. package/dist/tools/remember.js +0 -68
  213. package/dist/tools/scope-check.js +0 -32
  214. package/dist/tools/search-history.js +0 -85
  215. package/dist/tools/subagent.js +0 -196
  216. package/dist/tools/types.js +0 -1
  217. package/dist/tools/user-input.js +0 -123
  218. package/dist/tools/web-browse.js +0 -87
  219. package/dist/tools/web-fetch.js +0 -119
  220. package/dist/tools/web-search.js +0 -105
  221. package/dist/tools/write-file.js +0 -82
  222. package/dist/ui/box.js +0 -77
  223. package/dist/ui/colors.js +0 -4
  224. package/dist/ui/diff.js +0 -178
  225. package/dist/ui/index.js +0 -6
  226. package/dist/ui/line-editor.js +0 -822
  227. package/dist/ui/line-math.js +0 -73
  228. package/dist/ui/md-formatter.js +0 -212
  229. package/dist/ui/output.js +0 -13
  230. package/dist/ui/plan-view.js +0 -103
  231. package/dist/ui/renderer.js +0 -259
  232. package/dist/ui/spinner.js +0 -70
  233. 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";