micro-models-agent 0.6.0 → 0.6.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 (44) hide show
  1. package/dist/agent/error-escalation.d.ts +1 -0
  2. package/dist/agent/error-escalation.d.ts.map +1 -1
  3. package/dist/agent/error-escalation.js +16 -3
  4. package/dist/agent/error-escalation.js.map +1 -1
  5. package/dist/agent/loop.d.ts.map +1 -1
  6. package/dist/agent/loop.js +177 -200
  7. package/dist/agent/loop.js.map +1 -1
  8. package/dist/agent/tool-executor.js +1 -1
  9. package/dist/agent/tool-executor.js.map +1 -1
  10. package/dist/agent/write-enforcer.d.ts +1 -1
  11. package/dist/agent/write-enforcer.d.ts.map +1 -1
  12. package/dist/agent/write-enforcer.js +4 -24
  13. package/dist/agent/write-enforcer.js.map +1 -1
  14. package/dist/config/config.d.ts.map +1 -1
  15. package/dist/config/config.js +2 -2
  16. package/dist/config/config.js.map +1 -1
  17. package/dist/tools/adapters/bash-to-fs.d.ts +20 -0
  18. package/dist/tools/adapters/bash-to-fs.d.ts.map +1 -0
  19. package/dist/tools/adapters/bash-to-fs.js +74 -0
  20. package/dist/tools/adapters/bash-to-fs.js.map +1 -0
  21. package/dist/tools/adapters/content-sanitizer.d.ts +13 -0
  22. package/dist/tools/adapters/content-sanitizer.d.ts.map +1 -0
  23. package/dist/tools/adapters/content-sanitizer.js +73 -0
  24. package/dist/tools/adapters/content-sanitizer.js.map +1 -0
  25. package/dist/tools/adapters/grounding-fixer.d.ts +15 -0
  26. package/dist/tools/adapters/grounding-fixer.d.ts.map +1 -0
  27. package/dist/tools/adapters/grounding-fixer.js +57 -0
  28. package/dist/tools/adapters/grounding-fixer.js.map +1 -0
  29. package/dist/tools/adapters/index.d.ts +6 -0
  30. package/dist/tools/adapters/index.d.ts.map +1 -0
  31. package/dist/tools/adapters/index.js +5 -0
  32. package/dist/tools/adapters/index.js.map +1 -0
  33. package/dist/tools/adapters/pipeline.d.ts +34 -0
  34. package/dist/tools/adapters/pipeline.d.ts.map +1 -0
  35. package/dist/tools/adapters/pipeline.js +58 -0
  36. package/dist/tools/adapters/pipeline.js.map +1 -0
  37. package/dist/tools/adapters/types.d.ts +46 -0
  38. package/dist/tools/adapters/types.d.ts.map +1 -0
  39. package/dist/tools/adapters/types.js +2 -0
  40. package/dist/tools/adapters/types.js.map +1 -0
  41. package/dist/tools/subagent.d.ts.map +1 -1
  42. package/dist/tools/subagent.js +36 -1
  43. package/dist/tools/subagent.js.map +1 -1
  44. package/package.json +1 -1
@@ -8,6 +8,7 @@ import type { OutputRenderer } from "../ui/renderer.js";
8
8
  */
9
9
  export declare class ErrorEscalator {
10
10
  consecutiveToolErrors: Map<string, number>;
11
+ lastToolArgs: Map<string, string>;
11
12
  totalToolErrors: number;
12
13
  specialistSpawnedFor: string | null;
13
14
  errorStrategy: "normal" | "subagent-hint" | "force-subagent";
@@ -1 +1 @@
1
- {"version":3,"file":"error-escalation.d.ts","sourceRoot":"","sources":["../../src/agent/error-escalation.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAgGxD;;;GAGG;AACH,qBAAa,cAAc;IACzB,qBAAqB,sBAA6B;IAClD,eAAe,SAAK;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3C,aAAa,EAAE,QAAQ,GAAG,eAAe,GAAG,gBAAgB,CAAY;IAExE,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQpC,KAAK,IAAI,IAAI;IAOP,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,EAAE,EACvB,YAAY,EAAE,MAAM,EAAE,EACtB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,GAAG,EACf,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;CAkGjC"}
1
+ {"version":3,"file":"error-escalation.d.ts","sourceRoot":"","sources":["../../src/agent/error-escalation.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAgGxD;;;GAGG;AACH,qBAAa,cAAc;IACzB,qBAAqB,sBAA6B;IAClD,YAAY,sBAA6B;IACzC,eAAe,SAAK;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3C,aAAa,EAAE,QAAQ,GAAG,eAAe,GAAG,gBAAgB,CAAY;IAExE,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWpC,KAAK,IAAI,IAAI;IAQP,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,EAAE,EACvB,YAAY,EAAE,MAAM,EAAE,EACtB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,GAAG,EACf,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;CA2GjC"}
@@ -55,6 +55,7 @@ async function spawnSpecialist(p) {
55
55
  */
56
56
  export class ErrorEscalator {
57
57
  consecutiveToolErrors = new Map();
58
+ lastToolArgs = new Map();
58
59
  totalToolErrors = 0;
59
60
  specialistSpawnedFor = null;
60
61
  errorStrategy = "normal";
@@ -63,10 +64,15 @@ export class ErrorEscalator {
63
64
  if (key.startsWith(toolName + "::"))
64
65
  this.consecutiveToolErrors.delete(key);
65
66
  }
67
+ for (const [key] of this.lastToolArgs) {
68
+ if (key.startsWith(toolName + "::"))
69
+ this.lastToolArgs.delete(key);
70
+ }
66
71
  this.specialistSpawnedFor = null;
67
72
  }
68
73
  reset() {
69
74
  this.consecutiveToolErrors.clear();
75
+ this.lastToolArgs.clear();
70
76
  this.totalToolErrors = 0;
71
77
  this.specialistSpawnedFor = null;
72
78
  this.errorStrategy = "normal";
@@ -77,6 +83,13 @@ export class ErrorEscalator {
77
83
  .slice(0, 100)
78
84
  .replace(/[A-Z]:\\[^\s"']+/gi, "<PATH>")
79
85
  .replace(/\/[^\s"']+/g, "<PATH>")}`;
86
+ // Если аргументы изменились — сбрасываем счётчик ошибок для этого ключа
87
+ const argsStr = JSON.stringify(toolArgs);
88
+ const prevArgs = this.lastToolArgs.get(errKey);
89
+ if (prevArgs !== undefined && prevArgs !== argsStr) {
90
+ this.consecutiveToolErrors.delete(errKey);
91
+ }
92
+ this.lastToolArgs.set(errKey, argsStr);
80
93
  const prevCount = this.consecutiveToolErrors.get(errKey) ?? 0;
81
94
  this.consecutiveToolErrors.set(errKey, prevCount + 1);
82
95
  const errorCount = prevCount + 1;
@@ -138,15 +151,15 @@ export class ErrorEscalator {
138
151
  if (!quiet)
139
152
  renderer?.writeWarning(` [strategy: subagent-hint after ${this.totalToolErrors} errors]`);
140
153
  }
141
- if (errorCount >= 4) {
154
+ if (errorCount >= 6) {
142
155
  const blockId = generateToolCallId();
143
156
  messages.push({
144
157
  role: "tool",
145
- content: `BLOCKED: Tool "${toolName}" failed ${errorCount} times. Temporarily blocked.`,
158
+ content: `BLOCKED: Tool "${toolName}" failed ${errorCount} times with the same error. Use subagent for a fresh context or try a different approach.`,
146
159
  tool_call_id: blockId,
147
160
  });
148
161
  if (!quiet)
149
- renderer?.writeWarning(` [blocked ${toolName}: too many repeated errors]`);
162
+ renderer?.writeWarning(` [blocked ${toolName}: ${errorCount} consecutive errors]`);
150
163
  return "block";
151
164
  }
152
165
  return "block";
@@ -1 +1 @@
1
- {"version":3,"file":"error-escalation.js","sourceRoot":"","sources":["../../src/agent/error-escalation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"error-escalation.js","sourceRoot":"","sources":["../../src/agent/error-escalation.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAcxD,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAqFlE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,cAAc,EAC/B,gBAAgB,CAAC,EAAE,WAAW,EAAE,EAChC,QAAQ,CAAC,EAAE,cAAc,EACzB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IACR,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,GACA,OAAO,CAAC,eAAe,CAAC,CAqsC1B"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAcxD,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAqFlE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,cAAc,EAC/B,gBAAgB,CAAC,EAAE,WAAW,EAAE,EAChC,QAAQ,CAAC,EAAE,cAAc,EACzB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IACR,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,GACA,OAAO,CAAC,eAAe,CAAC,CAiqC1B"}
@@ -1,6 +1,7 @@
1
- import { readFileSync, existsSync } from "node:fs";
1
+ import { readFileSync } from "node:fs";
2
2
  import { join, isAbsolute, relative } from "node:path";
3
3
  import { parseToolCalls } from "../tools/parse-tool-call.js";
4
+ import { AdapterPipeline, } from "../tools/adapters/index.js";
4
5
  import { parseFileRefs } from "../file-loader.js";
5
6
  import { ContextManager } from "./context.js";
6
7
  import { ModeRegistry } from "./modes/registry.js";
@@ -225,6 +226,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
225
226
  let consecutiveInvalidCalls = 0; // счётчик невалидных tool call подряд
226
227
  /** Счётчик бесполезных действий (list_dir, glob_search, todo list без результата) */
227
228
  let uselessActionCount = 0;
229
+ /** Сколько шагов подряд модель не вызывает инструменты */
230
+ let consecutiveNoToolSteps = 0;
228
231
  /** История последних чтений файлов для.detect повторных чтений */
229
232
  const recentReadFiles = [];
230
233
  /** История последних grep-запросов для detect повторных поисков */
@@ -263,6 +266,9 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
263
266
  ]);
264
267
  const errorEscalator = new ErrorEscalator();
265
268
  const writeEnforcer = new WriteEnforcer();
269
+ const adapterPipeline = new AdapterPipeline();
270
+ /** Отслеживание созданных/изменённых файлов за всю сессию */
271
+ const createdFiles = [];
266
272
  // Reset abort signal for this run
267
273
  if (renderer?.abortSignal === undefined) {
268
274
  // Renderer doesn't support abort — fine
@@ -281,7 +287,6 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
281
287
  break;
282
288
  }
283
289
  let skillsAutoLoaded = false;
284
- const createdFiles = []; // отслеживание созданных/изменённых файлов
285
290
  logDebug("LOOP", `step ${step + 1}/${maxSteps} starting, ${messages.length} messages`);
286
291
  // Инкрементальный подсчёт — считаем только новые сообщения (быстрее в ~100x на больших историях)
287
292
  if (step === 0) {
@@ -426,6 +431,36 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
426
431
  consecutiveEmpty = 0;
427
432
  messages.push({ role: "assistant", content: response });
428
433
  const { validCalls, invalidCalls } = parseToolCalls(response);
434
+ // ─── Adapter pipeline: чиним кривые вызовы маленьких моделей ──
435
+ const adapterLogs = [];
436
+ const adaptedCalls = [];
437
+ const adapterCtx = {
438
+ cwd: process.cwd(),
439
+ config,
440
+ responseText: response,
441
+ existingFiles: createdFiles,
442
+ };
443
+ for (const call of validCalls) {
444
+ const result = await adapterPipeline.process(call, adapterCtx);
445
+ if ("blocked" in result) {
446
+ invalidCalls.push(result.blocked);
447
+ if (!quiet)
448
+ renderer?.writeError(` [adapter blocked: ${result.blocked}]`);
449
+ }
450
+ else {
451
+ adaptedCalls.push(result.call);
452
+ adapterLogs.push(...result.logs);
453
+ }
454
+ }
455
+ // Заменяем validCalls на адаптированные
456
+ validCalls.length = 0;
457
+ validCalls.push(...adaptedCalls);
458
+ // Показываем логи адаптеров
459
+ if (adapterLogs.length > 0 && !quiet) {
460
+ for (const log of adapterLogs) {
461
+ renderer?.writeDim(` ${log}`);
462
+ }
463
+ }
429
464
  if (validCalls.length === 0) {
430
465
  if (invalidCalls.length > 0) {
431
466
  // If the response has substantial text outside tool_call tags,
@@ -469,141 +504,122 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
469
504
  continue;
470
505
  }
471
506
  }
472
- if (!skillsAutoLoaded) {
473
- const textOutside = response
474
- .replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "")
475
- .trim();
476
- const looksLikeDone = /done|готово|создан|выполн|completed|finished/i.test(textOutside);
477
- // Проверка: план с незавершёнными шагами — не даём агенту остановиться
478
- const pendingSteps = getPlanPendingCount();
479
- if (pendingSteps > 0 && looksLikeDone) {
480
- const planText = showPlan();
481
- const pendingLines = planText
482
- .split("\n")
483
- .filter((l) => l.includes("○") || l.includes("►"));
484
- const nextStep = pendingLines[0] || "";
485
- const nextStepMatch = nextStep.match(/\d+\.\s*(.+)/);
486
- const nextStepTitle = nextStepMatch ? nextStepMatch[1].trim() : "";
507
+ // ─── Определение завершения: только по поведению, не по тексту ──────
508
+ // Смотрим: были ли хоть какие-то вызовы инструментов за сессию.
509
+ // Сообщения с role:tool — это результаты выполненных инструментов
510
+ // (кроме invalid — они с tool_call_id вида "invalid-...").
511
+ const hasDoneAnyWork = messages.some((m) => m.role === "tool" &&
512
+ m.tool_call_id &&
513
+ !m.tool_call_id.startsWith("invalid-"));
514
+ const planStatus = showPlan();
515
+ const hasActivePlan = !planStatus.startsWith("No active plan");
516
+ const pendingSteps = getPlanPendingCount();
517
+ // === 1. Незавершённый план ===
518
+ if (hasActivePlan && pendingSteps > 0) {
519
+ const planText = planStatus;
520
+ const pendingLines = planText
521
+ .split("\n")
522
+ .filter((l) => l.includes("○") || l.includes("►"));
523
+ const nextStep = pendingLines[0] || "";
524
+ const nextStepMatch = nextStep.match(/\d+\.\s*(.+)/);
525
+ const nextStepTitle = nextStepMatch ? nextStepMatch[1].trim() : "";
526
+ if (!quiet) {
527
+ renderer?.writeWarning(` [plan has ${pendingSteps} incomplete steps — pushing agent to continue]`);
528
+ }
529
+ const nudge = nextStepTitle
530
+ ? `Plan is NOT complete! ${pendingSteps} step(s) remaining.\nNext step: ${nextStepTitle}\nUse subagent to execute this step, then mark done: plan({action:"done", step:N})`
531
+ : `Plan is NOT complete! ${pendingSteps} step(s) remaining:\n${pendingLines.join("\n")}\n\nContinue with the next step using subagent.`;
532
+ messages.push({ role: "user", content: nudge });
533
+ continuationNudges = 0;
534
+ continue;
535
+ }
536
+ // === 2. План завершён — аудит файлов ===
537
+ // Проверка по createdFiles: если план выполнен, но ни одного файла
538
+ // не создано — скорее всего модель галлюцинирует выполнение шагов.
539
+ if (hasActivePlan && pendingSteps === 0) {
540
+ if (createdFiles.length === 0) {
487
541
  if (!quiet) {
488
- renderer?.writeWarning(` [plan has ${pendingSteps} incomplete steps — pushing agent to continue]`);
542
+ renderer?.writeWarning(` [plan complete but no files created]`);
489
543
  }
490
- const nudge = nextStepTitle
491
- ? `Plan is NOT complete! ${pendingSteps} step(s) remaining.\nNext step: ${nextStepTitle}\nUse subagent to execute this step, then mark done: plan({action:"done", step:N})`
492
- : `Plan is NOT complete! ${pendingSteps} step(s) remaining:\n${pendingLines.join("\n")}\n\nContinue with the next step using subagent.`;
493
- messages.push({ role: "user", content: nudge });
544
+ messages.push({
545
+ role: "user",
546
+ content: `Plan claims all steps done, but no files were created via write_file or subagent. Either execute the steps properly or explain why files aren't needed.`,
547
+ });
494
548
  continuationNudges = 0;
495
549
  continue;
496
550
  }
497
- // Phase 4.1: Аудит файлов при завершении плана
498
- if (pendingSteps === 0 && looksLikeDone) {
499
- const plan = showPlan();
500
- if (!plan.startsWith("No active plan")) {
501
- // Извлекаем заголовки шагов и проверяем наличие файлов
502
- const stepLines = plan
503
- .split("\n")
504
- .filter((l) => l.match(/\d+\.\s/));
505
- const missingFiles = [];
506
- for (const line of stepLines) {
507
- const titleMatch = line.match(/\d+\.\s*(.+)/);
508
- if (!titleMatch)
509
- continue;
510
- const title = titleMatch[1].trim();
511
- // Извлекаем имя файла из заголовка (паттерн: "Create file.ext" или "file.ext")
512
- const fileMatch = title.match(/(?:Create|Создать|Add|Добавить)\s+(\S+\.(?:ts|tsx|js|jsx|json|html|css|md|yaml|yml))/i) ||
513
- title.match(/(\w+\.(?:ts|tsx|js|jsx|json|html|css|md|yaml|yml))/i);
514
- if (fileMatch) {
515
- const fileName = fileMatch[1];
516
- if (!existsSync(fileName) &&
517
- !createdFiles.some((f) => f.endsWith(fileName))) {
518
- missingFiles.push(fileName);
519
- }
520
- }
521
- }
522
- if (missingFiles.length > 0) {
523
- if (!quiet) {
524
- renderer?.writeWarning(` [plan complete but missing files: ${missingFiles.join(", ")}]`);
525
- }
526
- messages.push({
527
- role: "user",
528
- content: `Plan claims all steps done, but these files are missing: ${missingFiles.join(", ")}. Create them before finishing.`,
529
- });
530
- continuationNudges = 0;
531
- continue;
532
- }
533
- }
551
+ // Файлы созданы — выходим
552
+ return { messages };
553
+ }
554
+ // === 3. Нет активного плана ===
555
+ // Stuck detection (без изменений)
556
+ const isUselessAction = validCalls.some((c) => c.parsed.name === "list_dir" ||
557
+ c.parsed.name === "tree" ||
558
+ (c.parsed.name === "todo" && c.parsed.arguments?.action === "list") ||
559
+ (c.parsed.name === "question" && !c.parsed.arguments?.question));
560
+ let isRepeatedRead = false;
561
+ let isRepeatedSearch = false;
562
+ let isRepeatedQuestion = false;
563
+ let isRepeatedBash = false;
564
+ for (const c of validCalls) {
565
+ if (c.parsed.name === "read_file") {
566
+ const filePath = String(c.parsed.arguments?.path || "");
567
+ if (recentReadFiles.includes(filePath))
568
+ isRepeatedRead = true;
569
+ recentReadFiles.push(filePath);
570
+ if (recentReadFiles.length > 5)
571
+ recentReadFiles.shift();
534
572
  }
535
- // Stuck detection: если модель делает бесполезные действия
536
- const isUselessAction = validCalls.some((c) => c.parsed.name === "list_dir" ||
537
- c.parsed.name === "tree" ||
538
- (c.parsed.name === "todo" &&
539
- c.parsed.arguments?.action === "list") ||
540
- (c.parsed.name === "question" && !c.parsed.arguments?.question));
541
- // Phase 5.1: Расширенное detect — повторные чтения, поиски, вопросы и bash
542
- let isRepeatedRead = false;
543
- let isRepeatedSearch = false;
544
- let isRepeatedQuestion = false;
545
- let isRepeatedBash = false;
546
- for (const c of validCalls) {
547
- if (c.parsed.name === "read_file") {
548
- const filePath = String(c.parsed.arguments?.path || "");
549
- if (recentReadFiles.includes(filePath)) {
550
- isRepeatedRead = true;
551
- }
552
- recentReadFiles.push(filePath);
553
- if (recentReadFiles.length > 5)
554
- recentReadFiles.shift();
555
- }
556
- if (c.parsed.name === "grep_search") {
557
- const pattern = String(c.parsed.arguments?.pattern || "");
558
- if (recentGrepPatterns.includes(pattern)) {
559
- isRepeatedSearch = true;
560
- }
561
- recentGrepPatterns.push(pattern);
562
- if (recentGrepPatterns.length > 5)
563
- recentGrepPatterns.shift();
564
- }
565
- if (c.parsed.name === "question") {
566
- const questionText = String(c.parsed.arguments?.question || "");
567
- if (recentQuestions.includes(questionText)) {
568
- isRepeatedQuestion = true;
569
- }
570
- recentQuestions.push(questionText);
571
- if (recentQuestions.length > 3)
572
- recentQuestions.shift();
573
- }
574
- if (c.parsed.name === "bash") {
575
- const cmdText = String(c.parsed.arguments?.cmd || "");
576
- const normalized = cmdText.trim().replace(/\s+/g, " ");
577
- if (recentBashCommands.includes(normalized)) {
578
- isRepeatedBash = true;
579
- }
580
- recentBashCommands.push(normalized);
581
- if (recentBashCommands.length > 5)
582
- recentBashCommands.shift();
583
- }
573
+ if (c.parsed.name === "grep_search") {
574
+ const pattern = String(c.parsed.arguments?.pattern || "");
575
+ if (recentGrepPatterns.includes(pattern))
576
+ isRepeatedSearch = true;
577
+ recentGrepPatterns.push(pattern);
578
+ if (recentGrepPatterns.length > 5)
579
+ recentGrepPatterns.shift();
584
580
  }
585
- const isStuckAction = isUselessAction ||
586
- isRepeatedRead ||
587
- isRepeatedSearch ||
588
- isRepeatedQuestion ||
589
- isRepeatedBash;
590
- if (isStuckAction) {
591
- uselessActionCount++;
592
- if (uselessActionCount >= 3) {
593
- if (!quiet) {
594
- renderer?.writeWarning(` [agent stuck: ${uselessActionCount} useless actions]`);
595
- }
596
- clearPlan();
597
- messages.push({
598
- role: "user",
599
- content: `[System: You are stuck in a loop. STOP calling the same tool. The user already answered your question — use their answer and proceed with the task. Do NOT ask the same question again.]`,
600
- });
601
- uselessActionCount = 0;
602
- continuationNudges = 0;
603
- continue;
581
+ if (c.parsed.name === "question") {
582
+ const questionText = String(c.parsed.arguments?.question || "");
583
+ if (recentQuestions.includes(questionText))
584
+ isRepeatedQuestion = true;
585
+ recentQuestions.push(questionText);
586
+ if (recentQuestions.length > 3)
587
+ recentQuestions.shift();
588
+ }
589
+ if (c.parsed.name === "bash") {
590
+ const cmdText = String(c.parsed.arguments?.cmd || "");
591
+ const normalized = cmdText.trim().replace(/\s+/g, " ");
592
+ if (recentBashCommands.includes(normalized))
593
+ isRepeatedBash = true;
594
+ recentBashCommands.push(normalized);
595
+ if (recentBashCommands.length > 5)
596
+ recentBashCommands.shift();
597
+ }
598
+ }
599
+ const isStuckAction = isUselessAction ||
600
+ isRepeatedRead ||
601
+ isRepeatedSearch ||
602
+ isRepeatedQuestion ||
603
+ isRepeatedBash;
604
+ if (isStuckAction) {
605
+ uselessActionCount++;
606
+ if (uselessActionCount >= 3) {
607
+ if (!quiet) {
608
+ renderer?.writeWarning(` [agent stuck: ${uselessActionCount} useless actions]`);
604
609
  }
610
+ clearPlan();
611
+ messages.push({
612
+ role: "user",
613
+ content: `[System: You are stuck in a loop. STOP calling the same tool. The user already answered your question — use their answer and proceed with the task. Do NOT ask the same question again.]`,
614
+ });
615
+ uselessActionCount = 0;
616
+ continuationNudges = 0;
617
+ continue;
605
618
  }
606
- else if (validCalls.some((c) => c.parsed.name === "subagent" ||
619
+ }
620
+ else {
621
+ consecutiveNoToolSteps = 0;
622
+ if (validCalls.some((c) => c.parsed.name === "subagent" ||
607
623
  c.parsed.name === "write_file" ||
608
624
  c.parsed.name === "bash")) {
609
625
  uselessActionCount = 0;
@@ -612,21 +628,44 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
612
628
  recentQuestions.length = 0;
613
629
  recentBashCommands.length = 0;
614
630
  }
615
- // Если модель claim-ит done но есть созданные файлы — напоминаем о полноте
616
- if (createdFiles.length > 0 && looksLikeDone) {
631
+ }
632
+ // === 4. Выход: решение по поведению, а не по тексту ===
633
+ // 4a. Модель ничего не делала — поведенческий контроль
634
+ if (!hasDoneAnyWork) {
635
+ if (step > 0)
636
+ consecutiveNoToolSteps++;
637
+ if (consecutiveNoToolSteps >= 2) {
638
+ if (!quiet) {
639
+ renderer?.writeWarning(` [idle: ${consecutiveNoToolSteps} steps without any tool calls]`);
640
+ }
641
+ const idleSteps = consecutiveNoToolSteps;
642
+ consecutiveNoToolSteps = 0;
617
643
  messages.push({
618
644
  role: "user",
619
- content: `Files created: ${createdFiles.join(", ")}. Before finishing — verify all files are complete and connected to each other. If any file is missing or not imported, continue working.`,
645
+ content: `[System: ты уже ${idleSteps} шага подряд не вызываешь инструменты. Если задача требует создания/изменения файлов — вызови write_file или edit_file. Если задача уже выполнена — напиши результат пользователю.]`,
620
646
  });
621
647
  continuationNudges = 0;
622
648
  continue;
623
649
  }
650
+ // Первый ответ (step === 0) без инструментов — обычный диалог
624
651
  return { messages };
625
652
  }
626
- continue;
653
+ // 4b. Модель что-то делала — проверяем созданные файлы перед выходом
654
+ if (createdFiles.length > 0) {
655
+ messages.push({
656
+ role: "user",
657
+ content: `Files created: ${createdFiles.join(", ")}. Before finishing — verify all files are complete and connected to each other. If any file is missing or not imported, continue working.`,
658
+ });
659
+ continuationNudges = 0;
660
+ continue;
661
+ }
662
+ // 4c. Модель работала, но не создавала файлы (читала, искала и т.д.)
663
+ return { messages };
627
664
  }
628
- // ─── Grounding check ────────────────────────────────
629
- // Проверка наличия маркера @grounding[файл: path] перед tool_call
665
+ // ─── Grounding check (ослаблен, агент больше не блокирует) ──
666
+ // Раньше grounding strict mode гонял модель по retry-циклу.
667
+ // Теперь grounding-fixer адаптер сам добавляет маркеры —
668
+ // grounding check только предупреждает, никогда не блокирует.
630
669
  if (validCalls.length > 0) {
631
670
  const groundingCfg = getGroundingConfig(config);
632
671
  if (groundingCfg.enabled) {
@@ -635,76 +674,12 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
635
674
  const pathCalls = validCalls.filter((c) => c.parsed.arguments?.path);
636
675
  if (pathCalls.length > groundPaths.length) {
637
676
  consecutiveMissingGrounding++;
638
- const maxMissing = groundingCfg.maxMissingGrounding ?? 3;
639
- const maxRetries = groundingCfg.maxConsecutiveGroundingErrors ?? 5;
640
- if (consecutiveMissingGrounding >= maxMissing) {
641
- // CRITICAL — сброс счётчика + учёт retry
642
- consecutiveMissingGrounding = 0;
643
- groundingRetryCount++;
644
- if (groundingRetryCount > maxRetries) {
645
- // Полный bypass — выполняем tool_call без маркера
646
- if (!quiet)
647
- renderer?.writeWarning(` [grounding: exceeded limit, bypassing]`);
648
- groundingRetryCount = 0;
649
- messages.push({
650
- role: "user",
651
- content: `[Note: Grounding check disabled after repeated failures. Proceeding without marker. To re-enable, reset the agent.]`,
652
- });
653
- // fall through — tool call будет выполнен
654
- }
655
- else {
656
- if (!quiet)
657
- renderer?.writeError(` [grounding: ${groundingRetryCount}/${maxRetries} retries]`);
658
- messages.push({
659
- role: "user",
660
- content: `[CRITICAL: Output @grounding[файл: <path>] BEFORE each <tool_call>.\nExample:\n@grounding[файл: package.json]\n<tool_call>{"name":"read_file","arguments":{"path":"package.json"}}</tool_call>]`,
661
- });
662
- continue;
663
- }
664
- }
665
- else if (groundingCfg.strictMode) {
666
- if (!quiet)
667
- renderer?.writeError(` [grounding: strict mode, retrying]`);
668
- messages.push({
669
- role: "user",
670
- content: `[Grounding Error: Missing @grounding[файл: path] marker. Output the marker BEFORE each <tool_call>. Example:\n@grounding[файл: package.json]\n<tool_call>{"name":"read_file","arguments":{"path":"package.json"}}</tool_call>]`,
671
- });
672
- continue;
673
- }
674
- else {
675
- if (!quiet)
676
- renderer?.writeDim(` [grounding: missing marker, reminder ${consecutiveMissingGrounding}/${maxMissing}]`);
677
- messages.push({
678
- role: "user",
679
- content: `[Reminder: Include @grounding[файл: path] marker before each <tool_call> to prevent path hallucinations. Example:\n@grounding[файл: package.json]\n<tool_call>{"name":"read_file","arguments":{"path":"package.json"}}</tool_call>]`,
680
- });
681
- continue;
682
- }
677
+ if (!quiet)
678
+ renderer?.writeDim(` [grounding: ${consecutiveMissingGrounding} call(s) without marker — adapter handled]`);
683
679
  }
684
680
  else {
685
681
  consecutiveMissingGrounding = 0;
686
682
  groundingRetryCount = 0;
687
- // Проверка соответствия пути в маркере и в аргументах tool_call
688
- for (let i = 0; i < pathCalls.length && i < groundPaths.length; i++) {
689
- const callPath = String(pathCalls[i].parsed.arguments.path || "")
690
- .replace(/\\/g, "/")
691
- .replace(/^\.\//, "");
692
- const groundPath = groundPaths[i]
693
- .replace(/\\/g, "/")
694
- .replace(/^\.\//, "");
695
- if (callPath &&
696
- groundPath &&
697
- !callPath.endsWith(groundPath) &&
698
- !groundPath.endsWith(callPath)) {
699
- if (!quiet)
700
- renderer?.writeWarning(` [grounding: path mismatch — marker says "${groundPath}", tool says "${callPath}"]`);
701
- messages.push({
702
- role: "user",
703
- content: `[Warning: Grounding marker path "${groundPath}" doesn't match tool call path "${callPath}". Fix the marker to match the actual path.]`,
704
- });
705
- break;
706
- }
707
- }
708
683
  }
709
684
  }
710
685
  }
@@ -1033,6 +1008,8 @@ export async function runAgentLoop(provider, registry, prompt, contextManager, p
1033
1008
  }
1034
1009
  }
1035
1010
  }
1011
+ // Любой выполненный tool call сбрасывает счётчик бездействия
1012
+ consecutiveNoToolSteps = 0;
1036
1013
  messages.push({
1037
1014
  role: "tool",
1038
1015
  content: truncateOutput(parsed.name, toolOutput, toolExitCode),