micro-models-agent 0.28.17 → 0.29.1

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 +625 -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
@@ -15,9 +15,8 @@ import { HallucinationDetector } from "../modules/hallucination/detector";
15
15
  import { ExecutionModule } from "../modules/execution/module";
16
16
  import { SessionStore, SessionManager, SessionModule, } from "../modules/session/index";
17
17
  import { UserProfile } from "../modules/user-profile/profile";
18
- import { SkillsLoader, SkillsModule } from "../modules/skills/index";
18
+ import { SkillsLoader, SkillsMatcher, SkillsModule, } from "../modules/skills/index";
19
19
  import { BrowserModule } from "../modules/browser/index";
20
- import { LspModule } from "../modules/lsp/index";
21
20
  import { IndexerModule } from "../modules/indexer/index";
22
21
  import { MCPModule } from "../modules/mcp/index";
23
22
  import { MemoryModule } from "../modules/memory/module";
@@ -26,30 +25,36 @@ import { Agent } from "./agent";
26
25
  import { homedir } from "os";
27
26
  import { join, resolve } from "path";
28
27
  import { existsSync, readFileSync, writeFileSync } from "fs";
29
- export function buildSystemInfo(config, baseDir, profileCompressed) {
28
+ function buildSystemInfo(config, baseDir, profileCompressed) {
30
29
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
31
30
  const isWin = profileCompressed.toLowerCase().includes("win32");
32
31
  const lines = [
33
- `You are MMA v2, an AI coding agent for small models (${config.model}). Date: ${now}. Workspace: ${baseDir}. ${profileCompressed}.`,
34
- `Reply in the user's language. Use tools for file ops (read/write/edit/delete), search (glob/grep), shell (bash), web, subagents, browser, MCP. Explain briefly if not obvious. On tool failure: analyze, fix the call, retry up to 2x with different approaches, then ask the user.`,
35
- `Design: YAGNI (no unneeded code), KISS (simple over clever), DRY (reuse existing utilities).`,
32
+ `You are MMA v2, an AI coding agent for small models (${config.model}).`,
33
+ `Date: ${now}`,
34
+ `Workspace: ${baseDir}`,
35
+ `${profileCompressed}`,
36
+ `Reply in the user's language. Use tools for filesystem, bash, web access.`,
37
+ `When you need to act, call a tool immediately. Do not describe your plans in text — use tools to perform operations.`,
38
+ `After every tool call, check whether the user's request is fully satisfied. If any files, commands, or checks are still missing, call the next needed tool right away. Do not stop with an empty or "done" response until the task is complete.`,
39
+ `If you create a directory, continue creating the files that belong inside it. A created folder alone is not a completed task.`,
40
+ `If a tool call fails (e.g., a skill is too large), try an alternative approach or continue without it. Do not reply with empty text when the task is unfinished.`,
41
+ ``,
42
+ `Design principles — apply them automatically without naming them:`,
43
+ `- Do not add code, files, or abstractions that are not needed right now (YAGNI — You Ain't Gonna Need It). If something is not required by the current task, omit it.`,
44
+ `- Prefer simple, straightforward solutions over clever or complex ones (KISS — Keep It Simple, Stupid). Flat code beats nested, minimal config beats flexible, explicit beats generic.`,
45
+ `- Do not duplicate code, logic, or configuration (DRY — Don't Repeat Yourself). Extract shared logic into a single place, reuse existing utilities and patterns before writing new ones.`,
36
46
  ];
37
47
  if (isWin) {
38
- lines.push(`Windows (PowerShell): use list_dir/read_file/delete_file/create_dir tools instead of dir/type/del/mkdir. No PowerShell cmdlets (Get-Content, Select-Object, Write-Output), no head/tail/grep/cat. Use forward slashes in paths. CWD: ${baseDir}`);
48
+ lines.push(``, `Windows environment use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
39
49
  }
40
- // MMA is a Bun-only project (package.json scripts, Bun API in code) — the
41
- // model should prefer bun over node/tsx/ts-node for running TypeScript
42
- // files and tests (observed: model wasted 10+ iterations installing tsx
43
- // instead of running `bun file.ts`).
44
- lines.push(`Runtime: Bun is available — run TypeScript directly with "bun <file.ts>" and tests with "bun test" (no tsx/ts-node/npm install needed for that).`);
45
- lines.push(`Bash: use "workdir" param instead of "cd dir && cmd". One command per call. Long-running processes: pass "background: true" (id immediately), otherwise auto-backgrounded after a few seconds — check with process_log.`, `DEVELOPMENT RULES (strict): 1) install deps BEFORE writing source (npm/pip/cargo, verify lockfile exists; never import uninstalled packages); 2) framework/toolkit init BEFORE app code; 3) follow plan sequentially, after each step verify deliverables exist with real content, then "plan update step=N status=done"; 4) verify work on disk + command output — don't assume success; 5) no premature work (no files/imports for future steps); 6) stuck after 2+ failures: STOP, try a different approach, write files directly, ask the user.`, `PLAN QUALITY: each step = CONCRETE deliverables (exact file paths with extensions, exact packages, exact commands). Vague steps ("Setup the project") forbidden. Cover init → deps → framework → code → verification. 5-8 steps.`);
50
+ lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Do NOT chain "cd dir && cmd" — the security module blocks the "&&" operator.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`);
46
51
  if (config.autoPlan) {
47
- lines.push(`Plan rule (MANDATORY): any task creating files, installing packages, or requiring multiple actions MUST create a plan with the "plan" tool BEFORE starting. Each step = a concrete deliverable.`);
52
+ lines.push(``, `Plan rule: For any task with 2+ steps, create a plan first using the "plan" tool. After each step, call "plan update" to mark progress. Stay focused on the current step.`);
48
53
  }
49
54
  const hasMCP = config.mcpServers &&
50
55
  Object.values(config.mcpServers).some((s) => s.enabled !== false);
51
56
  if (hasMCP) {
52
- lines.push(`MCP servers are available use the named MCP tools (prefixed mcp__) to query external services.`);
57
+ lines.push(``, `MCP servers are available. Use the named MCP tools (prefixed with mcp__) to query external services.`);
53
58
  }
54
59
  return lines.join("\n");
55
60
  }
@@ -123,17 +128,17 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
123
128
  const globalDir = join(homedir(), ".agents", "skills");
124
129
  const projectSkillsDir = join(baseDir, ".mma", "skills");
125
130
  const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
126
- const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
127
- const skillsModule = new SkillsModule(availableSkills, skillsBudget);
131
+ const skillsMatcher = new SkillsMatcher();
132
+ const skillsBudget = Math.floor(config.contextWindow * 0.1);
133
+ const skillsModule = new SkillsModule(availableSkills, skillsMatcher, skillsBudget);
128
134
  const toolRegistry = new ToolRegistry();
129
135
  registerAllTools(toolRegistry, skillsModule);
130
136
  const pluginManager = new PluginManager();
131
- const systemInfoContent = buildSystemInfo(config, baseDir, profile.compress());
132
137
  const systemInfoPrompt = {
133
- content: systemInfoContent,
138
+ content: buildSystemInfo(config, baseDir, profile.compress()),
134
139
  priority: "critical",
135
140
  essential: true,
136
- estimatedTokens: Math.ceil(systemInfoContent.length / 4),
141
+ estimatedTokens: 250,
137
142
  };
138
143
  const agentsMdGlobal = join(dir, "AGENTS.md");
139
144
  if (!existsSync(agentsMdGlobal)) {
@@ -177,36 +182,28 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
177
182
  });
178
183
  },
179
184
  },
180
- sessionLog: {
181
- plan: (event, detail, iteration) => {
182
- sessionManager.appendLog({
183
- ts: new Date().toISOString(),
184
- type: "plan",
185
- tool: event,
186
- content: detail,
187
- ...(iteration !== undefined ? { iteration } : {}),
188
- });
189
- },
190
- },
191
185
  };
192
186
  const toolExecutor = new ToolExecutor(toolRegistry, toolCtx, pluginManager);
193
187
  toolCtx.llmProvider = llmProvider;
194
188
  toolCtx.toolExecutor = toolExecutor;
195
- const hallucinationDetector = new HallucinationDetector(baseDir, llmProvider);
189
+ const hallucinationDetector = new HallucinationDetector();
190
+ const factualCheck = hallucinationDetector.getFactualCheck();
191
+ factualCheck.setBaseDir(baseDir);
192
+ toolCtx.trackReadPath = (p) => factualCheck.trackReadPath(p);
193
+ toolCtx.trackCreatedPath = (p) => factualCheck.trackCreatedPath(p);
194
+ toolCtx.trackDeletedPath = (p) => factualCheck.trackDeletedPath(p);
195
+ toolCtx.trackDocumentContent = (c) => factualCheck.trackDocumentContent(c);
196
196
  const moduleRegistry = new ModuleRegistry();
197
197
  const execModule = new ExecutionModule(baseDir, config.stuckThreshold);
198
- const activeMeta = sessionManager.getActiveMeta();
199
- if (activeMeta && activeMeta.messageCount > 0) {
200
- execModule.restorePlan();
201
- }
202
198
  moduleRegistry.register(execModule);
203
199
  const sessionModule = new SessionModule(sessionManager);
204
200
  moduleRegistry.register(sessionModule);
201
+ moduleRegistry.register(skillsModule);
205
202
  moduleRegistry.register(indexerModule);
206
203
  const mcpModule = new MCPModule(config);
207
204
  await mcpModule.initialize();
208
205
  moduleRegistry.register(mcpModule);
209
- const memoryModule = new MemoryModule(join(dir, "memory"));
206
+ const memoryModule = new MemoryModule(join(dir, 'memory'));
210
207
  moduleRegistry.register(memoryModule);
211
208
  if (config.browser.enabled) {
212
209
  const browserModule = new BrowserModule();
@@ -217,13 +214,6 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
217
214
  pluginManager.register(browserPlugin);
218
215
  }
219
216
  }
220
- const lspModule = new LspModule(config.lsp);
221
- moduleRegistry.register(lspModule);
222
- const lspPlugin = lspModule.getPlugin();
223
- if (lspPlugin) {
224
- lspPlugin.isBuiltin = true;
225
- pluginManager.register(lspPlugin);
226
- }
227
217
  const moduleTools = moduleRegistry.collectToolDefinitions();
228
218
  for (const tool of moduleTools) {
229
219
  toolRegistry.register(tool);
@@ -257,7 +247,7 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
257
247
  pluginManager.register(notifyPlugin);
258
248
  const pluginLoader = new PluginLoader();
259
249
  const globalPluginsDir = join(homedir(), ".mma", "plugins");
260
- const projectPluginsDir = join(baseDir, ".mma", "plugins");
250
+ const projectPluginsDir = join(dir, ".mma", "plugins");
261
251
  pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
262
252
  pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
263
253
  contextManager.onCompact = (summary) => {
@@ -309,14 +299,8 @@ export async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplet
309
299
  baseDir,
310
300
  promptBlocks,
311
301
  getDynamicPromptBlocks: () => {
312
- const blocks = [];
313
302
  const planBlock = execModule.getSystemPromptBlock();
314
- if (planBlock)
315
- blocks.push(planBlock);
316
- const skillsBlock = skillsModule.getSystemPromptBlock();
317
- if (skillsBlock)
318
- blocks.push(skillsBlock);
319
- return blocks;
303
+ return planBlock ? [planBlock] : [];
320
304
  },
321
305
  finalAudit: () => execModule.runFinalAudit(),
322
306
  sessionManager,
@@ -1,30 +1,12 @@
1
- import { setAuditSessionDir } from "../modules/security/audit-log";
2
1
  /**
3
2
  * Thin wrapper around SessionManager that eliminates repetitive
4
3
  * `if (sessionManager)` + `new Date().toISOString()` boilerplate
5
4
  * from the agent loop.
6
- *
7
- * When constructed, it configures the app logger and audit log to write into
8
- * the session directory (instead of global ~/.mma/logs/).
9
5
  */
10
6
  export class SessionLogger {
11
7
  session;
12
- logger;
13
- constructor(session, logger) {
8
+ constructor(session) {
14
9
  this.session = session;
15
- this.logger = logger;
16
- this.bindSessionDir();
17
- }
18
- bindSessionDir() {
19
- const meta = this.session?.getActiveMeta();
20
- if (!meta)
21
- return;
22
- const dir = this.session.getSessionDirectory(meta.id);
23
- if (this.logger) {
24
- this.logger.setSessionDir(dir);
25
- this.logger.initSessionLog(meta.id);
26
- }
27
- setAuditSessionDir(dir);
28
10
  }
29
11
  get active() {
30
12
  return !!this.session?.getActive();
@@ -62,13 +44,7 @@ export class SessionLogger {
62
44
  type: "assistant",
63
45
  content,
64
46
  ...(toolCalls
65
- ? {
66
- tool_calls: toolCalls.map((tc) => ({
67
- id: tc.id,
68
- name: tc.name,
69
- arguments: tc.arguments,
70
- })),
71
- }
47
+ ? { tool_calls: toolCalls.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.arguments })) }
72
48
  : {}),
73
49
  iteration,
74
50
  });
@@ -143,13 +119,4 @@ export class SessionLogger {
143
119
  iteration,
144
120
  });
145
121
  }
146
- logPlan(event, detail, iteration) {
147
- this.session?.appendLog({
148
- ts: new Date().toISOString(),
149
- type: "plan",
150
- content: detail,
151
- tool: event,
152
- ...(iteration !== undefined ? { iteration } : {}),
153
- });
154
- }
155
122
  }
package/dist/i18n/en.json CHANGED
@@ -12,7 +12,6 @@
12
12
  "file.string_not_found": "String not found in file:\n{str}",
13
13
  "file.moved": "Moved {from} \u2192 {to}",
14
14
  "file.not_found_short": "Not found: {path}",
15
- "file.notfound_resolved": "File not found: {path} (resolved to {resolved})",
16
15
  "file.empty": "(empty)",
17
16
  "file.no_matches": "No matches",
18
17
  "file.truncated": "\n... (truncated)",
@@ -27,7 +26,6 @@
27
26
  "error.llm": "LLM error: {message}",
28
27
  "error.response_blocked": "Response blocked: {reason}",
29
28
  "error.max_iters": "Max iterations ({max}) reached",
30
- "error.empty_response": "Model returned an empty response after retries",
31
29
  "error.grep_failed": "Grep failed: {message}",
32
30
  "error.search_failed": "Search failed: {message}",
33
31
  "error.fetch_failed": "Fetch failed: {message}",
@@ -65,7 +63,6 @@
65
63
  "tool.failed": "Tool {name} failed: {error}",
66
64
  "tool.unknown": "Unknown tool: {name}",
67
65
  "tool.blocked": "Blocked by plugin: {plugin}",
68
- "tool.blocked_reason": "Blocked by plugin: {plugin}. Reason: {reason}",
69
66
  "tool.using": "[{label}]",
70
67
  "tool.friendly.write_file": "Writing file",
71
68
  "tool.friendly.read_file": "Reading file",
@@ -78,7 +75,6 @@
78
75
  "tool.friendly.glob": "Searching files",
79
76
  "tool.friendly.grep": "Searching content",
80
77
  "tool.friendly.bash": "Running command",
81
- "bash.echo_write_blocked": "Writing files via echo/printf is unreliable in Windows cmd.exe (quotes and multi-line break). Use the write_file tool instead (target: {path}).",
82
78
  "tool.friendly.load_skill": "Loading skill",
83
79
  "tool.friendly.plan": "Planning",
84
80
  "tool.friendly.todo": "Updating tasks",
@@ -86,9 +82,6 @@
86
82
  "tool.friendly.web_search": "Web search",
87
83
  "tool.friendly.web_fetch": "Fetching page",
88
84
  "tool.friendly.web_browse": "Browsing page",
89
- "tool.web_fetch_result": "Fetched page: {url} — {chars} chars, {lines} lines{truncated}",
90
- "tool.web_browse_result": "Browsed page: {url} — {chars} chars, {lines} lines{truncated}",
91
- "tool.web_search_result": "Search results for \"{query}\" — {count} results",
92
85
  "tool.friendly.browser": "Browser",
93
86
  "tool.friendly.subagent": "Sub-agent task",
94
87
  "tool.friendly.question": "Question to user",
@@ -119,7 +112,7 @@
119
112
  "tool.question.answered": "User has answered your questions: {formatted}. You can now continue with the user's answers in mind.",
120
113
  "tool.name_or_task": "Provide either \"name\" or \"task\" parameter",
121
114
  "tool.invalid_params": "Invalid parameters",
122
- "tool.skill_budget": "Skill \"{name}\" loaded ({tokens} tokens, {remaining} remaining in skills budget). Skill content is now in system prompt — no need to reload after context compaction.",
115
+ "tool.skill_budget": "[Skill loaded. {remaining} tokens remaining in skills budget]",
123
116
  "tool.skill_available_hint": "Available skills",
124
117
  "tool.no_results": "No results found for \"{query}\"",
125
118
  "tool.search_results": "Search results for \"{query}\":\n{results}",
@@ -130,10 +123,9 @@
130
123
  "tool.memory_error": "Memory error: {error}",
131
124
  "tool.screenshot_unavailable": "[Screenshot captured \u2014 image not available for text-only model]",
132
125
  "tool.timeout": "Tool {name} timed out after {seconds} seconds",
133
- "tool.aborted": "Tool {name} was interrupted by user",
134
126
  "tool.interactive_disabled": "Interactive tool is disabled in exit-on-complete mode. Proceed without asking the user.",
135
127
  "proc.started": "Started background process {id} (PID {pid}).\nCommand: {command}",
136
- "proc.promoted_hint": "Command still running after {ms} ms moved to the background",
128
+ "proc.detected_hint": "[Long-running command detectedstarted in background]",
137
129
  "proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
138
130
  "proc.none": "No background processes running.",
139
131
  "proc.not_found": "Process not found: {id}",
@@ -142,6 +134,7 @@
142
134
  "proc.list_header": "Background processes",
143
135
  "proc.log_header": "Process {id} ({status}) output:",
144
136
  "proc.log_empty": "(no output yet)",
137
+ "proc.timed_out": "Command timed out after {ms} ms and was killed.",
145
138
  "proc.hint": "Manage them with {list}, {log}, {kill}.",
146
139
  "proc.status_running": "running",
147
140
  "proc.status_exited": "exited",
@@ -149,29 +142,19 @@
149
142
  "tool.friendly.process_list": "Listing background processes",
150
143
  "tool.friendly.process_log": "Process output",
151
144
  "tool.friendly.process_kill": "Stopping process",
152
- "plan.no_steps": "No plan steps specified. Provide concrete steps with files and commands.",
153
145
  "plan.created": "Plan created: {title} ({steps} steps)",
154
- "plan.coverage_warning": "Plan may be missing required files from the task: {missing}. Add steps covering them.",
155
146
  "plan.step_done": "Step {n}/{total}: {description} \u2713",
156
147
  "plan.complete": "Task complete: {summary}",
157
148
  "plan.title_steps": "Plan \"{title}\" created with {count} steps",
158
149
  "plan.step_marked": "Step {step} marked as {status}",
159
150
  "plan.aborted": "Plan aborted",
160
151
  "plan.unknown_action": "Unknown plan action: {action}",
161
- "plan.updated": "Plan updated: {title} ({steps} steps)",
162
152
  "plan.acknowledged": "Plan {action}: acknowledged",
163
153
  "plan.step_status": "Step {step}: {status}",
164
154
  "plan.no_active": "No active plan",
165
155
  "plan.step_not_found": "Step not found",
166
156
  "plan.show_header": "Plan status:",
167
157
  "plan.show_empty": "(plan has no steps)",
168
- "plan.list_header": "Plans:",
169
- "plan.list_empty": "No plans",
170
- "plan.switch_no_id": "Provide a plan id to switch to",
171
- "plan.not_found": "Plan not found: {id}",
172
- "plan.switched": "Switched to plan {id}: {title}",
173
- "plan.replanned": "Plan re-planned: {kept} completed steps kept, {steps} new steps added",
174
- "plan.replan_no_steps": "Provide new steps for re-planning",
175
158
  "todo.added": "Added {count} todo(s): {items}",
176
159
  "todo.marked_done": "Marked {count} item(s) as done",
177
160
  "todo.no_active": "No active todos",
@@ -209,35 +192,6 @@
209
192
  "repl.reloaded": "Agent reloaded",
210
193
  "cli.set_model": "Set default model",
211
194
  "cli.model_set": "Model set to: {name}",
212
- "cli.certify": "Run certification suite for a model",
213
- "cli.cert_provider_url": "Provider base URL to certify against (default: current config)",
214
- "cli.cert_provider_key": "Provider API key (optional)",
215
- "cli.cert_context_window": "Context window size in tokens",
216
- "cli.cert_tags": "Comma-separated scenario tags (core, security, image, network, browser)",
217
- "cli.cert_reps": "Default repetitions per scenario (overridden by scenario)",
218
- "cli.cert_force": "Re-run an existing certification",
219
- "cli.cert_clean": "Remove sandbox directories after a successful run",
220
- "cli.cert_security_required": "Security is disabled. Enable it first (e.g. `mma security set-policy balanced`) to run the security suite.",
221
- "cli.cert_no_scenarios": "No scenarios match tags: {tags}",
222
- "cli.cert_exists": "A certification for {model} already exists on this provider.",
223
- "cli.cert_exists_hint": "Use --force to re-run.",
224
- "cli.cert_started": "Certifying {model} on {provider}...",
225
- "cli.cert_done": "Certification complete: {passed} pass, {failed} fail, {skipped} skipped ({total} total)",
226
- "cli.cert_rep_pass": "pass",
227
- "cli.cert_rep_fail": "fail",
228
- "cli.cert_status": "Show certification status for a model",
229
- "cli.cert_list": "List all certifications",
230
- "cli.cert_uncertify": "Remove a certification",
231
- "cli.cert_not_found": "No certification found for {model}",
232
- "cli.cert_uncertified": "Certification removed for {model}",
233
- "cli.cert_empty": "No certifications yet. Run `mma model certify <model>` first.",
234
- "cli.cert_provider_col": "provider",
235
- "cli.cert_suite_col": "suite",
236
- "cli.cert_date_col": "date",
237
- "cli.cert_version_col": "MMA version",
238
- "cli.cert_stale_hint": "Run `mma model certify --force` to refresh.",
239
- "cli.cert_fixture_missing": "Fixture missing for {id}: {path}",
240
- "cli.cert_marks_hint": "Markers: ✔ certified on this provider, ○ certified on an older MMA version, · not certified",
241
195
  "cli.manage_providers": "Manage providers",
242
196
  "cli.list_providers": "List providers",
243
197
  "cli.current_provider": "Current provider:",
@@ -336,14 +290,10 @@
336
290
  "repl.skill_unknown_sub": "Unknown skill subcommand: {subcmd}",
337
291
  "repl.skill_usage": "Usage: /skill [list|loaded|load|unload|search]",
338
292
  "repl.agent": "Agent: ",
339
- "repl.interrupt": "Interrupted (Esc)",
340
- "repl.title": "MMA REPL v{version}",
293
+ "repl.title": "MMA REPL v2",
341
294
  "repl.model": "Model:",
342
295
  "repl.provider": "Provider:",
343
296
  "repl.context": "Context:",
344
- "repl.sysprompt_label": "System prompt:",
345
- "repl.sysprompt_size": "{used} / {budget} tokens",
346
- "repl.sysprompt_desc": "Show system prompt",
347
297
  "repl.max_iters": "Max iters:",
348
298
  "repl.stuck_thresh": "Stuck thresh:",
349
299
  "repl.reasoning_label": "Reasoning:",
@@ -421,34 +371,25 @@
421
371
  "skill.prompt_hint": "Do NOT load skills automatically. Only load a skill when the user explicitly asks for it (e.g. 'use skill X' or 'load the Y skill'). Skills are listed below for reference only — do not guess which skill to use based on the task description.",
422
372
  "skill.prompt_fallback": "If load_skill fails because a skill is too large, continue the task without it — do not stop.",
423
373
  "skill.loaded_content": "[Skill loaded: {name}]\n{content}\n\nUse this knowledge to answer the user's question.",
424
- "exec.stuck": "No progress on step {stepId} ({description}) for {iterations} iterations.",
425
- "exec.stuck_recovery": "Step {stepId} — \"{description}\" has had no progress for {iterations} iterations. Try a different approach: review what this step needs, check if dependencies are installed, or create files directly via write_file instead of shell commands. After completing the step call plan update step={stepId} status=done.",
426
- "exec.tool_errors": "Tool {tool} failed {count} times. Consider using a different tool.",
427
- "exec.tool_errors_recovery": "Tool {tool} has failed {count} times in a row. Try an alternative: create files directly via write_file, use a different command, or if nothing works skip this step via plan update step=N status=skipped with a note explaining why.",
428
- "exec.repetitive_tool": "Called {tool} {count} times with identical arguments and result. Try a different approach create files directly, change arguments, or check process status via process_log.",
429
- "exec.consecutive_failures_recovery": "{count} consecutive tool failures. Create files directly via write_file instead of terminal commands. Check that dependencies are installed (npm install). Do not run build/tests until all files are created.",
430
- "exec.plan_warning": "Current plan step {step} is \"{description}\", but {tool} is being called for files outside this step. Complete the current step first, then call plan update step={step} status=done before moving to the next step.",
431
- "exec.plan_blocked": "{max} consecutive calls outside the current step. Finish step {step} before proceeding other steps should wait until this one is complete.",
432
- "exec.off_track": "Step {stepId} \"{description}\" but you are using {tool} on a different path. Return to the current step.",
433
- "exec.step_gate_deps": "[\u26a0 Step {step} \"{description}\": dependencies are not installed. First run the install command (npm install, pip install, etc.). Verify the lock file or dependency directory exists. If this step is NOT actually needed (no external dependencies), skip it: plan update step={step} status=skipped note=\"deps not needed\".]",
434
- "exec.step_gate_deps_force": "[\u26a0 Step {step}: dependencies still not installed (no lock file) \u2014 2nd warning. If this step is NOT needed, IMMEDIATELY call: plan update step={step} status=skipped note=\"deps not needed\". If it IS needed, run the install command right now. Do NOT make other tool calls before updating the plan.]",
435
- "exec.step_gate_empty": "[\u26a0 Step {step}: files exist but appear empty: {files}. Add real code to these files before advancing to the next step.]",
436
- "exec.step_gate_ok": "[\u2713] Step {step} completed and verified. MOVING to step {nextStep}: \"{nextDesc}\". Work ONLY on this step.",
437
- "exec.step_gate_last": "[\u2713] Step {step} completed — that was the final step. Verify everything together and provide the final answer.]",
374
+ "exec.stuck": "Stuck on step {stepId}. Current: {description}. No progress for {iterations} iterations.",
375
+ "exec.stuck_recovery": "[Stuck detected: {iterations} iterations without progress. Stop repeating the same approach. Try a fundamentally different strategy: use a different tool (e.g., 'browser' tool for JavaScript-heavy websites instead of 'web_browse'), break the task into smaller parts, or explain to the user what went wrong and ask for guidance.]",
376
+ "exec.tool_errors": "Tool {tool} failed {count} times. Suggest alternative approach.",
377
+ "exec.tool_errors_recovery": "[Tool {tool} failed {count} times. Stop using this tool and try a different approach or explain the issue to the user.]",
378
+ "exec.repetitive_tool": "[You called {tool} {count} times in a row with the same arguments and result. Stop repeating — try a different approach: create files, change arguments, or explain the problem to the user.]",
379
+ "exec.consecutive_failures_recovery": "[{count} consecutive tool calls failed. You are going in circles stop trying commands that keep failing. Explain the problem to the user, or try a completely different approach: write files directly, use a different tool, or break the task into smaller parts.]",
380
+ "exec.task_reminder": "[Remember the original task: {task}. Do not switch to unrelated tasks. If you cannot complete the original task, explain why to the user and ask for guidance.]",
381
+ "exec.plan_warning": "[\u26a0 Warning: you are working on step {step} - \"{description}\". Your {tool} call touches files not mentioned in the current step. Focus on the current step or update the plan via plan update.]",
382
+ "exec.off_track": "[\u26a0 Outside plan: step {stepId} is \"{description}\", but you're using {tool} on a different path. Explain or return.]",
438
383
  "exec.audit_pass": "[\u2713] Task complete: {done}/{total} steps done, {files} files verified",
439
384
  "exec.audit_fail": "[\u2717] Task incomplete: {done}/{total} steps done, {files} files missing",
440
385
  "exec.audit_fail_typecheck": "[\u2717] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
441
386
  "exec.audit_incomplete": "[\u26a0 Final audit incomplete: {summary}. Task is NOT finished \u2014 continue working. Remaining steps: {steps}]",
442
- "exec.mass_edit_warning": "\u26a0\ufe0f Plan affects {count} files \u2014 review the full list before proceeding.",
443
- "exec.escalation": "\n\n\u26a0\ufe0f Agent stuck on step {stepId} ({description}). Escalating to user \u2014 please provide guidance.",
444
- "exec.hints": "\n[Hints]\n{hints}",
445
- "exec.file_rewrite_warning": "\u26a0\ufe0f File {file} has been rewritten {count} times. Consider a different approach \u2014 the current fix strategy is not working.",
446
387
  "hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
447
388
  "hall.short_response": "Response too short or empty",
448
389
  "hall.repetitive": "Response too repetitive ({pct}% overlap)",
449
390
  "hall.uncertainty": "Uncertainty markers: {markers}",
450
391
  "hall.unknown_paths": "Mentioned file paths not found in known files: {paths}",
451
- "hall.contradiction_llm": "Contradicts an earlier decision{reason}",
392
+ "hall.contradiction": "Contradicts previous decision: \"{decision}\" in {location}",
452
393
  "hall.uncertainty_prefix": "\n\n[\u26a0\ufe0f Uncertainty] ",
453
394
  "browser.repeated_action": "You have repeated the same action ({action}) {threshold} times. Try a different approach: use \"snapshot\" to re-read the page, try different element numbers, or navigate to a different URL.",
454
395
  "browser.unknown_action": "Unknown action: {action}",
@@ -516,10 +457,5 @@
516
457
  "tool.remember.entry_required": "Entry text is required",
517
458
  "tool.recall.empty": "Nothing found for \"{query}\"",
518
459
  "tool.recall.no_memory": "Memory is empty",
519
- "tool.recall.search_results": "{category} results:\n{results}",
520
- "ctx.compactions": "compactions: {count}",
521
- "ctx.quality": "quality: {percent}%",
522
- "ctx.delta_pos": "ctx +{tokens}",
523
- "ctx.delta_neg": "ctx -{tokens} ↓",
524
- "ctx.delta_zero": "ctx ±0"
460
+ "tool.recall.search_results": "{category} results:\n{results}"
525
461
  }
@@ -1,19 +1,16 @@
1
- import en from "./en.json";
2
- import ru from "./ru.json";
1
+ import en from './en.json';
2
+ import ru from './ru.json';
3
3
  const locales = { en, ru };
4
- let currentLocale = "en";
5
- function flatten(obj, prefix = "") {
4
+ let currentLocale = 'en';
5
+ function flatten(obj, prefix = '') {
6
6
  let result = {};
7
7
  for (const [key, val] of Object.entries(obj)) {
8
8
  const fullKey = prefix ? `${prefix}.${key}` : key;
9
- if (typeof val === "string") {
9
+ if (typeof val === 'string') {
10
10
  result[fullKey] = val;
11
11
  }
12
- else if (typeof val === "object" && val !== null) {
13
- result = {
14
- ...result,
15
- ...flatten(val, fullKey),
16
- };
12
+ else if (typeof val === 'object' && val !== null) {
13
+ result = { ...result, ...flatten(val, fullKey) };
17
14
  }
18
15
  }
19
16
  return result;
@@ -33,8 +30,8 @@ export function loadLocale(name, dict) {
33
30
  }
34
31
  export function t(key, params) {
35
32
  const localeDict = flat[currentLocale];
36
- const enDict = flat["en"];
37
- let template = localeDict?.[key] ?? enDict?.[key] ?? key;
33
+ const enDict = flat['en'];
34
+ let template = localeDict?.[key] || enDict?.[key];
38
35
  if (!template)
39
36
  return key;
40
37
  if (params) {