micro-models-agent 0.41.2 → 0.43.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 (171) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +19 -9
  3. package/dist/cli/completer.js +37 -36
  4. package/dist/cli/index.js +2 -2
  5. package/dist/cli/main.js +23 -48
  6. package/dist/cli/repl-commands.js +12 -40
  7. package/dist/cli/repl.js +87 -217
  8. package/dist/cli/security-commands.js +7 -5
  9. package/dist/cli/setup.js +26 -8
  10. package/dist/config/config.js +5 -52
  11. package/dist/config/defaults.js +5 -29
  12. package/dist/config/experts.js +1 -1
  13. package/dist/config/index.js +3 -3
  14. package/dist/config/security.js +10 -3
  15. package/dist/core/agent-moe.js +10 -2
  16. package/dist/core/agent.js +82 -273
  17. package/dist/core/bootstrap.js +13 -80
  18. package/dist/core/index.js +2 -2
  19. package/dist/core/prompt-builder.js +2 -23
  20. package/dist/core/session-logger.js +4 -46
  21. package/dist/i18n/en.json +2 -75
  22. package/dist/i18n/ru.json +1 -74
  23. package/dist/index.js +1 -1
  24. package/dist/llm/image-utils.js +5 -4
  25. package/dist/llm/index.js +4 -4
  26. package/dist/llm/model-loader.js +6 -6
  27. package/dist/llm/openai-compat.js +34 -40
  28. package/dist/llm/orchestrator.js +29 -33
  29. package/dist/llm/response.js +9 -9
  30. package/dist/logger/app-logger.js +1 -1
  31. package/dist/logger/index.js +1 -1
  32. package/dist/main.js +648 -183
  33. package/dist/migration/backup.js +13 -13
  34. package/dist/migration/detect.js +11 -11
  35. package/dist/migration/index.js +2 -2
  36. package/dist/modules/browser/actions.js +4 -34
  37. package/dist/modules/browser/bridge-server.mjs +202 -202
  38. package/dist/modules/browser/cookie-store.js +6 -6
  39. package/dist/modules/browser/index.js +5 -7
  40. package/dist/modules/browser/module.js +7 -8
  41. package/dist/modules/browser/session.js +84 -87
  42. package/dist/modules/browser/snapshot.js +58 -92
  43. package/dist/modules/browser/types.js +1 -4
  44. package/dist/modules/certification/cli.js +4 -2
  45. package/dist/modules/certification/fact-checker.js +3 -1
  46. package/dist/modules/certification/loader.js +9 -3
  47. package/dist/modules/certification/runner.js +4 -1
  48. package/dist/modules/context/index.js +1 -1
  49. package/dist/modules/context/manager.js +86 -160
  50. package/dist/modules/execution/auditor.js +25 -177
  51. package/dist/modules/execution/module.js +544 -201
  52. package/dist/modules/execution/moe-executor.js +0 -25
  53. package/dist/modules/execution/plan-store.js +3 -1
  54. package/dist/modules/execution/plan-validator.js +10 -10
  55. package/dist/modules/execution/planner.js +1 -6
  56. package/dist/modules/execution/stuck-detector.js +10 -173
  57. package/dist/modules/execution/verifier.js +42 -86
  58. package/dist/modules/hallucination/confidence.js +1 -8
  59. package/dist/modules/hallucination/detector.js +5 -2
  60. package/dist/modules/hallucination/factual.js +64 -3
  61. package/dist/modules/hallucination/index.js +1 -1
  62. package/dist/modules/hallucination/js-identifiers.js +0 -190
  63. package/dist/modules/hallucination/llm-judge.js +3 -1
  64. package/dist/modules/indexer/cache.js +7 -9
  65. package/dist/modules/indexer/index.js +3 -3
  66. package/dist/modules/indexer/module.js +42 -95
  67. package/dist/modules/indexer/walker.js +17 -17
  68. package/dist/modules/lsp/client.js +31 -74
  69. package/dist/modules/lsp/config.js +33 -87
  70. package/dist/modules/lsp/index.js +3 -3
  71. package/dist/modules/lsp/module.js +21 -185
  72. package/dist/modules/mcp/module.js +6 -2
  73. package/dist/modules/memory/index.js +1 -1
  74. package/dist/modules/memory/module.js +23 -71
  75. package/dist/modules/memory/search.js +9 -11
  76. package/dist/modules/memory/store.js +13 -13
  77. package/dist/modules/pipelines/engine.js +10 -10
  78. package/dist/modules/pipelines/index.js +3 -3
  79. package/dist/modules/pipelines/parser.js +14 -17
  80. package/dist/modules/pipelines/template.js +1 -1
  81. package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
  82. package/dist/modules/plugins/builtin/notify.js +2 -3
  83. package/dist/modules/plugins/index.js +1 -1
  84. package/dist/modules/plugins/loader.js +17 -59
  85. package/dist/modules/plugins/manager.js +17 -73
  86. package/dist/modules/processes/index.js +1 -1
  87. package/dist/modules/processes/registry.js +46 -135
  88. package/dist/modules/registry.js +2 -4
  89. package/dist/modules/security/audit-notifier.js +39 -39
  90. package/dist/modules/security/command-validator.js +8 -2
  91. package/dist/modules/security/data-sanitizer.js +9 -1
  92. package/dist/modules/security/encryption.js +56 -58
  93. package/dist/modules/security/network-validator.js +9 -1
  94. package/dist/modules/security/path-validator.js +3 -1
  95. package/dist/modules/security/security-policies.js +19 -3
  96. package/dist/modules/security/session-encryption.js +1 -1
  97. package/dist/modules/security/session-isolation.js +8 -8
  98. package/dist/modules/session/index.js +3 -3
  99. package/dist/modules/session/module.js +5 -5
  100. package/dist/modules/session/store.js +9 -3
  101. package/dist/modules/skills/module.js +2 -1
  102. package/dist/modules/updater/checker.js +6 -70
  103. package/dist/modules/updater/index.js +1 -2
  104. package/dist/modules/user-profile/compressor.js +2 -2
  105. package/dist/modules/user-profile/index.js +1 -1
  106. package/dist/modules/user-profile/profile.js +9 -9
  107. package/dist/tools/attach-image.js +1 -1
  108. package/dist/tools/bash.js +19 -178
  109. package/dist/tools/browser.js +29 -46
  110. package/dist/tools/executor.js +5 -4
  111. package/dist/tools/file-info.js +12 -13
  112. package/dist/tools/filter-tools.js +2 -9
  113. package/dist/tools/glob-tool.js +11 -11
  114. package/dist/tools/grep-tool.js +3 -1
  115. package/dist/tools/index.js +2 -13
  116. package/dist/tools/list-dir.js +17 -18
  117. package/dist/tools/load-skill.js +3 -1
  118. package/dist/tools/path-utils.js +4 -4
  119. package/dist/tools/pipeline-run.js +25 -25
  120. package/dist/tools/process-kill.js +11 -11
  121. package/dist/tools/process-list.js +22 -20
  122. package/dist/tools/process-log.js +18 -22
  123. package/dist/tools/question.js +3 -1
  124. package/dist/tools/read-file.js +2 -10
  125. package/dist/tools/recall.js +37 -44
  126. package/dist/tools/registry.js +4 -15
  127. package/dist/tools/remember.js +29 -29
  128. package/dist/tools/scope-check.js +9 -9
  129. package/dist/tools/subagent.js +9 -54
  130. package/dist/tools/user-input.js +1 -1
  131. package/dist/tools/web-browse.js +3 -3
  132. package/dist/tools/web-fetch.js +3 -3
  133. package/dist/tools/web-search.js +3 -3
  134. package/dist/tools/write-file.js +3 -1
  135. package/dist/ui/box.js +5 -1
  136. package/dist/ui/index.js +6 -6
  137. package/dist/ui/md-formatter.js +33 -33
  138. package/dist/ui/output.js +5 -5
  139. package/dist/ui/renderer.js +10 -15
  140. package/dist/ui/table.js +1 -1
  141. package/package.json +48 -48
  142. package/dist/cli/plugin-commands.js +0 -36
  143. package/dist/cli/run-result.js +0 -22
  144. package/dist/core/version.js +0 -24
  145. package/dist/modules/artifacts/store.js +0 -61
  146. package/dist/modules/browser/bridge-client.js +0 -199
  147. package/dist/modules/browser/bridge-path.js +0 -10
  148. package/dist/modules/browser/driver.js +0 -136
  149. package/dist/modules/context/chunk-query.js +0 -100
  150. package/dist/modules/context/fact-extractor.js +0 -162
  151. package/dist/modules/context/history.js +0 -15
  152. package/dist/modules/execution/audit-runners.js +0 -152
  153. package/dist/modules/execution/execution-plugin.js +0 -272
  154. package/dist/modules/execution/plan-tool.js +0 -508
  155. package/dist/modules/execution/windows-commands.js +0 -41
  156. package/dist/modules/indexer/project-profile.js +0 -183
  157. package/dist/modules/lsp/check-tool.js +0 -58
  158. package/dist/modules/lsp/command.js +0 -60
  159. package/dist/modules/lsp/probe.js +0 -76
  160. package/dist/modules/lsp/project-root.js +0 -32
  161. package/dist/modules/lsp/startup-check.js +0 -141
  162. package/dist/modules/processes/detect.js +0 -34
  163. package/dist/modules/skills/matcher.js +0 -27
  164. package/dist/modules/updater/module.js +0 -116
  165. package/dist/tools/chunk-query.js +0 -99
  166. package/dist/tools/download-file.js +0 -116
  167. package/dist/tools/enable-tools.js +0 -58
  168. package/dist/tools/hidden-tools-block.js +0 -37
  169. package/dist/ui/line-editor.js +0 -703
  170. package/dist/ui/line-math.js +0 -69
  171. package/dist/ui/plan-view.js +0 -103
@@ -1,272 +0,0 @@
1
- import { t } from "../../i18n/index";
2
- import { detectTestResults } from "../../tools/bash";
3
- import { forbiddenWindowsCommand } from "./windows-commands";
4
- import { platform } from "os";
5
- /**
6
- * Cooldown (in iterations) between stuck-recovery injections. Exported so the
7
- * ExecutionModule can seed its state's lastRecoveryIteration with the same
8
- * offset the plugin compares against.
9
- */
10
- export const STUCK_RECOVERY_COOLDOWN = 5;
11
- const MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3;
12
- const FORCE_SKIP_THRESHOLD = 10;
13
- /**
14
- * The execution plugin hooks, extracted verbatim from ExecutionModule.getPlugin().
15
- * All shared mutable state (pendingMessages, forbiddenBashFailures, state,
16
- * tracker) is owned by the module and mutated by reference through `deps`.
17
- */
18
- export function createExecutionPlugin(deps) {
19
- return {
20
- name: "execution",
21
- onBeforeThink: (ctx) => {
22
- // Flush deferred <system-summary> messages first so they are visible
23
- // to the next model call but never interleave tool messages.
24
- if (ctx.contextManager && deps.pendingMessages.length > 0) {
25
- for (const m of deps.pendingMessages.splice(0)) {
26
- ctx.contextManager.addMessage(m);
27
- }
28
- }
29
- if (deps.trackerRef.current?.isComplete()) {
30
- // All steps done/skipped — stop counting "no progress" against
31
- // the last step and silence step-stuck warnings for a finished
32
- // plan, BUT keep the tool-call history so a read-without-write
33
- // loop is still caught (observed: plan complete + false-positive
34
- // audit → the model re-read files for 50+ iterations with zero
35
- // writes and no recovery hint).
36
- deps.stuckDetector.resetStepProgress();
37
- deps.state.consecutivePlanWarnings = 0;
38
- deps.state.lastStepId = -1;
39
- deps.state.stuckNotified = false;
40
- }
41
- else {
42
- const step = deps.trackerRef.current?.getCurrentStep();
43
- if (deps.trackerRef.current && step) {
44
- if (step.id !== deps.state.lastStepId) {
45
- deps.state.consecutivePlanWarnings = 0;
46
- deps.state.lastStepId = step.id;
47
- deps.state.stuckNotified = false;
48
- }
49
- deps.stuckDetector.setCurrentStep(step.id, step.description);
50
- deps.stuckDetector.recordIteration(step.id);
51
- }
52
- else {
53
- deps.stuckDetector.reset();
54
- deps.state.consecutivePlanWarnings = 0;
55
- deps.state.lastStepId = -1;
56
- deps.state.stuckNotified = false;
57
- const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
58
- if (iter === 3 && !deps.trackerRef.current && ctx.contextManager) {
59
- ctx.contextManager.addMessage({
60
- role: "user",
61
- content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`,
62
- });
63
- }
64
- if (iter >= 6 && !deps.trackerRef.current && ctx.contextManager) {
65
- ctx.contextManager.addMessage({
66
- role: "user",
67
- content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`,
68
- });
69
- }
70
- }
71
- }
72
- const stuckReason = deps.stuckDetector.getStuckReason();
73
- if (stuckReason) {
74
- // Log once per stuck episode instead of spamming every iteration.
75
- const logIt = deps.stuckDetector.isStuck() ? !deps.state.stuckNotified : true;
76
- if (logIt) {
77
- ctx.logger?.warn(stuckReason);
78
- ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
79
- if (deps.stuckDetector.isStuck())
80
- deps.state.stuckNotified = true;
81
- }
82
- }
83
- if (deps.stuckDetector.isStuck() ||
84
- deps.stuckDetector.hasRepetitiveToolCalls() ||
85
- deps.stuckDetector.hasReadOnlyLoop()) {
86
- const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
87
- if (currentIter - deps.state.lastRecoveryIteration >= STUCK_RECOVERY_COOLDOWN) {
88
- const recovery = deps.stuckDetector.getRecoveryMessage();
89
- if (recovery && ctx.contextManager) {
90
- const lastError = deps.stuckDetector.getLastErrorOutput();
91
- const skillHint = lastError
92
- ? "\nIf you have relevant skills available, consider loading one with load_skill for expert guidance."
93
- : "";
94
- // Actionable hints based on actual error output
95
- const actionableHints = deps.stuckDetector.getActionableHints();
96
- const actionableHintStr = actionableHints.length > 0
97
- ? `\n${t("exec.hints", { hints: actionableHints.map((h) => `- ${h}`).join("\n") })}`
98
- : "";
99
- // Tool alternative suggestion
100
- const alternative = deps.stuckDetector.getToolAlternative();
101
- const altHint = alternative
102
- ? `\nTool "${deps.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}" instead.`
103
- : "";
104
- ctx.contextManager.addMessage({
105
- role: "user",
106
- content: `<system-summary>${recovery}${skillHint}${actionableHintStr}${altHint}</system-summary>`,
107
- });
108
- }
109
- const hints = deps.stuckDetector.getHints();
110
- if (hints.length > 0 && ctx.contextManager) {
111
- const hintMsg = t("exec.hints", {
112
- hints: hints.map((h) => `- ${h}`).join("\n"),
113
- });
114
- ctx.contextManager.addMessage({
115
- role: "user",
116
- content: `<system-summary>${hintMsg}</system-summary>`,
117
- });
118
- }
119
- deps.stuckDetector.recordEscalation();
120
- deps.state.lastRecoveryIteration = currentIter;
121
- if (deps.stuckDetector.shouldEscalate() && ctx.onMeta) {
122
- const escalation = t("exec.escalation", {
123
- stepId: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
124
- description: deps.trackerRef.current?.getCurrentStep()?.description ?? "",
125
- });
126
- ctx.onMeta(escalation);
127
- }
128
- // Force skip after too many iterations on the same step
129
- if (deps.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD &&
130
- ctx.contextManager) {
131
- const step = deps.trackerRef.current?.getCurrentStep();
132
- ctx.contextManager.addMessage({
133
- role: "user",
134
- content: `<system-summary>STOP. Step ${step?.id ?? "?"} ("${step?.description ?? ""}") took ${deps.stuckDetector.getIterationsOnCurrentStep()} iterations with no progress. DO NOT continue this step. Immediately call: plan update step=${step?.id ?? "?"} status=done (if code works despite warnings) OR plan update step=${step?.id ?? "?"} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.</system-summary>`,
135
- });
136
- }
137
- }
138
- }
139
- },
140
- onBeforeTool: (_ctx, call) => {
141
- const warning = deps.checkPlanAlignment(call);
142
- if (warning) {
143
- deps.pendingMessages.push({
144
- role: "user",
145
- content: `<system-summary>${warning}</system-summary>`,
146
- });
147
- deps.state.consecutivePlanWarnings++;
148
- if (deps.state.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
149
- deps.pendingMessages.push({
150
- role: "user",
151
- content: `<system-summary>${t("exec.plan_blocked", { step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`,
152
- });
153
- // Block with an explicit reason so the model sees WHY the call
154
- // was blocked (executor shows it via tool.blocked_reason).
155
- return t("exec.plan_blocked", {
156
- step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
157
- max: MAX_PLAN_WARNINGS_BEFORE_BLOCK,
158
- });
159
- }
160
- }
161
- else {
162
- deps.state.consecutivePlanWarnings = 0;
163
- }
164
- return true;
165
- },
166
- onToolCall: (ctx) => {
167
- const toolName = ctx?.toolName;
168
- const args = ctx?.args;
169
- if (toolName && args) {
170
- deps.stuckDetector.recordToolCall(toolName, args);
171
- }
172
- },
173
- onAfterTool: (ctx, call, result) => {
174
- // Remember every bash invocation (successful or not) so stuck
175
- // detection can reason about e.g. repeated runs with empty output.
176
- if (call.name === "bash") {
177
- deps.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
178
- }
179
- // A write/edit/bash that reports success but still carries a
180
- // type/syntax error in its output is NOT a success — the model must
181
- // fix the actual error, not keep rewriting the file blindly. Feed it
182
- // to the stuck detector so the rewrite-loop hint fires.
183
- const toolText = String(result.output ?? "");
184
- if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
185
- deps.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
186
- }
187
- if (!result.success) {
188
- // Hard stop for forbidden Windows commands: after the second
189
- // failure of the same forbidden command (grep/sed/ls/find/…),
190
- // inject a STOP message so the session stops burning bash calls
191
- // on commands cmd.exe does not have. Mirrors the bash tool's own
192
- // win32-gated hard-stop (src/tools/bash.ts) — on macOS/Linux
193
- // these commands exist and fail for legit reasons.
194
- if (call.name === "bash" && platform() === "win32") {
195
- const cmd = String(call.arguments?.command ?? "");
196
- const forbidden = forbiddenWindowsCommand(cmd);
197
- if (forbidden) {
198
- const n = (deps.forbiddenBashFailures.get(forbidden) || 0) + 1;
199
- deps.forbiddenBashFailures.set(forbidden, n);
200
- if (n === 2) {
201
- deps.pendingMessages.push({
202
- role: "user",
203
- content: `<system-summary>${t("exec.forbidden_cmd", { cmd: forbidden })}</system-summary>`,
204
- });
205
- }
206
- }
207
- }
208
- deps.stuckDetector.recordToolError(call.name, result.output);
209
- // Immediately queue an actionable hint on failure (don't wait for
210
- // the stuck threshold). Flushed in onBeforeThink so it does not
211
- // land between tool messages.
212
- const actionableHints = deps.stuckDetector.getActionableHints();
213
- const alternative = deps.stuckDetector.getToolAlternative();
214
- if (actionableHints.length > 0 || alternative) {
215
- const parts = [...actionableHints];
216
- if (alternative) {
217
- parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
218
- }
219
- deps.pendingMessages.push({
220
- role: "user",
221
- content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join("\n") })}</system-summary>`,
222
- });
223
- }
224
- }
225
- else {
226
- deps.stuckDetector.recordToolSuccess();
227
- // When a bash command runs code successfully, suggest marking the
228
- // step done — unless the output shows failing tests, which must
229
- // never be reported as a clean success.
230
- if (call.name === "bash" && result.success) {
231
- const cmd = String(call.arguments?.command ?? "");
232
- const testRun = detectTestResults(String(result.output ?? ""));
233
- if (testRun && testRun.failed > 0) {
234
- deps.pendingMessages.push({
235
- role: "user",
236
- content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`,
237
- });
238
- }
239
- else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
240
- deps.pendingMessages.push({
241
- role: "user",
242
- content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`,
243
- });
244
- }
245
- else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
246
- deps.pendingMessages.push({
247
- role: "user",
248
- content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`,
249
- });
250
- }
251
- }
252
- }
253
- if (result.success && (call.name === "write_file" || call.name === "edit_file")) {
254
- const filePath = call.arguments?.path;
255
- if (filePath) {
256
- deps.stuckDetector.recordFileRewrite(filePath);
257
- if (deps.stuckDetector.hasExcessiveRewrites()) {
258
- const file = deps.stuckDetector.getExcessiveRewriteFile();
259
- const count = deps.stuckDetector.getFileRewriteCount(file);
260
- if (ctx.onMeta) {
261
- ctx.onMeta(t("exec.file_rewrite_warning", {
262
- file: file,
263
- count: String(count),
264
- }));
265
- }
266
- }
267
- }
268
- deps.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
269
- }
270
- },
271
- };
272
- }