omnius 1.0.454 → 1.0.456

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.
package/dist/index.js CHANGED
@@ -14238,6 +14238,66 @@ function findMatchOffsets(haystack, needle) {
14238
14238
  function replaceAllOccurrences(haystack, needle, replacement) {
14239
14239
  return haystack.split(needle).join(replacement);
14240
14240
  }
14241
+ function buildMissingOldStringLlmContent(input) {
14242
+ const lines = [
14243
+ "[FILE_EDIT_OLD_STRING_NOT_FOUND]",
14244
+ `path=${input.path}`,
14245
+ "The attempted old_string was absent from the current file.",
14246
+ "Do not copy diagnostic gutters into old_string. Ignore line numbers, leading '>' markers, and the '|' separator.",
14247
+ "",
14248
+ "attempted_old_string:",
14249
+ fencedText(input.attemptedOldString)
14250
+ ];
14251
+ if (input.snippetContent) {
14252
+ const range = input.snippetStartLine && input.snippetEndLine && input.totalLines ? `lines ${input.snippetStartLine}-${input.snippetEndLine} of ${input.totalLines}` : "closest current text";
14253
+ lines.push("", `copyable_current_text_without_gutters (${range}):`, fencedText(stripDiagnosticGutters(input.snippetContent)), "", "Next action: build a new file_edit old_string only from the copyable_current_text_without_gutters block, or file_read the target if more context is needed.");
14254
+ } else {
14255
+ lines.push("", "Next action: file_read the target, then build old_string from exact current file content.");
14256
+ }
14257
+ lines.push("", "Do not use shell text-rewrite commands as a workaround for this edit failure; keep the change tracked through file_edit, batch_edit, file_patch, or file_write.");
14258
+ if (input.newString.length > 0) {
14259
+ lines.push("", "desired_new_string:", fencedText(input.newString));
14260
+ }
14261
+ return lines.join("\n");
14262
+ }
14263
+ function buildAmbiguousOldStringLlmContent(input) {
14264
+ const replaceAllArgs = {
14265
+ path: input.path,
14266
+ old_string: input.oldString,
14267
+ new_string: input.newString,
14268
+ replace_all: true
14269
+ };
14270
+ return [
14271
+ "[FILE_EDIT_AMBIGUOUS_OLD_STRING]",
14272
+ `path=${input.path}`,
14273
+ `old_string matched ${input.occurrences} locations at lines ${input.matchLines.join(", ")}.`,
14274
+ "Do not retry the same old_string without either replace_all=true or additional unique surrounding context.",
14275
+ "Do not copy diagnostic gutters into old_string. Ignore line numbers, leading '>' markers, and the '|' separator.",
14276
+ "",
14277
+ "valid_next_action_replace_all_if_every_match_should_change_identically:",
14278
+ fencedJson(replaceAllArgs),
14279
+ "",
14280
+ "valid_next_action_single_location:",
14281
+ "Use file_edit with an old_string that includes unique surrounding current file text from exactly one match below.",
14282
+ "",
14283
+ "copyable_match_contexts_without_gutters:",
14284
+ fencedText(stripDiagnosticGutters(input.snippets).trim()),
14285
+ "",
14286
+ "Do not use shell text-rewrite commands as a workaround for this ambiguity; keep the change tracked through file_edit, batch_edit, file_patch, or file_write."
14287
+ ].join("\n");
14288
+ }
14289
+ function stripDiagnosticGutters(text2) {
14290
+ return text2.split("\n").map((line) => {
14291
+ const match = line.match(/^[ >]\s*\d+\s\|\s?(.*)$/);
14292
+ return match ? match[1] ?? "" : line;
14293
+ }).join("\n");
14294
+ }
14295
+ function fencedText(value2) {
14296
+ return ["```text", value2, "```"].join("\n");
14297
+ }
14298
+ function fencedJson(value2) {
14299
+ return ["```json", JSON.stringify(value2, null, 2), "```"].join("\n");
14300
+ }
14241
14301
  var FileEditTool;
14242
14302
  var init_file_edit = __esm({
14243
14303
  "packages/execution/dist/tools/file-edit.js"() {
@@ -14394,6 +14454,15 @@ Use the EXACT current content above to construct a working old_string. Do not re
14394
14454
  success: false,
14395
14455
  output: "",
14396
14456
  error: errorMsg,
14457
+ llmContent: buildMissingOldStringLlmContent({
14458
+ path: filePath,
14459
+ attemptedOldString: oldString,
14460
+ newString,
14461
+ snippetContent: snippet?.content ?? null,
14462
+ snippetStartLine: snippet?.startLine ?? null,
14463
+ snippetEndLine: snippet?.endLine ?? null,
14464
+ totalLines: snippet?.totalLines ?? null
14465
+ }),
14397
14466
  durationMs: performance.now() - start2,
14398
14467
  mutated: false,
14399
14468
  mutatedFiles: [],
@@ -14417,6 +14486,14 @@ ${s2.content}`;
14417
14486
  ${snippets}
14418
14487
 
14419
14488
  Add UNIQUE surrounding context (a function name, distinctive comment, or unique line above/below) to disambiguate. Or set replace_all=true if you want all ${occurrences} occurrences replaced identically.`,
14489
+ llmContent: buildAmbiguousOldStringLlmContent({
14490
+ path: filePath,
14491
+ oldString,
14492
+ newString,
14493
+ occurrences,
14494
+ matchLines,
14495
+ snippets
14496
+ }),
14420
14497
  durationMs: performance.now() - start2,
14421
14498
  mutated: false,
14422
14499
  mutatedFiles: [],
@@ -566026,12 +566103,12 @@ function buildCriticGuidanceMessage(call, hits, opts = {}) {
566026
566103
  const cached = opts.cachedResult ? `
566027
566104
  Prior evidence preview:
566028
566105
  ${opts.cachedResult.slice(0, 700)}` : "";
566029
- const source = opts.adversaryFlag ? "The adversary recognized this exact tool call as already observed earlier." : `This is exact repeat #${hits} for the same ${call.tool} arguments.`;
566030
- return `[ADVERSARY GUIDANCE — non-blocking]
566106
+ const source = opts.adversaryFlag ? "The runtime recognized this exact tool call as already observed earlier." : `This is exact repeat #${hits} for the same ${call.tool} arguments.`;
566107
+ return `[RUNTIME EVIDENCE CACHE — non-blocking]
566031
566108
  Observation: ${source}
566032
566109
  Call: ${call.tool}(${argPreview})
566033
- Root cause hypothesis: the run is losing track of already-observed evidence, usually after path confusion, compaction, or an over-broad discovery loop.
566034
- Corrective action: let this call's result inform the next step once, then pivot to a concrete action.
566110
+ State: exact prior evidence exists for these arguments in the current state version.
566111
+ Next action contract: let this result inform the next step once, then pivot to a concrete action.
566035
566112
  Suggested next actions: edit/write the implicated file, run verification, read a different specific file, or complete with evidence. Prefer not to repeat this exact call again unless the filesystem, browser, or page state changed.${cached}`;
566036
566113
  }
566037
566114
  function buildCachedResultEnvelope(result) {
@@ -570813,7 +570890,7 @@ ${fileLines}`);
570813
570890
  const counts = { ok: 0, failed: 0, blocked: 0 };
570814
570891
  for (const ev of events) {
570815
570892
  const preview = ev.outputPreview ?? "";
570816
- if (preview.includes("[FOCUS SUPERVISOR BLOCK]") || preview.includes("[STOP RE-RUNNING") || preview.includes("[REPEAT GATE")) {
570893
+ if (preview.includes("[FOCUS SUPERVISOR BLOCK]") || preview.includes("[STOP RE-RUNNING") || preview.includes("[CACHED VERIFIER FAILURE") || preview.includes("[REPEAT GATE")) {
570817
570894
  counts.blocked++;
570818
570895
  } else if (ev.success === false) {
570819
570896
  counts.failed++;
@@ -573775,6 +573852,10 @@ function adversarySystemPrompt() {
573775
573852
  ].join("\n");
573776
573853
  }
573777
573854
  function buildObservationPrompt(obs, recentLedger) {
573855
+ const stateDigest = obs.stateDigest ? `
573856
+ Runner world/progress digest:
573857
+ ${obs.stateDigest.slice(0, 1800)}
573858
+ ` : "";
573778
573859
  const outcomes = obs.recentToolOutcomes.slice(-8).map((o2) => {
573779
573860
  const target = o2.path ? ` path=${o2.path}` : "";
573780
573861
  const evidence = o2.evidence ? ` | evidence: ${o2.evidence.slice(0, 220)}` : "";
@@ -573788,6 +573869,8 @@ function buildObservationPrompt(obs, recentLedger) {
573788
573869
  `This is a loop. Reason about WHY — for THIS specific call, not generically.`,
573789
573870
  ls2.alreadyHave ? `Evidence the agent ALREADY obtained from a prior identical call:
573790
573871
  ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeated call.)`,
573872
+ "",
573873
+ stateDigest.trim(),
573791
573874
  "",
573792
573875
  "Agent's latest message:",
573793
573876
  obs.assistantText.slice(0, 1200) || "(empty)",
@@ -573809,6 +573892,8 @@ ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeate
573809
573892
  "",
573810
573893
  "A repeating identical error means the CHANGE ITSELF is wrong — an unsupported attribute/option, a wrong API/signature, or a missing prerequisite — NOT a value the agent hasn't guessed yet.",
573811
573894
  "",
573895
+ stateDigest.trim(),
573896
+ "",
573812
573897
  "Agent's latest message:",
573813
573898
  obs.assistantText.slice(0, 900) || "(empty)",
573814
573899
  "",
@@ -573819,12 +573904,32 @@ ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeate
573819
573904
  "Return ONLY the JSON object."
573820
573905
  ].join("\n");
573821
573906
  }
573907
+ if (obs.diagnosticSignal) {
573908
+ return [
573909
+ `The runtime queued a diagnostic trigger: ${obs.diagnosticSignal}.`,
573910
+ "This trigger is only a routing signal. Do NOT trust it as a verdict; classify from the world/progress digest and tool evidence.",
573911
+ stateDigest.trim(),
573912
+ "",
573913
+ "Agent's latest message:",
573914
+ obs.assistantText.slice(0, 1600) || "(empty)",
573915
+ "",
573916
+ "Recent tool outcomes:",
573917
+ outcomes || " (none)",
573918
+ priorDoubts ? `
573919
+ Unresolved doubts you raised earlier:
573920
+ ${priorDoubts}` : "",
573921
+ "",
573922
+ "Decide whether there is a real contradiction, stale-state action, unproven claim, or no issue. Return ONLY the JSON object."
573923
+ ].filter((line) => line !== "").join("\n");
573924
+ }
573822
573925
  return [
573823
573926
  obs.claimsCompletion ? "The agent is asserting COMPLETION this turn." : "The agent produced a progress/success-flavored claim this turn.",
573824
573927
  "",
573825
573928
  "Agent's latest message:",
573826
573929
  obs.assistantText.slice(0, 1800) || "(empty)",
573827
573930
  "",
573931
+ stateDigest.trim(),
573932
+ "",
573828
573933
  "Recent tool outcomes (evidence available):",
573829
573934
  outcomes || " (none)",
573830
573935
  priorDoubts ? `
@@ -573959,14 +574064,16 @@ var init_adversaryStream = __esm({
573959
574064
  return true;
573960
574065
  if (obs.failingApproach)
573961
574066
  return true;
574067
+ if (obs.diagnosticSignal)
574068
+ return true;
573962
574069
  return SUCCESS_LANGUAGE.test(obs.assistantText);
573963
574070
  }
573964
574071
  /** Ingest an observation. Replaces any prior un-audited pending observation. */
573965
574072
  observe(obs) {
573966
574073
  if (!this.shouldAudit(obs))
573967
574074
  return;
573968
- const loopKey = obs.loopSignal ? `loop:${obs.loopSignal.tool}:${obs.loopSignal.target}:${obs.loopSignal.count}` : obs.failingApproach ? `fail:${obs.failingApproach.count}:${obs.failingApproach.sample.slice(0, 60)}` : "";
573969
- const sig = `${obs.turn}:${loopKey}:${obs.assistantText.slice(0, 200)}`;
574075
+ const loopKey = obs.loopSignal ? `loop:${obs.loopSignal.tool}:${obs.loopSignal.target}:${obs.loopSignal.count}` : obs.failingApproach ? `fail:${obs.failingApproach.count}:${obs.failingApproach.sample.slice(0, 60)}` : obs.diagnosticSignal ? `diag:${obs.diagnosticSignal}` : "";
574076
+ const sig = `${obs.turn}:${loopKey}:${obs.assistantText.slice(0, 200)}:${obs.stateDigest?.slice(0, 120) ?? ""}`;
573970
574077
  if (sig === this.lastAuditedSignature)
573971
574078
  return;
573972
574079
  this.pending = obs;
@@ -574783,8 +574890,13 @@ function violatesDirective(directive, input) {
574783
574890
  if (directive.requiredNextAction !== "update_todos") {
574784
574891
  if (directive.forbiddenActionFamilies.includes(family))
574785
574892
  return true;
574786
- if (directive.forbiddenActionFamilies.includes(input.toolName))
574893
+ if (directive.forbiddenActionFamilies.includes(input.toolName)) {
574894
+ if (input.toolName === "shell" && input.isReadLike)
574895
+ return false;
574896
+ if (input.toolName === "shell" && input.shellLikelyMutatesFilesystem)
574897
+ return false;
574787
574898
  return true;
574899
+ }
574788
574900
  }
574789
574901
  switch (directive.requiredNextAction) {
574790
574902
  case "update_todos":
@@ -574797,6 +574909,12 @@ function violatesDirective(directive, input) {
574797
574909
  return !(input.toolName === "todo_write" || isEditTool(input.toolName));
574798
574910
  case "read_authoritative_target":
574799
574911
  return !(isEvidenceGatheringTool(input.toolName) || input.isReadLike);
574912
+ case "disambiguate_edit_match":
574913
+ if (isEditTool(input.toolName))
574914
+ return false;
574915
+ if (isEvidenceGatheringTool(input.toolName) || input.isReadLike)
574916
+ return false;
574917
+ return true;
574800
574918
  case "run_verification":
574801
574919
  return input.toolName !== "shell";
574802
574920
  case "report_blocked":
@@ -574812,6 +574930,9 @@ function violatesDirective(directive, input) {
574812
574930
  return false;
574813
574931
  }
574814
574932
  }
574933
+ function forbiddenCachedEvidenceFamilies(input, family) {
574934
+ return [family];
574935
+ }
574815
574936
  function normalizeCompletionStatus(value2) {
574816
574937
  if (typeof value2 !== "string")
574817
574938
  return null;
@@ -574874,7 +574995,7 @@ function cleanFailureSample(text2) {
574874
574995
  }
574875
574996
  function failureErrorClass(text2) {
574876
574997
  const normalized = String(text2 || "").toLowerCase();
574877
- if (/ambiguous|multiple occurrences|matches more than once/.test(normalized)) {
574998
+ if (/ambiguous|not unique|multiple occurrences|matches more than once|found \d+ occurrences|has \d+ matches/.test(normalized)) {
574878
574999
  return "stale_ambiguous_target";
574879
575000
  }
574880
575001
  if (/expected[_ -]?hash|hash mismatch|stale hash|beforehash|afterhash/.test(normalized)) {
@@ -574928,13 +575049,15 @@ function editTargetDescriptor(toolName, args) {
574928
575049
  "expected_old_string",
574929
575050
  "expectedHash",
574930
575051
  "expected_hash",
575052
+ "replace_all",
575053
+ "replaceAll",
574931
575054
  "mode",
574932
575055
  "offset",
574933
575056
  "limit",
574934
575057
  "start_line"
574935
575058
  ]) {
574936
575059
  const value2 = args[key];
574937
- if (typeof value2 === "string" || typeof value2 === "number") {
575060
+ if (typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean") {
574938
575061
  parts.push(`${key}=${String(value2)}`);
574939
575062
  }
574940
575063
  }
@@ -574946,7 +575069,8 @@ function editTargetDescriptor(toolName, args) {
574946
575069
  const rec = edit;
574947
575070
  const editPath = rec["path"] ?? rec["file"] ?? rec["filePath"] ?? rec["file_path"];
574948
575071
  const target = rec["old_string"] ?? rec["oldString"] ?? rec["oldText"] ?? rec["search"] ?? rec["expected_old_string"] ?? rec["offset"] ?? rec["start_line"] ?? "";
574949
- parts.push(`${String(editPath ?? "")}=${String(target ?? "")}`);
575072
+ const replaceAll = rec["replace_all"] === true || rec["replaceAll"] === true;
575073
+ parts.push(`${String(editPath ?? "")}=${String(target ?? "")}${replaceAll ? ":replace_all=true" : ""}`);
574950
575074
  }
574951
575075
  }
574952
575076
  if (parts.length === 0 && toolName === "file_patch") {
@@ -575141,7 +575265,7 @@ var init_focusSupervisor = __esm({
575141
575265
  state,
575142
575266
  reason: input.cachedResultFailed ? `cached failed ${input.toolName} evidence already exists` : `duplicate ${input.toolName} call has cached evidence`,
575143
575267
  requiredNextAction: "use_cached_evidence",
575144
- forbiddenActionFamilies: [family]
575268
+ forbiddenActionFamilies: forbiddenCachedEvidenceFamilies(input, family)
575145
575269
  });
575146
575270
  if (strict && !advisoryOnly && (input.cachedResultFailed || duplicateHitCount >= hitLimit)) {
575147
575271
  return this.block(directive, [
@@ -575201,7 +575325,7 @@ var init_focusSupervisor = __esm({
575201
575325
  return;
575202
575326
  }
575203
575327
  if (input.toolName === "file_read" && input.success) {
575204
- if (this.directive?.requiredNextAction === "read_authoritative_target" || this.directive?.requiredNextAction === "use_cached_evidence") {
575328
+ if (this.directive?.requiredNextAction === "read_authoritative_target") {
575205
575329
  this.clearSatisfiedDirective("authoritative evidence refreshed", input.turn);
575206
575330
  }
575207
575331
  return;
@@ -575209,8 +575333,10 @@ var init_focusSupervisor = __esm({
575209
575333
  if (input.toolName === "shell" && input.success) {
575210
575334
  if (this.directive?.requiredNextAction === "run_verification") {
575211
575335
  this.clearSatisfiedDirective("verification ran successfully", input.turn);
575212
- } else if (input.isReadLike && (this.directive?.requiredNextAction === "read_authoritative_target" || this.directive?.requiredNextAction === "use_cached_evidence")) {
575336
+ } else if (input.isReadLike && this.directive?.requiredNextAction === "read_authoritative_target") {
575213
575337
  this.clearSatisfiedDirective("authoritative evidence read via shell", input.turn);
575338
+ } else if (!input.isReadLike && this.directive?.requiredNextAction === "use_cached_evidence") {
575339
+ this.clearSatisfiedDirective("distinct shell action executed after cached evidence", input.turn);
575214
575340
  }
575215
575341
  return;
575216
575342
  }
@@ -575226,26 +575352,29 @@ var init_focusSupervisor = __esm({
575226
575352
  };
575227
575353
  this.failureFamilies.set(family, next);
575228
575354
  if (next.count >= 2) {
575355
+ const ambiguousEditFailure = isEditTool(input.toolName) && next.errorClass === "stale_ambiguous_target";
575229
575356
  const staleEditFailure = isEditTool(input.toolName) && next.errorClass.startsWith("stale_");
575230
- const forbidden = staleEditFailure ? uniqueLimited([
575357
+ const forbidden = staleEditFailure ? ambiguousEditFailure ? [actionFamily(input.toolName, input.args)] : uniqueLimited([
575231
575358
  actionFamily(input.toolName, input.args),
575232
575359
  input.toolName
575233
- ]) : [actionFamily(input.toolName, input.args)];
575360
+ ]) : input.toolName === "shell" ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)];
575234
575361
  this.setDirective({
575235
575362
  turn: input.turn,
575236
575363
  state: "forced_replan",
575237
575364
  reason: `same ${input.toolName} failure family repeated ${next.count} times: ${next.sample}`,
575238
- requiredNextAction: staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "edit_different_target" : "update_todos",
575365
+ requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "use_cached_evidence" : "update_todos",
575239
575366
  forbiddenActionFamilies: forbidden
575240
575367
  });
575241
575368
  } else if (next.count === 1 && !this.directive) {
575369
+ const ambiguousEditFailure = isEditTool(input.toolName) && next.errorClass === "stale_ambiguous_target";
575242
575370
  const staleEditFailure = isEditTool(input.toolName) && next.errorClass.startsWith("stale_");
575371
+ const shellFailure = input.toolName === "shell";
575243
575372
  this.setDirective({
575244
575373
  turn: input.turn,
575245
- state: "warn",
575374
+ state: shellFailure ? "cached_evidence" : "warn",
575246
575375
  reason: `first ${input.toolName} failure (${next.errorClass}): ${next.sample}`,
575247
- requiredNextAction: staleEditFailure ? "read_authoritative_target" : "update_todos",
575248
- forbiddenActionFamilies: [actionFamily(input.toolName, input.args)]
575376
+ requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : shellFailure ? "use_cached_evidence" : "update_todos",
575377
+ forbiddenActionFamilies: shellFailure ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)]
575249
575378
  });
575250
575379
  }
575251
575380
  }
@@ -577767,6 +577896,15 @@ function formatTodoReminderItems(todos, maxRows) {
577767
577896
  rows.push(`... +${todos.length - rows.length} more`);
577768
577897
  return rows.join("\n");
577769
577898
  }
577899
+ function selectHeadTailLines(lines, headCount, tailCount) {
577900
+ if (lines.length <= headCount + tailCount)
577901
+ return lines;
577902
+ return [
577903
+ ...lines.slice(0, headCount),
577904
+ `... [${lines.length - headCount - tailCount} middle line(s) omitted]`,
577905
+ ...lines.slice(-tailCount)
577906
+ ];
577907
+ }
577770
577908
  function stripThinkBlocks(s2) {
577771
577909
  if (!s2)
577772
577910
  return s2;
@@ -578918,8 +579056,11 @@ ${parts.join("\n")}
578918
579056
  case "update_todos":
578919
579057
  return "todo_write with a changed plan/status, or a real file edit if that is the active work";
578920
579058
  case "read_authoritative_target":
578921
- case "use_cached_evidence":
578922
579059
  return "use cached evidence or one authoritative file_read/read-only shell, then act";
579060
+ case "use_cached_evidence":
579061
+ return "do not rerun the blocked family; use the cached result to make a concrete edit, run a genuinely different diagnostic/verification command, or report incomplete";
579062
+ case "disambiguate_edit_match":
579063
+ return "file_edit or batch_edit with replace_all=true for identical global changes, or with a more unique old_string copied from current file context";
578923
579064
  case "edit_different_target":
578924
579065
  return "file_write, file_edit, batch_edit, or file_patch on a different supported target";
578925
579066
  case "run_verification":
@@ -582836,13 +582977,11 @@ ${contentPreview}
582836
582977
  for (const f2 of shown) {
582837
582978
  const argsRepr = JSON.stringify(f2.args).slice(0, 120);
582838
582979
  const failureText = [f2.error, f2.output].filter((v) => typeof v === "string" && v.trim().length > 0).join("\n");
582839
- const errFirst = failureText.split(/\n/)[0]?.slice(0, 200) || "(no error message)";
582840
- const errFull = failureText.slice(0, f2.tool === "file_edit" || f2.tool === "batch_edit" || f2.tool === "file_patch" ? 1200 : 600);
582980
+ const excerpt = this._formatRecentFailureExcerpt(f2.tool, failureText);
582841
582981
  lines.push(`• turn ${f2.turn} — ${f2.tool}(${argsRepr})`);
582842
- lines.push(` first line: ${errFirst}`);
582843
- if (errFull && errFull !== errFirst) {
582844
- const indented = errFull.split(/\n/).slice(0, 6).map((l2) => ` ${l2}`).join("\n");
582845
- lines.push(indented);
582982
+ lines.push(` first line: ${excerpt.firstLine}`);
582983
+ if (excerpt.body) {
582984
+ lines.push(excerpt.body);
582846
582985
  }
582847
582986
  }
582848
582987
  if (repeating.length > 0) {
@@ -582852,6 +582991,14 @@ ${contentPreview}
582852
582991
  lines.push(`(turn ${turn} — failures auto-expire after 10 turns; cleared on success or successful retry)`);
582853
582992
  return lines.join("\n");
582854
582993
  }
582994
+ _formatRecentFailureExcerpt(tool, text2) {
582995
+ const lines = String(text2 || "").replace(/\r/g, "").split("\n").map((line) => line.trimEnd());
582996
+ const firstLine = lines.find((line) => line.trim().length > 0)?.slice(0, 220) || "(no error message)";
582997
+ const maxChars = tool === "file_edit" || tool === "batch_edit" || tool === "file_patch" ? 1400 : tool === "shell" ? 2200 : 900;
582998
+ const selected = tool === "shell" ? selectHeadTailLines(lines, 8, 14) : lines.slice(0, 8);
582999
+ const body = selected.filter((line) => line.trim().length > 0).map((line) => ` ${line}`).join("\n").slice(0, maxChars);
583000
+ return { firstLine, body };
583001
+ }
582855
583002
  /**
582856
583003
  * Render a short, local-code-first nudge from the freshest failure signal.
582857
583004
  * This is intentionally not a web-search suggestion: most coding-agent loops
@@ -585487,16 +585634,14 @@ TASK: ${scrubbedTask}` : scrubbedTask;
585487
585634
  cwd: this._workingDirectory || process.cwd(),
585488
585635
  model: this._backendModelLabel(this.backend),
585489
585636
  onCritique: (critique2, sourceTurn) => {
585490
- if (completed || this._completionIncompleteVerification || this.aborted) {
585491
- return;
585492
- }
585493
- if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
585637
+ const runClosed = completed || this._completionIncompleteVerification || this.aborted;
585638
+ if (!runClosed && (this._adversaryMode === "skillcoach" || this._adversaryMode === "both")) {
585494
585639
  this.pendingUserMessages.push(AdversaryStream.formatInjection(critique2));
585495
585640
  }
585496
585641
  this.emit({
585497
585642
  type: "adversary_reaction",
585498
585643
  adversary: {
585499
- class: critique2.class === "false_failure" ? "false_failure" : critique2.class === "false_success" ? "false_success" : "guidance",
585644
+ class: critique2.class === "ok" ? "guidance" : critique2.class,
585500
585645
  shortText: critique2.shortText,
585501
585646
  confidence: critique2.confidence,
585502
585647
  details: AdversaryStream.formatInjection(critique2)
@@ -585517,6 +585662,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
585517
585662
  this._argCohorts.clear();
585518
585663
  this._adversaryRedundantSignals.clear();
585519
585664
  this._adversaryRecentFlags.clear();
585665
+ this._adversaryStateVersion = 0;
585520
585666
  this._lastTodoWriteTurn = -1;
585521
585667
  this._lastTodoReminderTurn = -1;
585522
585668
  let pendingConstraintWarnings = [];
@@ -588378,6 +588524,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
588378
588524
  ].includes(tc.name);
588379
588525
  const isStatefulBrowserTool = this._isStatefulBrowserTool(tc.name);
588380
588526
  const isReadLike = !isStatefulBrowserTool && (baseIsReadLike || tc.name === "shell" && this._isShellCommandReadOnly(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? ""));
588527
+ const shellLikelyMutatesFilesystem = tc.name === "shell" && this._shellCommandLikelyMutatesFilesystem(String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? ""));
588381
588528
  const adversaryRedundantSignal = this._adversaryRedundantSignals.has(toolFingerprint);
588382
588529
  if (adversaryRedundantSignal) {
588383
588530
  this._adversaryRedundantSignals.delete(toolFingerprint);
@@ -588453,13 +588600,10 @@ Read the current file if needed, make a different concrete edit, or move to veri
588453
588600
  }
588454
588601
  criticGuidance = criticDecision.guidanceMessage;
588455
588602
  this.emit({
588456
- type: "adversary_reaction",
588457
- adversary: {
588458
- class: "guidance",
588459
- shortText: `Adversary guidance for repeated ${tc.name} call`,
588460
- confidence: 0.9,
588461
- details: criticDecision.reason
588462
- },
588603
+ type: "status",
588604
+ content: `Runtime cache guidance for repeated ${tc.name} call: ${criticDecision.reason}`,
588605
+ toolName: tc.name,
588606
+ turn,
588463
588607
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
588464
588608
  });
588465
588609
  if (this._adversaryStream && criticDecision.hitNumber >= 2) {
@@ -588489,6 +588633,9 @@ Read the current file if needed, make a different concrete edit, or move to veri
588489
588633
  const _repeatGateMax = this._resolveRepeatGateMax();
588490
588634
  const isFailedShellRepeat = tc.name === "shell" && typeof _existingFp?.result === "string" && _existingFp.result.includes("status: failure");
588491
588635
  const repeatGateEligible = isReadLike || tc.name === "memory_write" || isFailedShellRepeat;
588636
+ const suppressSuccessfulShellCacheGuidance = tc.name === "shell" && !isFailedShellRepeat;
588637
+ if (suppressSuccessfulShellCacheGuidance)
588638
+ criticGuidance = null;
588492
588639
  if (isReadLike && this._evidenceLedger) {
588493
588640
  const readArgs = tc.arguments;
588494
588641
  const readPath2 = String(readArgs?.["path"] ?? readArgs?.["file"] ?? "");
@@ -588534,10 +588681,10 @@ Read the current file if needed, make a different concrete edit, or move to veri
588534
588681
  const cachedResult = this.sanitizeCachedToolResult(tc.name, _existingFp.result);
588535
588682
  repeatShortCircuit = isFailedShellRepeat ? {
588536
588683
  success: false,
588537
- output: `[STOP RE-RUNNING FAILED COMMAND — this exact shell command already failed and no real file mutation has invalidated that evidence. It was NOT executed again. Use the cached failure below to change approach, inspect a different target, or report incomplete.]
588684
+ output: `[CACHED VERIFIER FAILURE — this same shell command already produced debugging evidence and was not re-executed unchanged. Treat the cached transcript below as the current failure signal. Change the code/config, run a genuinely different verifier/diagnostic command, or report the blocker with this evidence.]
588538
588685
 
588539
588686
  ${cachedResult}`,
588540
- error: "Repeated failing shell command was not re-executed.",
588687
+ error: "Cached verifier failure reused; command was not re-executed unchanged.",
588541
588688
  runtimeAuthored: true
588542
588689
  } : {
588543
588690
  success: true,
@@ -588550,7 +588697,7 @@ ${cachedResult}`,
588550
588697
  this.emit({
588551
588698
  type: "status",
588552
588699
  toolName: tc.name,
588553
- content: repeatShortCircuit ? repeatShortCircuit.success ? `Exact repeat of ${tc.name} served from cache (hit #${criticDecision.hitNumber}); not re-executed` : `Exact repeat of ${tc.name} blocked after ${criticDecision.hitNumber} repeats; re-plan required` : `Adversary guidance emitted for ${tc.name}; tool call will still execute`,
588700
+ content: repeatShortCircuit ? repeatShortCircuit.success ? `Exact repeat of ${tc.name} served from cache (hit #${criticDecision.hitNumber}); not re-executed` : `Exact repeat of ${tc.name} blocked after ${criticDecision.hitNumber} repeats; re-plan required` : suppressSuccessfulShellCacheGuidance ? `Repeated successful shell command observed; re-executing because no state-independent verdict was made` : `Runtime cache guidance emitted for ${tc.name}; tool call will still execute`,
588554
588701
  turn,
588555
588702
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
588556
588703
  });
@@ -588564,6 +588711,7 @@ ${cachedResult}`,
588564
588711
  completionStatus: completionStatusFromTaskCompleteArgs(tc.arguments),
588565
588712
  fingerprint: toolFingerprint,
588566
588713
  isReadLike,
588714
+ shellLikelyMutatesFilesystem,
588567
588715
  cachedResult: focusCachedEntry?.result,
588568
588716
  cachedResultFailed: tc.name === "shell" && typeof focusCachedEntry?.result === "string" && focusCachedEntry.result.includes("status: failure"),
588569
588717
  duplicateHitCount: focusDuplicateHits,
@@ -588831,7 +588979,11 @@ Respond with EXACTLY this structure before your next tool call:
588831
588979
  }
588832
588980
  const shellFilesystemMutation = tc.name === "shell" && result.success === true && this._shellCommandLikelyMutatesFilesystem(String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? ""));
588833
588981
  const realFileMutation = this._isRealProjectMutation(tc.name, result);
588982
+ const observedStateMutation = realFileMutation || shellFilesystemMutation || result.success === true && result.mutated === true;
588834
588983
  const realMutationPaths = realFileMutation ? this._extractToolTargetPaths(tc.name, tc.arguments, result) : [];
588984
+ if (observedStateMutation) {
588985
+ this._markAdversaryObservedStateMutation();
588986
+ }
588835
588987
  if (realFileMutation && this._reg61PerpetualGateActive) {
588836
588988
  this._reg61PerpetualGateActive = false;
588837
588989
  this.emit({
@@ -589671,8 +589823,10 @@ Respond with EXACTLY this structure before your next tool call:
589671
589823
  }
589672
589824
  if (isFileMutation && recentToolResults.size > 0) {
589673
589825
  for (const key of Array.from(recentToolResults.keys())) {
589674
- if (key.startsWith("shell:"))
589826
+ if (key.startsWith("shell:")) {
589675
589827
  recentToolResults.delete(key);
589828
+ dedupHitCount.delete(key);
589829
+ }
589676
589830
  }
589677
589831
  }
589678
589832
  if (isFileMutation) {
@@ -589712,7 +589866,7 @@ Respond with EXACTLY this structure before your next tool call:
589712
589866
  fingerprint: toolFingerprint,
589713
589867
  args: tc.arguments,
589714
589868
  error: (result.error ?? "").slice(0, 600),
589715
- output: (result.output ?? "").slice(0, 1500),
589869
+ output: (result.output ?? "").slice(0, tc.name === "shell" ? 4e3 : 1500),
589716
589870
  turn
589717
589871
  });
589718
589872
  if (this._recentFailures.length > 8) {
@@ -593081,7 +593235,18 @@ ${folded}`);
593081
593235
  buildRecoveryGuidance(toolName, error, args) {
593082
593236
  const errLower = error.toLowerCase();
593083
593237
  if (toolName === "file_edit" || toolName === "batch_edit") {
593084
- if (errLower.includes("not found") || errLower.includes("old_string") || errLower.includes("no match")) {
593238
+ if (/ambiguous|not unique|multiple occurrences|matches more than once|found \d+ occurrences|has \d+ matches/.test(errLower)) {
593239
+ if (toolName === "batch_edit") {
593240
+ return `[RECOVERY] batch_edit failed because at least one old_string matched multiple locations.
593241
+ ACTION: retry the affected edit with replace_all=true if every match should change identically, or add unique surrounding current file text to that edit's old_string if only one location should change. Do not switch to shell sed/perl/python rewrites.`;
593242
+ }
593243
+ const filePath = String(args["path"] ?? "the file");
593244
+ const oldStr = String(args["old_string"] ?? "").slice(0, 120);
593245
+ return `[RECOVERY] file_edit failed because old_string is ambiguous in ${filePath}.
593246
+ Attempted old_string preview: "${oldStr}".
593247
+ ACTION: retry file_edit with replace_all=true if every match should change identically, or use an old_string containing unique surrounding current file text for exactly one location. Do not retry the same old_string without replace_all, and do not switch to shell sed/perl/python rewrites.`;
593248
+ }
593249
+ if (errLower.includes("not found") || errLower.includes("no match") || errLower.includes("no occurrences") || errLower.includes("0 occurrences") || errLower.includes("could not find")) {
593085
593250
  if (toolName === "batch_edit") {
593086
593251
  const edits = Array.isArray(args["edits"]) ? args["edits"] : [];
593087
593252
  const filePaths = [
@@ -594354,6 +594519,10 @@ ${trimmedNew}`;
594354
594519
  // we add a second analysis path that catches mismatches in real-time.
594355
594520
  /** Track recent tool outcomes for the adversary */
594356
594521
  _adversaryToolOutcomes = [];
594522
+ /** Monotonic counter for observed local state changes.
594523
+ * Redundant-action classification is only valid within the same state
594524
+ * version; a repeated verifier after an edit is fresh evidence. */
594525
+ _adversaryStateVersion = 0;
594357
594526
  /** WO-FIX-C: Tool fingerprints the adversary has flagged as redundant.
594358
594527
  * Checked in executeSingle to attach advisory guidance before dispatch. */
594359
594528
  _adversaryRedundantSignals = /* @__PURE__ */ new Set();
@@ -594368,6 +594537,59 @@ ${trimmedNew}`;
594368
594537
  * Generates typed self-reflections on task failure and injects them
594369
594538
  * into the next attempt's context for active learning. */
594370
594539
  _reflectionBuffer = null;
594540
+ _markAdversaryObservedStateMutation() {
594541
+ this._adversaryStateVersion++;
594542
+ this._adversaryRedundantSignals.clear();
594543
+ for (const key of Array.from(this._adversaryRecentFlags.keys())) {
594544
+ if (key.startsWith("redundant_action:")) {
594545
+ this._adversaryRecentFlags.delete(key);
594546
+ }
594547
+ }
594548
+ }
594549
+ _buildAdversaryStateDigest(turn) {
594550
+ const ts = this._taskState;
594551
+ let todos = [];
594552
+ try {
594553
+ todos = this.readSessionTodos() ?? [];
594554
+ } catch {
594555
+ todos = [];
594556
+ }
594557
+ const todoCounts = {
594558
+ completed: todos.filter((t2) => t2.status === "completed").length,
594559
+ inProgress: todos.filter((t2) => t2.status === "in_progress").length,
594560
+ pending: todos.filter((t2) => t2.status === "pending").length,
594561
+ blocked: todos.filter((t2) => t2.status === "blocked").length
594562
+ };
594563
+ const activeTodos = todos.filter((t2) => t2.status === "in_progress" || t2.status === "blocked").slice(0, 4).map((t2) => `[${t2.status}] ${String(t2.content ?? "").slice(0, 120)}`);
594564
+ const modifiedFiles = [...ts.modifiedFiles.entries()].slice(-10).map(([path12, action]) => `${action}:${path12}`);
594565
+ const worldFiles = [...this._worldFacts.files.entries()].sort((a2, b) => (b[1].lastSeenTurn ?? 0) - (a2[1].lastSeenTurn ?? 0)).slice(0, 8).map(([path12, fact]) => {
594566
+ const write2 = fact.lastWriteTurn ? ` write@${fact.lastWriteTurn}` : "";
594567
+ const seen = `seen@${fact.lastSeenTurn}`;
594568
+ const wc = fact.writeCount ? ` writes=${fact.writeCount}` : "";
594569
+ return `${path12} (${seen}${write2}${wc})`;
594570
+ });
594571
+ const recentFailures = this._recentFailures.slice(-5).map((failure) => {
594572
+ const text2 = (failure.error || failure.output || "").replace(/\s+/g, " ").trim().slice(0, 180);
594573
+ return `${failure.tool}: ${text2}`;
594574
+ });
594575
+ const focus = this._focusSupervisor?.snapshot().directive;
594576
+ const lines = [
594577
+ `[WORLD STATE SUMMARY] turn=${turn} state_version=${this._adversaryStateVersion}`,
594578
+ `goal=${(ts.originalGoal || ts.goal || "").slice(0, 260)}`,
594579
+ `phase=${ts.phase ?? "unknown"} current_step=${(ts.currentStep || "none").slice(0, 180)}`,
594580
+ ts.nextAction ? `next_action=${ts.nextAction.slice(0, 180)}` : "",
594581
+ `progress=todos completed:${todoCounts.completed} in_progress:${todoCounts.inProgress} pending:${todoCounts.pending} blocked:${todoCounts.blocked}; completed_steps:${ts.completedSteps.length}; failed_approaches:${ts.failedApproaches.length}; modified_files:${ts.modifiedFiles.size}; tool_calls:${ts.toolCallCount}`,
594582
+ activeTodos.length ? `active_todos=${activeTodos.join(" | ")}` : "",
594583
+ ts.completedSteps.length ? `recent_completed=${ts.completedSteps.slice(-5).join(" | ").slice(0, 500)}` : "",
594584
+ ts.failedApproaches.length ? `recent_failed_approaches=${ts.failedApproaches.slice(-4).join(" | ").slice(0, 500)}` : "",
594585
+ modifiedFiles.length ? `modified_files_recent=${modifiedFiles.join(" | ").slice(0, 700)}` : "",
594586
+ worldFiles.length ? `world_files_recent=${worldFiles.join(" | ").slice(0, 700)}` : "",
594587
+ this._worldFacts.lastTest.summary ? `last_verification=${this._worldFacts.lastTest.passed ? "passed" : "failed"} turn=${this._worldFacts.lastTest.turn ?? "?"}: ${this._worldFacts.lastTest.summary.slice(0, 260)}` : "",
594588
+ recentFailures.length ? `recent_failures=${recentFailures.join(" | ").slice(0, 700)}` : "",
594589
+ focus ? `focus=${focus.requiredNextAction}: ${focus.reason.slice(0, 240)}` : ""
594590
+ ].filter(Boolean);
594591
+ return lines.join("\n").slice(0, 2600);
594592
+ }
594371
594593
  buildAdversaryToolOutcomeEvidence(toolName, toolArgs, content, succeeded) {
594372
594594
  const pathValue = toolArgs?.["path"] ?? toolArgs?.["file"] ?? toolArgs?.["filePath"] ?? toolArgs?.["file_path"];
594373
594595
  const path12 = typeof pathValue === "string" && pathValue.trim() ? pathValue.trim() : void 0;
@@ -594451,6 +594673,7 @@ ${trimmedNew}`;
594451
594673
  toolCallId: msg.tool_call_id,
594452
594674
  argsKey,
594453
594675
  fingerprint,
594676
+ stateVersion: this._adversaryStateVersion,
594454
594677
  succeeded,
594455
594678
  ...outcomeEvidence
594456
594679
  });
@@ -594459,136 +594682,66 @@ ${trimmedNew}`;
594459
594682
  }
594460
594683
  while (this._adversaryToolOutcomes.length > 20)
594461
594684
  this._adversaryToolOutcomes.shift();
594462
- if (this._adversaryStream && !this._completionIncompleteVerification) {
594463
- const lastAssistantMsg = [...recent].reverse().find((m2) => m2.role === "assistant" && typeof m2.content === "string");
594464
- const assistantText = typeof lastAssistantMsg?.content === "string" ? lastAssistantMsg.content.replace(/<think>[\s\S]*?<\/think>/g, "").trim() : "";
594465
- if (assistantText) {
594466
- const claimsCompletion = /task.?complete|all tests pass|\bdone\b|\bcomplete(d)?\b/i.test(assistantText);
594467
- this._adversaryStream.observe({
594468
- turn,
594469
- assistantText,
594470
- recentToolOutcomes: this._adversaryToolOutcomes.slice(-8).map((o2) => ({
594471
- tool: o2.tool,
594472
- succeeded: o2.succeeded,
594473
- preview: o2.preview,
594474
- path: o2.path,
594475
- evidence: o2.evidence
594476
- })),
594477
- claimsCompletion
594478
- });
594479
- void this._adversaryStream.tick().catch(() => {
594480
- });
594481
- }
594482
- }
594483
- for (const [key, val] of this._adversaryRecentFlags) {
594484
- if (turn - val.lastTurn > _AgenticRunner.ADVERSARY_FLAG_TTL)
594485
- this._adversaryRecentFlags.delete(key);
594486
- }
594487
- const adversaryFlag = (flagKey, messages3) => {
594488
- const existing = this._adversaryRecentFlags.get(flagKey);
594489
- if (existing) {
594490
- existing.count++;
594491
- existing.lastTurn = turn;
594492
- if (existing.count >= _AgenticRunner.ADVERSARY_ESCALATE_THRESHOLD) {
594493
- return "escalate";
594494
- }
594495
- return "suppress";
594496
- }
594497
- this._adversaryRecentFlags.set(flagKey, { count: 1, lastTurn: turn });
594498
- return "proceed";
594499
- };
594500
- const emitReaction = (cls, shortText, confidence2, details2) => {
594685
+ const stateDigest = this._buildAdversaryStateDigest(turn);
594686
+ const recentToolOutcomes = this._adversaryToolOutcomes.slice(-8).map((o2) => ({
594687
+ tool: o2.tool,
594688
+ succeeded: o2.succeeded,
594689
+ preview: o2.preview,
594690
+ path: o2.path,
594691
+ evidence: o2.evidence
594692
+ }));
594693
+ const queueAdversaryAudit = (input, reason) => {
594694
+ if (!this._adversaryStream || this._completionIncompleteVerification)
594695
+ return;
594696
+ const observation = {
594697
+ turn,
594698
+ recentToolOutcomes,
594699
+ stateDigest,
594700
+ ...input
594701
+ };
594702
+ if (!this._adversaryStream.shouldAudit(observation))
594703
+ return;
594704
+ this._adversaryStream.observe(observation);
594705
+ void this._adversaryStream.tick().catch(() => {
594706
+ });
594501
594707
  this.emit({
594502
- type: "adversary_reaction",
594503
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
594504
- adversary: { class: cls, shortText, confidence: confidence2, details: details2 }
594708
+ type: "status",
594709
+ content: `Adversary inference queued: ${reason}`,
594710
+ turn,
594711
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
594505
594712
  });
594506
594713
  };
594507
- const buildAdversaryCritique = (input) => {
594508
- const alternatives = input.alternatives && input.alternatives.length > 0 ? `
594509
- Alternatives:
594510
- ${input.alternatives.map((item) => `- ${item}`).join("\n")}` : "";
594511
- return [
594512
- `[ADVERSARY CRITIQUE — non-blocking]`,
594513
- `Evidence: ${input.evidence}`,
594514
- `Root cause hypothesis: ${input.hypothesis}`,
594515
- `Corrective action: ${input.correctiveAction}${alternatives}`
594516
- ].join("\n");
594517
- };
594518
594714
  const lastAssistant = [...recent].reverse().find((m2) => m2.role === "assistant" && typeof m2.content === "string");
594519
594715
  if (lastAssistant && typeof lastAssistant.content === "string") {
594520
- const text2 = lastAssistant.content.toLowerCase();
594716
+ const assistantText = lastAssistant.content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
594717
+ const text2 = assistantText.toLowerCase();
594718
+ const claimsCompletion = /task.?complete|all tests pass|\bdone\b|\bcomplete(d)?\b/i.test(text2);
594719
+ queueAdversaryAudit({
594720
+ assistantText,
594721
+ claimsCompletion
594722
+ }, claimsCompletion ? "completion/progress claim" : "success-language audit");
594521
594723
  const claimsFailure = /(?:fail|error|didn't work|not working|unable to|cannot|couldn't|both .* fail|tools? (?:have |has )?been fail)/i.test(text2);
594522
594724
  if (claimsFailure) {
594523
594725
  const recentOutcomes = this._adversaryToolOutcomes.slice(-4);
594524
594726
  const successes = recentOutcomes.filter((o2) => o2.succeeded);
594525
594727
  if (successes.length >= 1) {
594526
- const successList = successes.map((o2) => `${o2.tool}: ${o2.preview.slice(0, 60)}`).join("; ");
594527
- const ffFlag = adversaryFlag("false_failure", messages2);
594528
- if (ffFlag !== "suppress") {
594529
- const critiqueText = buildAdversaryCritique({
594530
- evidence: `Recent tools succeeded: ${successList}.`,
594531
- hypothesis: "The main loop is interpreting uncertainty or partial progress as failure and may be about to discard usable evidence.",
594532
- correctiveAction: "Use the successful results to advance the task, then verify the next concrete step.",
594533
- alternatives: [
594534
- "Edit or run the next verification step that follows from the successful output.",
594535
- "Read a different targeted file if the successful result exposed a new path or symbol.",
594536
- "Complete only if the successful output is sufficient evidence for the user's request."
594537
- ]
594538
- });
594539
- emitReaction("false_failure", `Claimed failure, but recent tools succeeded (${successes.length})`, 0.9, successList);
594540
- if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
594541
- if (ffFlag === "escalate") {
594542
- messages2.push({
594543
- role: "system",
594544
- content: critiqueText + "\n[ESCALATED: This warning was previously issued without effect.]"
594545
- });
594546
- } else {
594547
- this.pendingUserMessages.push(critiqueText);
594548
- }
594549
- }
594550
- this.emit({
594551
- type: "status",
594552
- content: `\x1B[38;5;178m⚠ Corrected false failure claim (${successes.length} tools succeeded)\x1B[0m`,
594553
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
594554
- });
594555
- }
594728
+ queueAdversaryAudit({
594729
+ assistantText,
594730
+ claimsCompletion: false,
594731
+ diagnosticSignal: "possible_false_failure"
594732
+ }, `possible false-failure claim after ${successes.length} successful tool outcome(s)`);
594556
594733
  }
594557
594734
  }
594558
- }
594559
- if (lastAssistant && typeof lastAssistant.content === "string") {
594560
- const text2 = lastAssistant.content.toLowerCase();
594561
594735
  const claimsSuccess = /(done|fixed|success|passed|complete)/i.test(text2);
594562
594736
  if (claimsSuccess) {
594563
594737
  const recentOutcomes = this._adversaryToolOutcomes.slice(-4);
594564
594738
  const failures = recentOutcomes.filter((o2) => !o2.succeeded);
594565
- const successes = recentOutcomes.filter((o2) => o2.succeeded);
594566
- if (failures.length > 0 && successes.length === 0) {
594567
- const failList = failures.map((o2) => `${o2.tool}: ${o2.preview.slice(0, 60)}`).join("; ");
594568
- const fsFlag = adversaryFlag("false_success", messages2);
594569
- if (fsFlag !== "suppress") {
594570
- const critiqueText = buildAdversaryCritique({
594571
- evidence: `Recent tools show errors (${failures.length}): ${failList}.`,
594572
- hypothesis: "The main loop is prematurely compressing intent into success language before the verifier produced evidence.",
594573
- correctiveAction: "Inspect the failed output, identify the implicated path/symbol/command, and run one focused corrective step before claiming success.",
594574
- alternatives: [
594575
- "Read the smallest relevant source region around the failed symbol.",
594576
- "Patch the implicated code or configuration.",
594577
- "Run the same verifier only after a state-changing fix."
594578
- ]
594579
- });
594580
- emitReaction("false_success", `Claimed success, but recent tools failed (${failures.length})`, 0.9, failList);
594581
- if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
594582
- if (fsFlag === "escalate") {
594583
- messages2.push({
594584
- role: "system",
594585
- content: critiqueText + "\n[ESCALATED: This warning was previously issued without effect.]"
594586
- });
594587
- } else {
594588
- this.pendingUserMessages.push(critiqueText);
594589
- }
594590
- }
594591
- }
594739
+ if (failures.length > 0) {
594740
+ queueAdversaryAudit({
594741
+ assistantText,
594742
+ claimsCompletion,
594743
+ diagnosticSignal: "possible_false_success"
594744
+ }, `possible false-success claim near ${failures.length} recent failure(s)`);
594592
594745
  }
594593
594746
  }
594594
594747
  }
@@ -594606,38 +594759,20 @@ ${input.alternatives.map((item) => `- ${item}`).join("\n")}` : "";
594606
594759
  }
594607
594760
  const argsKey = this._buildExactArgsKey(args);
594608
594761
  const fingerprint = this._buildToolFingerprint(name10, args);
594609
- const prior = this._adversaryToolOutcomes.find((o2) => o2.succeeded && o2.tool === name10 && o2.fingerprint === fingerprint && o2.turn < turn);
594762
+ const prior = this._adversaryToolOutcomes.find((o2) => o2.succeeded && o2.tool === name10 && o2.fingerprint === fingerprint && o2.stateVersion === this._adversaryStateVersion && o2.turn < turn);
594610
594763
  if (prior) {
594611
- this._adversaryRedundantSignals.add(fingerprint);
594612
- const raFlag = adversaryFlag(`redundant_action:${fingerprint}`, messages2);
594613
- if (raFlag !== "suppress") {
594614
- const critiqueText = buildAdversaryCritique({
594615
- evidence: `${name10} already succeeded on turn ${prior.turn} with exact arguments (${argsKey.slice(0, 120)}). Prior preview: ${prior.preview}`,
594616
- hypothesis: "The main loop may have lost track of previously observed evidence because of context pressure, path confusion, or repeated discovery.",
594617
- correctiveAction: "Let this duplicate run execute if needed, but treat the prior result as evidence and pivot afterward unless state has changed.",
594618
- alternatives: [
594619
- "Use the prior result to edit/write, verify, or finish with evidence.",
594620
- "Read a different specific file or selector if the current evidence is insufficient.",
594621
- "Repeat exact arguments only when filesystem, browser, or page state changed."
594622
- ]
594623
- });
594624
- emitReaction("redundant_action", `Already ran ${name10} successfully on turn ${prior.turn}`, 0.8, prior.preview);
594625
- if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
594626
- if (raFlag === "escalate") {
594627
- messages2.push({
594628
- role: "system",
594629
- content: critiqueText + "\n[ESCALATED: This redundant-action warning was issued 3+ times as a user message without effect.]"
594630
- });
594631
- } else {
594632
- this.pendingUserMessages.push(critiqueText);
594633
- }
594634
- }
594635
- this.emit({
594636
- type: "status",
594637
- content: `\x1B[38;5;178m⚠ Adversary noted redundant ${name10} call (succeeded on turn ${prior.turn}); action remains allowed\x1B[0m`,
594638
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
594639
- });
594640
- }
594764
+ const repeatCount = this._adversaryToolOutcomes.filter((o2) => o2.tool === name10 && o2.fingerprint === fingerprint && o2.stateVersion === this._adversaryStateVersion && o2.turn < turn).length + 1;
594765
+ queueAdversaryAudit({
594766
+ assistantText: "",
594767
+ claimsCompletion: false,
594768
+ diagnosticSignal: "possible_repeated_action",
594769
+ loopSignal: {
594770
+ tool: name10,
594771
+ target: argsKey.slice(0, 180),
594772
+ count: repeatCount,
594773
+ alreadyHave: prior.preview
594774
+ }
594775
+ }, `possible repeated action ${name10} after prior same-state success`);
594641
594776
  break;
594642
594777
  }
594643
594778
  }
@@ -594655,35 +594790,11 @@ ${input.alternatives.map((item) => `- ${item}`).join("\n")}` : "";
594655
594790
  }
594656
594791
  }
594657
594792
  if (consecutiveShortResults >= 3) {
594658
- const itFlag = adversaryFlag("idle_think", messages2);
594659
- if (itFlag !== "suppress") {
594660
- const critiqueText = buildAdversaryCritique({
594661
- evidence: `${consecutiveShortResults} consecutive output-like calls occurred without an input-like observation.`,
594662
- hypothesis: "The loop may be acting from stale state instead of re-observing the environment.",
594663
- correctiveAction: "Take one input/observation step before another output step.",
594664
- alternatives: [
594665
- "Call the input/listen/poll tool for the current environment.",
594666
- "Read the current UI/page state before clicking or typing again.",
594667
- "If the task is already complete, finish with the concrete evidence already observed."
594668
- ]
594669
- });
594670
- emitReaction("idle_think", `Consecutive output without input: ${consecutiveShortResults}`, 0.7);
594671
- if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
594672
- if (itFlag === "escalate") {
594673
- messages2.push({
594674
- role: "system",
594675
- content: critiqueText + "\n[ESCALATED: This warning was previously issued without effect.]"
594676
- });
594677
- } else {
594678
- this.pendingUserMessages.push(critiqueText);
594679
- }
594680
- }
594681
- this.emit({
594682
- type: "status",
594683
- content: `\x1B[38;5;178m⚠ Adversary flagged runaway-output risk (${consecutiveShortResults} consecutive sends without receive); action remains allowed\x1B[0m`,
594684
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
594685
- });
594686
- }
594793
+ queueAdversaryAudit({
594794
+ assistantText: "",
594795
+ claimsCompletion: false,
594796
+ diagnosticSignal: "possible_stale_output"
594797
+ }, `possible stale-output loop (${consecutiveShortResults} short successful outcomes)`);
594687
594798
  }
594688
594799
  }
594689
594800
  }
@@ -594691,6 +594802,8 @@ ${input.alternatives.map((item) => `- ${item}`).join("\n")}` : "";
594691
594802
  const failCount = this._adversaryToolOutcomes.filter((o2) => !o2.succeeded).length;
594692
594803
  const lastFour = this._adversaryToolOutcomes.slice(-4);
594693
594804
  const details = [
594805
+ stateDigest,
594806
+ ``,
594694
594807
  `Recent tool outcomes:`,
594695
594808
  ...lastFour.map((o2) => `- ${o2.tool}: ${o2.succeeded ? "OK" : "ERR"} — ${o2.preview}`)
594696
594809
  ].join("\n");
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.454",
3
+ "version": "1.0.456",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.454",
9
+ "version": "1.0.456",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.454",
3
+ "version": "1.0.456",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",