micro-models-agent 0.28.17 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/cli/commands.js +3 -116
  2. package/dist/cli/main.js +8 -35
  3. package/dist/cli/repl.js +611 -110
  4. package/dist/cli/setup.js +12 -32
  5. package/dist/config/config.js +30 -46
  6. package/dist/config/defaults.js +1 -10
  7. package/dist/config/security.js +8 -15
  8. package/dist/core/agent-moe.js +12 -24
  9. package/dist/core/agent.js +47 -281
  10. package/dist/core/bootstrap.js +36 -52
  11. package/dist/core/session-logger.js +2 -35
  12. package/dist/i18n/en.json +15 -79
  13. package/dist/i18n/index.js +9 -12
  14. package/dist/i18n/ru.json +15 -79
  15. package/dist/index.js +13 -13
  16. package/dist/llm/openai-compat.js +10 -39
  17. package/dist/logger/app-logger.js +16 -83
  18. package/dist/main.js +624 -243
  19. package/dist/modules/browser/session.js +60 -108
  20. package/dist/modules/context/history.js +15 -0
  21. package/dist/modules/context/manager.js +10 -119
  22. package/dist/modules/execution/auditor.js +39 -33
  23. package/dist/modules/execution/index.js +6 -8
  24. package/dist/modules/execution/module.js +32 -474
  25. package/dist/modules/execution/moe-executor.js +40 -97
  26. package/dist/modules/execution/planner.js +13 -63
  27. package/dist/modules/execution/stuck-detector.js +39 -252
  28. package/dist/modules/execution/tracker.js +7 -21
  29. package/dist/modules/execution/verifier.js +17 -46
  30. package/dist/modules/hallucination/confidence.js +2 -7
  31. package/dist/modules/hallucination/consistency.js +42 -8
  32. package/dist/modules/hallucination/detector.js +21 -26
  33. package/dist/modules/hallucination/factual.js +150 -170
  34. package/dist/modules/hallucination/index.js +4 -5
  35. package/dist/modules/index.js +5 -5
  36. package/dist/modules/mcp/client.js +2 -8
  37. package/dist/modules/memory/store.js +0 -4
  38. package/dist/modules/plugins/builtin/lint-on-write.js +38 -143
  39. package/dist/modules/processes/detect.js +34 -0
  40. package/dist/modules/processes/index.js +2 -1
  41. package/dist/modules/processes/registry.js +35 -125
  42. package/dist/modules/processes/runner.js +110 -9
  43. package/dist/modules/security/audit-log.js +10 -30
  44. package/dist/modules/security/command-validator.js +16 -42
  45. package/dist/modules/security/content-scanner.js +8 -9
  46. package/dist/modules/security/network-validator.js +2 -2
  47. package/dist/modules/security/path-validator.js +10 -64
  48. package/dist/modules/security/security-policies.js +67 -221
  49. package/dist/modules/security/session-encryption.js +25 -42
  50. package/dist/modules/session/manager.js +10 -15
  51. package/dist/modules/session/store.js +8 -62
  52. package/dist/modules/skills/index.js +3 -2
  53. package/dist/modules/skills/matcher.js +27 -0
  54. package/dist/modules/skills/module.js +23 -10
  55. package/dist/tools/bash.js +90 -287
  56. package/dist/tools/create-dir.js +1 -0
  57. package/dist/tools/delete-file.js +1 -0
  58. package/dist/tools/edit-file.js +8 -10
  59. package/dist/tools/executor.js +7 -57
  60. package/dist/tools/grep-tool.js +29 -51
  61. package/dist/tools/index.js +40 -55
  62. package/dist/tools/load-skill.js +18 -14
  63. package/dist/tools/move-file.js +2 -3
  64. package/dist/tools/pipeline-run.js +1 -1
  65. package/dist/tools/read-file.js +5 -15
  66. package/dist/tools/search-history.js +22 -42
  67. package/dist/tools/subagent.js +12 -21
  68. package/dist/tools/web-browse.js +25 -54
  69. package/dist/tools/web-fetch.js +34 -60
  70. package/dist/tools/web-search.js +20 -39
  71. package/dist/tools/write-file.js +10 -13
  72. package/dist/ui/diff.js +16 -9
  73. package/dist/ui/renderer.js +6 -69
  74. package/package.json +1 -1
  75. package/dist/cli/repl-commands.js +0 -633
  76. package/dist/core/workspace.js +0 -76
  77. package/dist/logger/file-log.js +0 -151
  78. package/dist/modules/certification/cli.js +0 -176
  79. package/dist/modules/certification/fact-checker.js +0 -84
  80. package/dist/modules/certification/loader.js +0 -111
  81. package/dist/modules/certification/manifest.js +0 -50
  82. package/dist/modules/certification/runner.js +0 -162
  83. package/dist/modules/certification/scenarios.js +0 -124
  84. package/dist/modules/certification/types.js +0 -1
  85. package/dist/modules/execution/plan-coverage.js +0 -68
  86. package/dist/modules/execution/plan-persister.js +0 -46
  87. package/dist/modules/execution/plan-store.js +0 -159
  88. package/dist/modules/hallucination/js-identifiers.js +0 -72
  89. package/dist/modules/hallucination/llm-judge.js +0 -103
  90. package/dist/modules/lsp/client.js +0 -235
  91. package/dist/modules/lsp/config.js +0 -81
  92. package/dist/modules/lsp/index.js +0 -3
  93. package/dist/modules/lsp/module.js +0 -68
  94. package/dist/modules/lsp/types.js +0 -1
@@ -1,38 +1,28 @@
1
- import { getExpertConfig } from "../../config/experts";
2
- import { filterToolsByTags } from "../../tools/filter-tools";
3
- import { StuckDetector } from "./stuck-detector";
4
- import { t } from "../../i18n/index";
1
+ import { getExpertConfig } from '../../config/experts';
2
+ import { filterToolsByTags } from '../../tools/filter-tools';
5
3
  const TRANSIENT_ERROR_PATTERNS = [
6
- "timeout",
7
- "Timeout",
8
- "TIMEOUT",
9
- "5xx",
10
- "500",
11
- "502",
12
- "503",
13
- "network",
14
- "Network",
15
- "ECONNREFUSED",
16
- "ECONNRESET",
17
- "fetch failed",
18
- "abort",
19
- "Abort",
4
+ 'timeout', 'Timeout', 'TIMEOUT',
5
+ '5xx', '500', '502', '503',
6
+ 'network', 'Network',
7
+ 'ECONNREFUSED', 'ECONNRESET',
8
+ 'fetch failed',
9
+ 'abort', 'Abort',
20
10
  ];
21
11
  function isTransientError(error) {
22
- return TRANSIENT_ERROR_PATTERNS.some((p) => error.includes(p));
12
+ return TRANSIENT_ERROR_PATTERNS.some(p => error.includes(p));
23
13
  }
24
14
  function sleep(ms) {
25
- return new Promise((r) => setTimeout(r, ms));
15
+ return new Promise(r => setTimeout(r, ms));
26
16
  }
27
17
  export function topologicalSort(subtasks) {
28
18
  const sorted = [];
29
- const remaining = new Set(subtasks.map((s) => s.id));
30
- const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
19
+ const remaining = new Set(subtasks.map(s => s.id));
20
+ const subtaskMap = new Map(subtasks.map(s => [s.id, s]));
31
21
  while (remaining.size > 0) {
32
22
  const ready = [];
33
23
  for (const id of remaining) {
34
24
  const sub = subtaskMap.get(id);
35
- const depsSatisfied = (sub.depends_on || []).every((d) => !remaining.has(d));
25
+ const depsSatisfied = (sub.depends_on || []).every(d => !remaining.has(d));
36
26
  if (depsSatisfied) {
37
27
  ready.push(sub);
38
28
  }
@@ -55,19 +45,19 @@ async function executeSubtask(subtask, deps, _sharedContext) {
55
45
  subtaskId: subtask.id,
56
46
  success: false,
57
47
  summary: `Unknown expert_tag: ${subtask.expert_tag}`,
58
- result: "",
48
+ result: '',
59
49
  error: `No expert config found for tag "${subtask.expert_tag}"`,
60
50
  durationMs: Date.now() - startTime,
61
51
  };
62
52
  }
63
- const subagentTool = deps.toolRegistry.get("subagent");
53
+ const subagentTool = deps.toolRegistry.get('subagent');
64
54
  if (!subagentTool) {
65
55
  return {
66
56
  subtaskId: subtask.id,
67
57
  success: false,
68
- summary: "Subagent tool not registered",
69
- result: "",
70
- error: "Subagent tool not found in registry",
58
+ summary: 'Subagent tool not registered',
59
+ result: '',
60
+ error: 'Subagent tool not found in registry',
71
61
  durationMs: Date.now() - startTime,
72
62
  };
73
63
  }
@@ -77,25 +67,23 @@ async function executeSubtask(subtask, deps, _sharedContext) {
77
67
  return {
78
68
  subtaskId: subtask.id,
79
69
  success: false,
80
- summary: `No tools available for expert "${subtask.expert_tag}" (tags: ${expertConfig.tool_tags.join(", ")})`,
81
- result: "",
82
- error: "No matching tools",
70
+ summary: `No tools available for expert "${subtask.expert_tag}" (tags: ${expertConfig.tool_tags.join(', ')})`,
71
+ result: '',
72
+ error: 'No matching tools',
83
73
  durationMs: Date.now() - startTime,
84
74
  };
85
75
  }
86
76
  const maxAttempts = expertConfig.max_attempts || 3;
87
- let lastError = "";
77
+ let lastError = '';
88
78
  let lastResult = null;
89
- const stuckDetector = new StuckDetector(maxAttempts * 2, maxAttempts);
90
79
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
91
- stuckDetector.recordIteration(1);
92
80
  try {
93
81
  const allowedFiles = subtask.allowed_files || [];
94
82
  const readOnlyFiles = subtask.read_only_files || [];
95
83
  const scopePrompt = allowedFiles.length > 0 || readOnlyFiles.length > 0
96
- ? `\n\nAllowed files to write: ${allowedFiles.join(", ") || "(none)"}\nRead-only files: ${readOnlyFiles.join(", ") || "(none)"}`
97
- : "";
98
- const taskPrompt = `${subtask.description}\n\nExpected output: ${subtask.expected_output}\nSuccess criteria:\n${(subtask.success_criteria || []).map((c) => `- ${c}`).join("\n")}${scopePrompt}`;
84
+ ? `\n\nAllowed files to write: ${allowedFiles.join(', ') || '(none)'}\nRead-only files: ${readOnlyFiles.join(', ') || '(none)'}`
85
+ : '';
86
+ const taskPrompt = `${subtask.description}\n\nExpected output: ${subtask.expected_output}\nSuccess criteria:\n${(subtask.success_criteria || []).map((c) => `- ${c}`).join('\n')}${scopePrompt}`;
99
87
  const subagentArgs = {
100
88
  task: taskPrompt,
101
89
  allowed_files: allowedFiles,
@@ -120,83 +108,45 @@ async function executeSubtask(subtask, deps, _sharedContext) {
120
108
  }
121
109
  lastError = result.output;
122
110
  lastResult = result;
123
- stuckDetector.recordToolError("subagent", result.output);
124
- // Inject actionable hints into the next retry's prompt
125
- if (attempt < maxAttempts) {
126
- const hints = stuckDetector.getActionableHints();
127
- const recovery = stuckDetector.getRecoveryMessage();
128
- if (hints.length > 0) {
129
- deps.logger.debug(`Hint for subtask "${subtask.id}" retry: ${hints.join("; ")}`);
130
- }
131
- if (recovery) {
132
- deps.logger.debug(`Recovery for subtask "${subtask.id}": ${recovery}`);
133
- }
134
- }
135
111
  if (isTransientError(lastError) && attempt < maxAttempts) {
136
112
  const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);
137
113
  deps.logger.debug(`Retrying subtask "${subtask.id}" (attempt ${attempt}/${maxAttempts}) after ${delay}ms: transient error`);
138
114
  await sleep(delay);
139
115
  continue;
140
116
  }
141
- // Non-transient failure — build detailed error with hints
142
- const hints = stuckDetector.getActionableHints();
143
- let errorDetail = result.output;
144
- if (hints.length > 0) {
145
- errorDetail += `\n\n${t("exec.hints", { hints: hints.map((h) => `- ${h}`).join("\n") })}`;
146
- }
147
- if (lastError) {
148
- errorDetail +=
149
- "\n\nIf relevant skills are available, consider loading one with load_skill for expert guidance.";
150
- }
151
117
  return {
152
118
  subtaskId: subtask.id,
153
119
  success: false,
154
120
  summary: `Failed: ${subtask.id}`,
155
121
  result: result.output,
156
- error: errorDetail,
122
+ error: result.output,
157
123
  durationMs: Date.now() - startTime,
158
124
  };
159
125
  }
160
126
  catch (e) {
161
127
  lastError = e.message;
162
- stuckDetector.recordToolError("subagent", e.message);
163
128
  if (isTransientError(lastError) && attempt < maxAttempts) {
164
129
  const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);
165
130
  deps.logger.debug(`Retrying subtask "${subtask.id}" (attempt ${attempt}/${maxAttempts}) after ${delay}ms: ${lastError}`);
166
131
  await sleep(delay);
167
132
  continue;
168
133
  }
169
- const hints = stuckDetector.getActionableHints();
170
- let errorDetail = e.message;
171
- if (hints.length > 0) {
172
- errorDetail += `\n\n${t("exec.hints", { hints: hints.map((h) => `- ${h}`).join("\n") })}`;
173
- }
174
- if (lastError) {
175
- errorDetail +=
176
- "\n\nIf relevant skills are available, consider loading one with load_skill for expert guidance.";
177
- }
178
134
  return {
179
135
  subtaskId: subtask.id,
180
136
  success: false,
181
137
  summary: `Error: ${subtask.id}`,
182
- result: "",
183
- error: errorDetail,
138
+ result: '',
139
+ error: e.message,
184
140
  durationMs: Date.now() - startTime,
185
141
  };
186
142
  }
187
143
  }
188
- // Exhausted retries — build detailed failure
189
- const stuckReason = stuckDetector.getStuckReason();
190
- let finalError = lastError;
191
- if (stuckReason) {
192
- finalError += `\n${stuckReason}`;
193
- }
194
144
  return {
195
145
  subtaskId: subtask.id,
196
146
  success: false,
197
147
  summary: `Failed after ${maxAttempts} attempts: ${subtask.id}`,
198
- result: "",
199
- error: finalError,
148
+ result: '',
149
+ error: lastError,
200
150
  durationMs: Date.now() - startTime,
201
151
  };
202
152
  }
@@ -211,41 +161,34 @@ export class MoEExecutor {
211
161
  const warnings = [];
212
162
  const waves = topologicalSort(plan.subtasks);
213
163
  if (waves.length === 0) {
214
- return {
215
- success: false,
216
- results: [],
217
- errors: ["Failed to topologically sort subtasks (possible cycle)"],
218
- warnings: [],
219
- };
164
+ return { success: false, results: [], errors: ['Failed to topologically sort subtasks (possible cycle)'], warnings: [] };
220
165
  }
221
166
  // Verify all subtasks are included — catch silent drops from unresolved dependencies
222
167
  const sortedCount = waves.flat().length;
223
168
  if (sortedCount < plan.subtasks.length) {
224
169
  const missing = plan.subtasks
225
- .filter((s) => !waves.flat().some((w) => w.id === s.id))
226
- .map((s) => s.id);
170
+ .filter(s => !waves.flat().some(w => w.id === s.id))
171
+ .map(s => s.id);
227
172
  return {
228
173
  success: false,
229
174
  results: [],
230
- errors: [
231
- `Missing subtasks after topological sort: ${missing.join(", ")} (dangling or invalid depends_on)`,
232
- ],
175
+ errors: [`Missing subtasks after topological sort: ${missing.join(', ')} (dangling or invalid depends_on)`],
233
176
  warnings: [],
234
177
  };
235
178
  }
236
179
  for (let waveIdx = 0; waveIdx < waves.length; waveIdx++) {
237
180
  const wave = waves[waveIdx];
238
- const wavePromises = wave.map((subtask) => executeSubtask(subtask, this.deps, plan.shared_context)
239
- .then((result) => {
181
+ const wavePromises = wave.map(subtask => executeSubtask(subtask, this.deps, plan.shared_context)
182
+ .then(result => {
240
183
  results.push(result);
241
184
  return result;
242
185
  })
243
- .catch((e) => {
186
+ .catch(e => {
244
187
  const errResult = {
245
188
  subtaskId: subtask.id,
246
189
  success: false,
247
190
  summary: `Unhandled error: ${subtask.id}`,
248
- result: "",
191
+ result: '',
249
192
  error: e.message,
250
193
  durationMs: 0,
251
194
  };
@@ -254,9 +197,9 @@ export class MoEExecutor {
254
197
  }));
255
198
  await Promise.all(wavePromises);
256
199
  }
257
- const failed = results.filter((r) => !r.success);
200
+ const failed = results.filter(r => !r.success);
258
201
  if (failed.length > 0) {
259
- errors.push(...failed.map((f) => `[${f.subtaskId}] ${f.error || f.summary}`));
202
+ errors.push(...failed.map(f => `[${f.subtaskId}] ${f.error || f.summary}`));
260
203
  }
261
204
  return { success: failed.length === 0, results, errors, warnings };
262
205
  }
@@ -1,85 +1,35 @@
1
- function generatePlanId() {
2
- const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
3
- let id = "";
4
- for (let i = 0; i < 6; i++) {
5
- id += chars[Math.floor(Math.random() * chars.length)];
6
- }
7
- return `plan_${id}`;
8
- }
9
1
  export class PlanCreator {
10
2
  static isMultiStep(task) {
11
3
  const fileCount = (task.match(/\b[\w./-]+\.[a-z]+\b/gi) || []).length;
12
4
  if (fileCount > 1)
13
5
  return true;
14
- const actionWords = [
15
- "implement",
16
- "create",
17
- "add",
18
- "build",
19
- "setup",
20
- "configure",
21
- "write",
22
- "make",
23
- "develop",
24
- ];
6
+ const actionWords = ['implement', 'create', 'add', 'build', 'setup', 'configure', 'write', 'make', 'develop'];
25
7
  const words = task.split(/\s+/);
26
- const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
8
+ const hasActionWord = actionWords.some(w => task.toLowerCase().includes(w));
27
9
  return hasActionWord && words.length > 8;
28
10
  }
29
- static createPlan(title, stepDescriptions, baseDir) {
30
- const stepCount = stepDescriptions.length;
11
+ static createPlan(title, stepDescriptions) {
31
12
  return {
32
- id: generatePlanId(),
33
- title: `[${stepCount}] ${title}`,
13
+ title,
34
14
  steps: stepDescriptions.map((desc, i) => ({
35
15
  id: i + 1,
36
16
  description: desc,
37
- status: "pending",
17
+ status: 'pending',
38
18
  })),
39
19
  createdAt: new Date().toISOString(),
40
- baseDir,
41
- };
42
- }
43
- static replan(plan, newSteps, title) {
44
- const kept = plan.steps.filter((s) => s.status === "done" || s.status === "skipped");
45
- const baseTitle = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
46
- const newTitle = title || baseTitle;
47
- const totalSteps = kept.length + newSteps.length;
48
- const added = newSteps.map((desc, i) => ({
49
- id: kept.length + i + 1,
50
- description: desc,
51
- status: "pending",
52
- }));
53
- return {
54
- id: plan.id,
55
- title: `[${totalSteps}] ${newTitle}`,
56
- steps: [...kept, ...added],
57
- createdAt: plan.createdAt,
58
- baseDir: plan.baseDir,
59
- name: plan.name,
60
20
  };
61
21
  }
62
22
  static toPromptBlock(plan, currentStepIndex) {
63
- const date = plan.createdAt.slice(0, 10);
64
- const lines = [
65
- `[${plan.id}] ${plan.title}`,
66
- `Dir: ${plan.baseDir}`,
67
- `Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
68
- ``,
69
- ];
23
+ const lines = [`[Plan: ${plan.title}] (steps: ${plan.steps.filter(s => s.status === 'done').length}/${plan.steps.length} done, current: step ${currentStepIndex + 1})`];
70
24
  for (const step of plan.steps) {
71
- const icon = step.status === "done"
72
- ? "[x]"
73
- : step.status === "in_progress"
74
- ? "[*]"
75
- : step.status === "failed"
76
- ? "[!]"
77
- : step.status === "skipped"
78
- ? "[-]"
79
- : "[ ]";
80
- const note = step.note ? ` — ${step.note}` : "";
25
+ const icon = step.status === 'done' ? '[x]' :
26
+ step.status === 'in_progress' ? '[*]' :
27
+ step.status === 'failed' ? '[!]' :
28
+ step.status === 'skipped' ? '[-]' :
29
+ '[ ]';
30
+ const note = step.note ? ` — ${step.note}` : '';
81
31
  lines.push(`${icon} ${step.id}. ${step.description}${note}`);
82
32
  }
83
- return lines.join("\n");
33
+ return lines.join('\n');
84
34
  }
85
35
  }