micro-models-agent 0.29.0 → 0.29.2

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 (2) hide show
  1. package/dist/main.js +120 -58
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2304,6 +2304,7 @@ var init_en = __esm(() => {
2304
2304
  "error.response_blocked": "Response blocked: {reason}",
2305
2305
  "error.max_iters": "Max iterations ({max}) reached",
2306
2306
  "error.empty_response": "Model returned an empty response after retries",
2307
+ "error.audit_failed": "Task could not be verified as complete: {summary}",
2307
2308
  "error.grep_failed": "Grep failed: {message}",
2308
2309
  "error.search_failed": "Search failed: {message}",
2309
2310
  "error.fetch_failed": "Fetch failed: {message}",
@@ -2448,6 +2449,8 @@ Command: {command}`,
2448
2449
  "plan.step_status": "Step {step}: {status}",
2449
2450
  "plan.no_active": "No active plan",
2450
2451
  "plan.step_not_found": "Step not found",
2452
+ "plan.step_already_done": "Step {step} is already marked done. Stop re-marking it — either continue with the next step or investigate why progress appears stuck.",
2453
+ "plan.order_blocked": 'Cannot mark step {step} done: step {first} ("{desc}") is not finished yet. Complete earlier steps first, or mark step {first} status=skipped if it is not needed.',
2451
2454
  "plan.show_header": "Plan status:",
2452
2455
  "plan.show_empty": "(plan has no steps)",
2453
2456
  "plan.list_header": "Plans:",
@@ -2870,6 +2873,7 @@ var init_ru = __esm(() => {
2870
2873
  "error.response_blocked": "Ответ заблокирован: {reason}",
2871
2874
  "error.max_iters": "Достигнут максимум итераций ({max})",
2872
2875
  "error.empty_response": "Модель вернула пустой ответ после повторных попыток",
2876
+ "error.audit_failed": "Задача не может быть подтверждена как выполненная: {summary}",
2873
2877
  "error.grep_failed": "Ошибка grep: {message}",
2874
2878
  "error.search_failed": "Ошибка поиска: {message}",
2875
2879
  "error.fetch_failed": "Ошибка загрузки: {message}",
@@ -3013,6 +3017,8 @@ var init_ru = __esm(() => {
3013
3017
  "plan.step_status": "Шаг {step}: {status}",
3014
3018
  "plan.no_active": "Нет активного плана",
3015
3019
  "plan.step_not_found": "Шаг не найден",
3020
+ "plan.step_already_done": "Шаг {step} уже отмечен выполненным. Прекратите повторную отметку — переходите к следующему шагу или выясните, почему прогресс заблокирован.",
3021
+ "plan.order_blocked": "Нельзя отметить шаг {step} выполненным: шаг {first} («{desc}») ещё не завершён. Сначала завершите предыдущие шаги или отметьте шаг {first} status=skipped, если он не нужен.",
3016
3022
  "plan.show_header": "Статус плана:",
3017
3023
  "plan.show_empty": "(в плане нет шагов)",
3018
3024
  "plan.list_header": "Планы:",
@@ -7469,7 +7475,7 @@ function emptyCliRunHint(command, output, code) {
7469
7475
  return null;
7470
7476
  return "the command exited 0 but printed NOTHING to stdout. If this should run a CLI program, the file probably has no entry point: read it with read_file and check the code actually calls its main function with command-line arguments (e.g. main(process.argv[2])) and prints results with console.log.";
7471
7477
  }
7472
- var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, bashTool;
7478
+ var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, bashTool;
7473
7479
  var init_bash = __esm(() => {
7474
7480
  init_command_validator();
7475
7481
  init_audit_log();
@@ -7478,6 +7484,7 @@ var init_bash = __esm(() => {
7478
7484
  init_processes();
7479
7485
  init_i18n();
7480
7486
  bashGraceMs = BASH_GRACE_MS;
7487
+ FAILING_FIRST_WORDS = new Map;
7481
7488
  UNIX_TO_WIN_HINTS = {
7482
7489
  ls: "Use the list_dir tool instead.",
7483
7490
  pwd: "Use the file_info tool instead.",
@@ -7629,13 +7636,22 @@ ${output2}`;
7629
7636
  if (!output2 && code !== 0) {
7630
7637
  output2 = `(exit code ${code})`;
7631
7638
  }
7632
- if (platform2() === "win32" && code !== 0) {
7639
+ if (platform2() === "win32") {
7633
7640
  const firstWord = command.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
7634
- const hint = firstWord ? UNIX_TO_WIN_HINTS[firstWord] : undefined;
7635
- if (hint) {
7636
- output2 = `${output2}
7641
+ if (firstWord && firstWord in UNIX_TO_WIN_HINTS) {
7642
+ if (code === 0) {
7643
+ FAILING_FIRST_WORDS.delete(firstWord);
7644
+ } else {
7645
+ const failures = (FAILING_FIRST_WORDS.get(firstWord) || 0) + 1;
7646
+ FAILING_FIRST_WORDS.set(firstWord, failures);
7647
+ if (failures >= HARD_BLOCK_THRESHOLD) {
7648
+ output2 = `STOP using "${firstWord}" — it does not work in this cmd.exe shell and has failed ${failures} times in a row. ${UNIX_TO_WIN_HINTS[firstWord]}`;
7649
+ } else {
7650
+ output2 = `${output2}
7637
7651
 
7638
- Hint: "${firstWord}" may not work on Windows. ${hint}`;
7652
+ Hint: "${firstWord}" may not work on Windows. ${UNIX_TO_WIN_HINTS[firstWord]}`;
7653
+ }
7654
+ }
7639
7655
  }
7640
7656
  }
7641
7657
  if (securityConfig?.logCommands) {
@@ -10392,6 +10408,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10392
10408
  this.abortController = new AbortController;
10393
10409
  let iteration = 0;
10394
10410
  let lastText = "";
10411
+ let lastForcedCompactionIteration = -FORCED_COMPACTION_COOLDOWN;
10395
10412
  let hallucinationRetries = 0;
10396
10413
  let lastToolSignature = "";
10397
10414
  let apiPromptTokens = 0;
@@ -10405,6 +10422,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10405
10422
  let emptyResponseRetries = 0;
10406
10423
  const MAX_EMPTY_RESPONSE_RETRIES = 2;
10407
10424
  let emptyResponseExhausted = false;
10425
+ let auditFailed = false;
10426
+ let lastAuditSummary = "";
10408
10427
  let repeatedToolCount = 0;
10409
10428
  const MAX_REPEATED_TOOL_CALLS = 2;
10410
10429
  const allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
@@ -10431,7 +10450,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
10431
10450
  const currentTokens = contextManager.getEstimatedTokens();
10432
10451
  const budget2 = contextManager.getBudget();
10433
10452
  const quality = contextManager.getQuality();
10434
- if (quality < QUALITY_TRIGGER_THRESHOLD && contextManager.getCompactionCount() > 0) {
10453
+ if (quality < QUALITY_TRIGGER_THRESHOLD && contextManager.getCompactionCount() > 0 && iteration - lastForcedCompactionIteration >= FORCED_COMPACTION_COOLDOWN) {
10454
+ lastForcedCompactionIteration = iteration;
10435
10455
  contextManager.compact();
10436
10456
  logger.warn(`Low context quality (${quality}%) — forced compaction`);
10437
10457
  slog.logCompaction(`quality-triggered compaction (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%), iteration ${iteration}`, iteration, currentTokens, budget2.history);
@@ -10794,8 +10814,10 @@ ${warnLine}
10794
10814
  });
10795
10815
  slog.logAudit(audit.summary, iteration);
10796
10816
  auditRetries++;
10817
+ lastAuditSummary = audit.summary;
10797
10818
  if (auditRetries >= MAX_AUDIT_RETRIES || iteration >= config.maxToolIterations - 1) {
10798
- logger.warn(`Final audit still incomplete after ${auditRetries} retries — finishing anyway`);
10819
+ logger.warn(`Final audit still incomplete after ${auditRetries} retries — reporting failure`);
10820
+ auditFailed = true;
10799
10821
  break;
10800
10822
  }
10801
10823
  continue;
@@ -10823,9 +10845,9 @@ ${warnLine}
10823
10845
  };
10824
10846
  }
10825
10847
  return {
10826
- success: emptyResponseExhausted ? false : true,
10848
+ success: emptyResponseExhausted || auditFailed ? false : true,
10827
10849
  text: lastText,
10828
- error: emptyResponseExhausted ? t("error.empty_response") : undefined,
10850
+ error: emptyResponseExhausted ? t("error.empty_response") : auditFailed ? t("error.audit_failed", { summary: lastAuditSummary }) : undefined,
10829
10851
  iterationCount: iteration,
10830
10852
  contextUsed: tokensUsed,
10831
10853
  contextLimit: budget.history,
@@ -10890,7 +10912,7 @@ ${warnLine}
10890
10912
  });
10891
10913
  }
10892
10914
  }
10893
- var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40;
10915
+ var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3;
10894
10916
  var init_agent = __esm(() => {
10895
10917
  init_i18n();
10896
10918
  init_colors();
@@ -10993,7 +11015,8 @@ class ContextManager {
10993
11015
  getQuality() {
10994
11016
  const usedTokens = this.getEstimatedTokens();
10995
11017
  const tokenLoad = Math.max(0, 1 - usedTokens / this.budget.history);
10996
- const compactionLoss = Math.max(0, 1 - this.compactionCount * 0.15);
11018
+ const COMPACTION_PENALTY_FLOOR = 0.5;
11019
+ const compactionLoss = Math.max(COMPACTION_PENALTY_FLOOR, 1 - this.compactionCount * 0.15);
10997
11020
  const msgCount = this.messages.length || 1;
10998
11021
  const errorDensity = Math.max(0, 1 - Math.min(1, this.errorFacts.length / msgCount));
10999
11022
  const freshness = Math.max(0, 1 - this.iterationsSinceCompaction / COMPACTION_INTERVAL);
@@ -11139,6 +11162,9 @@ ${lines.join(`
11139
11162
  const newErrors = [];
11140
11163
  for (const msg of turns) {
11141
11164
  const content = getMessageText(msg.content);
11165
+ if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
11166
+ continue;
11167
+ }
11142
11168
  if (msg.role === "tool") {
11143
11169
  for (const pattern of filePatterns) {
11144
11170
  for (const match of content.matchAll(pattern)) {
@@ -12119,6 +12145,7 @@ var DEFAULT_CHUNK_SYSTEM_PROMPT = 'Answer the query using ONLY the provided text
12119
12145
 
12120
12146
  // src/tools/chunk-query.ts
12121
12147
  import { readFileSync as readFileSync12 } from "node:fs";
12148
+ import { resolve as resolve14 } from "node:path";
12122
12149
  var chunkQueryTool;
12123
12150
  var init_chunk_query = __esm(() => {
12124
12151
  init_i18n();
@@ -12186,7 +12213,7 @@ var init_chunk_query = __esm(() => {
12186
12213
  return { success: false, output: `[SCOPE] ${check.reason || "Path not allowed"}` };
12187
12214
  }
12188
12215
  try {
12189
- content = readFileSync12(inputPath, "utf8");
12216
+ content = readFileSync12(resolve14(ctx.baseDir, inputPath), "utf8");
12190
12217
  } catch (e) {
12191
12218
  return { success: false, output: `Cannot read ${inputPath}: ${e.message}` };
12192
12219
  }
@@ -12965,7 +12992,7 @@ class MCPClient {
12965
12992
  }
12966
12993
  }
12967
12994
  connectStdio() {
12968
- return new Promise((resolve14, reject) => {
12995
+ return new Promise((resolve15, reject) => {
12969
12996
  const child = spawn3(this.config.command, this.config.args || [], {
12970
12997
  env: { ...process.env, ...this.config.env },
12971
12998
  stdio: ["pipe", "pipe", "pipe"]
@@ -12989,7 +13016,7 @@ class MCPClient {
12989
13016
  this._connected = true;
12990
13017
  this.process = child;
12991
13018
  _resolved = true;
12992
- resolve14();
13019
+ resolve15();
12993
13020
  });
12994
13021
  });
12995
13022
  }
@@ -13058,7 +13085,7 @@ class MCPClient {
13058
13085
  if (!this.process) {
13059
13086
  throw new Error("Not connected to MCP server");
13060
13087
  }
13061
- return new Promise((resolve14, reject) => {
13088
+ return new Promise((resolve15, reject) => {
13062
13089
  const request = {
13063
13090
  jsonrpc: "2.0",
13064
13091
  id: Date.now(),
@@ -13082,7 +13109,7 @@ class MCPClient {
13082
13109
  if (response.error) {
13083
13110
  reject(new Error(response.error.message));
13084
13111
  } else {
13085
- resolve14(response.result?.tools || []);
13112
+ resolve15(response.result?.tools || []);
13086
13113
  }
13087
13114
  return;
13088
13115
  }
@@ -13164,8 +13191,8 @@ class MCPClient {
13164
13191
  arguments: args
13165
13192
  }
13166
13193
  };
13167
- return new Promise((resolve14, reject) => {
13168
- this.pendingResolve = resolve14;
13194
+ return new Promise((resolve15, reject) => {
13195
+ this.pendingResolve = resolve15;
13169
13196
  this.pendingReject = reject;
13170
13197
  const timeout = this.config.timeout || 30000;
13171
13198
  const timer = setTimeout(() => {
@@ -13192,7 +13219,7 @@ class MCPClient {
13192
13219
  if (result.error) {
13193
13220
  reject(new Error(result.error.message));
13194
13221
  } else {
13195
- resolve14(result.result || result);
13222
+ resolve15(result.result || result);
13196
13223
  }
13197
13224
  }).catch((err) => {
13198
13225
  clearTimeout(timer);
@@ -13206,7 +13233,7 @@ class MCPClient {
13206
13233
  if (!this.process) {
13207
13234
  throw new Error("Not connected to MCP server");
13208
13235
  }
13209
- return new Promise((resolve14, reject) => {
13236
+ return new Promise((resolve15, reject) => {
13210
13237
  const request = {
13211
13238
  jsonrpc: "2.0",
13212
13239
  id: Date.now(),
@@ -13234,7 +13261,7 @@ class MCPClient {
13234
13261
  if (response.error) {
13235
13262
  reject(new Error(response.error.message));
13236
13263
  } else {
13237
- resolve14(response.result);
13264
+ resolve15(response.result);
13238
13265
  }
13239
13266
  return;
13240
13267
  }
@@ -14364,7 +14391,7 @@ var init_image_utils = __esm(() => {
14364
14391
 
14365
14392
  // src/tools/attach-image.ts
14366
14393
  import { existsSync as existsSync24 } from "fs";
14367
- import { resolve as resolve14 } from "path";
14394
+ import { resolve as resolve15 } from "path";
14368
14395
  var attachImageTool;
14369
14396
  var init_attach_image = __esm(() => {
14370
14397
  init_i18n();
@@ -14405,7 +14432,7 @@ var init_attach_image = __esm(() => {
14405
14432
  const result = await loadUrlAsDataUrl(source);
14406
14433
  dataUrl = result.dataUrl;
14407
14434
  } else {
14408
- const absPath = resolve14(ctx.baseDir, source);
14435
+ const absPath = resolve15(ctx.baseDir, source);
14409
14436
  if (!existsSync24(absPath)) {
14410
14437
  return {
14411
14438
  success: false,
@@ -14598,7 +14625,7 @@ var init_loader = __esm(() => {
14598
14625
  // src/modules/plugins/builtin/lint-on-write.ts
14599
14626
  import { spawn as spawn4, execSync } from "child_process";
14600
14627
  import { existsSync as existsSync26, readFileSync as readFileSync15 } from "fs";
14601
- import { resolve as resolve15, extname as extname4, join as join20 } from "path";
14628
+ import { resolve as resolve16, extname as extname4, join as join20 } from "path";
14602
14629
  import { platform as platform3 } from "os";
14603
14630
  function contentHash(content) {
14604
14631
  let h = 5381;
@@ -14643,7 +14670,7 @@ class LintOnWritePlugin {
14643
14670
  const path = String(call.arguments.path || "");
14644
14671
  if (!path)
14645
14672
  return;
14646
- const fullPath = resolve15(ctx.baseDir, path);
14673
+ const fullPath = resolve16(ctx.baseDir, path);
14647
14674
  if (!existsSync26(fullPath))
14648
14675
  return;
14649
14676
  const ext = extname4(fullPath);
@@ -14762,7 +14789,7 @@ class LintOnWritePlugin {
14762
14789
  }
14763
14790
  }
14764
14791
  function runAsync(command, cwd, timeoutMs) {
14765
- return new Promise((resolve16, reject) => {
14792
+ return new Promise((resolve17, reject) => {
14766
14793
  const child = spawn4(command, {
14767
14794
  cwd,
14768
14795
  shell: true,
@@ -14791,7 +14818,7 @@ function runAsync(command, cwd, timeoutMs) {
14791
14818
  stdout += decoder.decode();
14792
14819
  stderr += decoder.decode();
14793
14820
  if (code === 0) {
14794
- resolve16({ stdout, stderr });
14821
+ resolve17({ stdout, stderr });
14795
14822
  } else {
14796
14823
  const err = new Error(`Command failed with exit code ${code}`);
14797
14824
  err.stdout = stdout;
@@ -14964,7 +14991,7 @@ var init_tracker = () => {};
14964
14991
 
14965
14992
  // src/modules/execution/auditor.ts
14966
14993
  import { existsSync as existsSync27, readdirSync as readdirSync8 } from "fs";
14967
- import { resolve as resolve16, join as join21 } from "path";
14994
+ import { resolve as resolve17, join as join21 } from "path";
14968
14995
  function findTestFile(dir, depth = 0) {
14969
14996
  if (depth > 5)
14970
14997
  return null;
@@ -15054,7 +15081,7 @@ class Auditor {
15054
15081
  const missingFiles = [];
15055
15082
  const existingFiles = [];
15056
15083
  for (const filePath of uniqueFiles) {
15057
- const resolved = resolve16(this.baseDir, filePath);
15084
+ const resolved = resolve17(this.baseDir, filePath);
15058
15085
  if (existsSync27(resolved)) {
15059
15086
  existingFiles.push(filePath);
15060
15087
  } else {
@@ -15350,7 +15377,7 @@ var init_plan_coverage = __esm(() => {
15350
15377
 
15351
15378
  // src/modules/execution/module.ts
15352
15379
  import { existsSync as existsSync29, readFileSync as readFileSync17 } from "fs";
15353
- import { resolve as resolve17 } from "path";
15380
+ import { resolve as resolve18 } from "path";
15354
15381
 
15355
15382
  class ExecutionModule {
15356
15383
  name = "execution";
@@ -15591,7 +15618,34 @@ ${display}`,
15591
15618
  };
15592
15619
  }
15593
15620
  if (action === "update" && args.step && this.tracker) {
15594
- this.tracker.updateStepStatus(Number(args.step), args.status || "done");
15621
+ const stepId = Number(args.step);
15622
+ const target = this.tracker.getStep(stepId);
15623
+ if (!target) {
15624
+ return { success: false, output: t("plan.step_not_found") };
15625
+ }
15626
+ const status = String(args.status || "done");
15627
+ if (status === "done" && target.status === "done") {
15628
+ return {
15629
+ success: false,
15630
+ output: t("plan.step_already_done", {
15631
+ step: String(stepId)
15632
+ })
15633
+ };
15634
+ }
15635
+ if (status === "done") {
15636
+ const blockers = this.tracker.getPlan().steps.filter((s) => s.id < stepId && s.status !== "done" && s.status !== "skipped");
15637
+ if (blockers.length > 0) {
15638
+ return {
15639
+ success: false,
15640
+ output: t("plan.order_blocked", {
15641
+ step: String(stepId),
15642
+ first: String(blockers[0].id),
15643
+ desc: blockers[0].description
15644
+ })
15645
+ };
15646
+ }
15647
+ }
15648
+ this.tracker.updateStepStatus(stepId, status);
15595
15649
  if (args.note)
15596
15650
  this.tracker.addNote(Number(args.step), String(args.note));
15597
15651
  this.tracker.syncCurrentStep();
@@ -16005,7 +16059,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
16005
16059
  "poetry.lock",
16006
16060
  "requirements.txt"
16007
16061
  ];
16008
- const hasLockFile = lockFiles.some((f) => existsSync29(resolve17(this.baseDir, f)));
16062
+ const hasLockFile = lockFiles.some((f) => existsSync29(resolve18(this.baseDir, f)));
16009
16063
  if (!hasLockFile) {
16010
16064
  if (contextManager) {
16011
16065
  const hints = this.depsGateHints.get(step.id) || 0;
@@ -16025,13 +16079,13 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
16025
16079
  }
16026
16080
  if (stepPaths.length === 0)
16027
16081
  return;
16028
- const allExist = stepPaths.every((p) => existsSync29(resolve17(this.baseDir, p)));
16082
+ const allExist = stepPaths.every((p) => existsSync29(resolve18(this.baseDir, p)));
16029
16083
  if (!allExist)
16030
16084
  return;
16031
16085
  const emptyFiles = [];
16032
16086
  for (const p of stepPaths) {
16033
16087
  try {
16034
- const content = readFileSync17(resolve17(this.baseDir, p), "utf-8");
16088
+ const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
16035
16089
  if (content.trim().length < 10) {
16036
16090
  emptyFiles.push(p);
16037
16091
  }
@@ -16968,7 +17022,7 @@ var init_browser2 = __esm(() => {
16968
17022
 
16969
17023
  // src/modules/lsp/client.ts
16970
17024
  import { spawn as spawn5, execSync as execSync2 } from "child_process";
16971
- import { resolve as resolve18 } from "path";
17025
+ import { resolve as resolve19 } from "path";
16972
17026
 
16973
17027
  class LspClient {
16974
17028
  process = null;
@@ -16984,7 +17038,7 @@ class LspClient {
16984
17038
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
16985
17039
  try {
16986
17040
  await this.startServer(config, baseDir);
16987
- const rootUri = this.pathToUri(resolve18(baseDir));
17041
+ const rootUri = this.pathToUri(resolve19(baseDir));
16988
17042
  const initResult = await this.sendRequest("initialize", {
16989
17043
  processId: process.pid,
16990
17044
  rootUri,
@@ -16993,11 +17047,11 @@ class LspClient {
16993
17047
  }, timeout);
16994
17048
  this.initialized = true;
16995
17049
  this.sendNotification("initialized", {});
16996
- const uri = this.pathToUri(resolve18(filePath));
17050
+ const uri = this.pathToUri(resolve19(filePath));
16997
17051
  const fs2 = await import("fs");
16998
17052
  const content = fs2.readFileSync(filePath, "utf-8");
16999
- const diagPromise = new Promise((resolve19) => {
17000
- this.diagnosticsResolve = resolve19;
17053
+ const diagPromise = new Promise((resolve20) => {
17054
+ this.diagnosticsResolve = resolve20;
17001
17055
  this.diagnostics = [];
17002
17056
  this.diagnosticsTimer = setTimeout(() => {
17003
17057
  if (this.diagnosticsResolve) {
@@ -17015,8 +17069,8 @@ class LspClient {
17015
17069
  }
17016
17070
  });
17017
17071
  return await diagPromise;
17018
- } catch {
17019
- return [];
17072
+ } catch (e) {
17073
+ throw e instanceof Error ? e : new Error(String(e));
17020
17074
  } finally {
17021
17075
  await this.shutdown();
17022
17076
  }
@@ -17029,7 +17083,7 @@ class LspClient {
17029
17083
  throw new Error(`${config.command} not found in PATH`);
17030
17084
  }
17031
17085
  }
17032
- return new Promise((resolve19, reject) => {
17086
+ return new Promise((resolve20, reject) => {
17033
17087
  const args = config.args ?? [];
17034
17088
  const proc = spawn5(config.command, args, {
17035
17089
  stdio: ["pipe", "pipe", "pipe"],
@@ -17042,7 +17096,7 @@ class LspClient {
17042
17096
  });
17043
17097
  proc.stderr.on("data", () => {});
17044
17098
  proc.once("spawn", () => {
17045
- resolve19();
17099
+ resolve20();
17046
17100
  });
17047
17101
  this.process = proc;
17048
17102
  setTimeout(() => {
@@ -17119,9 +17173,9 @@ class LspClient {
17119
17173
  }
17120
17174
  }
17121
17175
  sendRequest(method, params, timeout) {
17122
- return new Promise((resolve19, reject) => {
17176
+ return new Promise((resolve20, reject) => {
17123
17177
  const id = ++this.requestId;
17124
- this.pending.set(id, { resolve: resolve19, reject });
17178
+ this.pending.set(id, { resolve: resolve20, reject });
17125
17179
  const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
17126
17180
  this.write(message);
17127
17181
  setTimeout(() => {
@@ -17200,7 +17254,7 @@ var init_client2 = () => {};
17200
17254
 
17201
17255
  // src/modules/lsp/module.ts
17202
17256
  import { existsSync as existsSync34 } from "fs";
17203
- import { resolve as resolve19 } from "path";
17257
+ import { resolve as resolve20 } from "path";
17204
17258
 
17205
17259
  class LspModule {
17206
17260
  name = "lsp";
@@ -17224,7 +17278,7 @@ class LspModule {
17224
17278
  const filePath = String(call.arguments.path ?? "");
17225
17279
  if (!filePath)
17226
17280
  return;
17227
- const fullPath = resolve19(_ctx.baseDir, filePath);
17281
+ const fullPath = resolve20(_ctx.baseDir, filePath);
17228
17282
  if (!existsSync34(fullPath))
17229
17283
  return;
17230
17284
  const serverConfig = getServerForFile(fullPath, self.config);
@@ -17248,7 +17302,15 @@ ${items}`;
17248
17302
  [LSP warnings]:
17249
17303
  ${items}`;
17250
17304
  }
17251
- } catch {}
17305
+ } catch (e) {
17306
+ const msg = e instanceof Error ? e.message : String(e);
17307
+ try {
17308
+ _ctx.logger?.warn(`LSP check failed for ${filePath}: ${msg}`);
17309
+ } catch {}
17310
+ result.output += `
17311
+
17312
+ [LSP check failed]: ${msg}`;
17313
+ }
17252
17314
  }
17253
17315
  };
17254
17316
  }
@@ -17827,7 +17889,7 @@ __export(exports_bootstrap, {
17827
17889
  bootstrap: () => bootstrap
17828
17890
  });
17829
17891
  import { homedir as homedir11 } from "os";
17830
- import { join as join30, resolve as resolve20 } from "path";
17892
+ import { join as join30, resolve as resolve21 } from "path";
17831
17893
  import { existsSync as existsSync37, readFileSync as readFileSync24, writeFileSync as writeFileSync14 } from "fs";
17832
17894
  function buildSystemInfo(config, baseDir, profileCompressed) {
17833
17895
  const now = new Date().toISOString().replace("T", " ").slice(0, 19);
@@ -17897,7 +17959,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
17897
17959
  retry: config.retry,
17898
17960
  rateLimits: config.security?.rateLimits
17899
17961
  });
17900
- const baseDir = projectDir ? resolve20(projectDir) : process.cwd();
17962
+ const baseDir = projectDir ? resolve21(projectDir) : process.cwd();
17901
17963
  const projectMapCacheDir = join30(baseDir, ".mma");
17902
17964
  const indexerModule = new IndexerModule({
17903
17965
  baseDir,
@@ -18698,10 +18760,10 @@ function menuList(items) {
18698
18760
  console.log(l);
18699
18761
  }
18700
18762
  function ask(rl, question, defaultValue) {
18701
- return new Promise((resolve21) => {
18763
+ return new Promise((resolve22) => {
18702
18764
  const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
18703
18765
  rl.question(prompt, (answer) => {
18704
- resolve21(answer.trim() || defaultValue || "");
18766
+ resolve22(answer.trim() || defaultValue || "");
18705
18767
  });
18706
18768
  });
18707
18769
  }
@@ -26285,7 +26347,7 @@ var init_fact_checker = () => {};
26285
26347
  import { spawn as spawn6 } from "child_process";
26286
26348
  import { existsSync as existsSync41, mkdirSync as mkdirSync18, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
26287
26349
  import { platform as platform5 } from "os";
26288
- import { join as join35, resolve as resolve21, dirname as dirname8 } from "path";
26350
+ import { join as join35, resolve as resolve22, dirname as dirname8 } from "path";
26289
26351
  async function runScenario(scenario, opts) {
26290
26352
  if (scenario.mode === "skip") {
26291
26353
  return {
@@ -26382,8 +26444,8 @@ function resolveMmaEntry(mmaRoot) {
26382
26444
  }
26383
26445
  function findMmaRoot(fromDir) {
26384
26446
  const candidates = [
26385
- resolve21(fromDir, "..", "..", ".."),
26386
- resolve21(fromDir, "..")
26447
+ resolve22(fromDir, "..", "..", ".."),
26448
+ resolve22(fromDir, "..")
26387
26449
  ];
26388
26450
  for (const c of candidates) {
26389
26451
  if (existsSync41(join35(c, "package.json")))
@@ -26699,7 +26761,7 @@ function registerMmaCommands(ctx) {
26699
26761
  try {
26700
26762
  const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
26701
26763
  const { existsSync: existsSync45 } = await import("fs");
26702
- const { resolve: resolve22 } = await import("path");
26764
+ const { resolve: resolve23 } = await import("path");
26703
26765
  let dataUrl;
26704
26766
  let label;
26705
26767
  if (source.toLowerCase() === "clipboard") {
@@ -26717,7 +26779,7 @@ function registerMmaCommands(ctx) {
26717
26779
  dataUrl = result.dataUrl;
26718
26780
  label = source;
26719
26781
  } else {
26720
- const absPath = resolve22(process.cwd(), source);
26782
+ const absPath = resolve23(process.cwd(), source);
26721
26783
  if (!existsSync45(absPath)) {
26722
26784
  console.log(pc2.red(t("image.not_found", { path: source })));
26723
26785
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-models-agent",
3
- "version": "0.29.0",
3
+ "version": "0.29.2",
4
4
  "description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
5
5
  "type": "module",
6
6
  "bin": {