omnius 1.0.466 → 1.0.468

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
@@ -14235,6 +14235,65 @@ function findMatchOffsets(haystack, needle) {
14235
14235
  }
14236
14236
  return offsets;
14237
14237
  }
14238
+ function lineNumberAtOffset(haystack, offset) {
14239
+ return haystack.slice(0, offset).split("\n").length;
14240
+ }
14241
+ function findLineEndWhitespaceEquivalentSpans(haystack, needle) {
14242
+ const normalizedNeedle = normalizeLineEndWhitespaceWithMap(needle).text;
14243
+ if (normalizedNeedle.length === 0)
14244
+ return [];
14245
+ const normalizedHaystack = normalizeLineEndWhitespaceWithMap(haystack);
14246
+ const spans = [];
14247
+ let pos = 0;
14248
+ while ((pos = normalizedHaystack.text.indexOf(normalizedNeedle, pos)) !== -1) {
14249
+ const normalizedEnd = pos + normalizedNeedle.length;
14250
+ const start2 = normalizedHaystack.map[pos];
14251
+ const end = normalizedEnd < normalizedHaystack.map.length ? normalizedHaystack.map[normalizedEnd] : haystack.length;
14252
+ if (start2 !== void 0 && end >= start2) {
14253
+ const text2 = haystack.slice(start2, end);
14254
+ if (text2 !== needle) {
14255
+ spans.push({ start: start2, end, text: text2 });
14256
+ }
14257
+ }
14258
+ pos += Math.max(1, normalizedNeedle.length);
14259
+ }
14260
+ return spans;
14261
+ }
14262
+ function normalizeLineEndWhitespaceWithMap(value2) {
14263
+ const chars = [];
14264
+ const map2 = [];
14265
+ for (let i2 = 0; i2 < value2.length; ) {
14266
+ const ch = value2[i2];
14267
+ if (ch === " " || ch === " ") {
14268
+ let j = i2 + 1;
14269
+ while (j < value2.length && (value2[j] === " " || value2[j] === " ")) {
14270
+ j++;
14271
+ }
14272
+ const next = value2[j];
14273
+ if (j >= value2.length || next === "\n" || next === "\r") {
14274
+ i2 = j;
14275
+ continue;
14276
+ }
14277
+ for (let k = i2; k < j; k++) {
14278
+ chars.push(value2[k]);
14279
+ map2.push(k);
14280
+ }
14281
+ i2 = j;
14282
+ continue;
14283
+ }
14284
+ chars.push(ch);
14285
+ map2.push(i2);
14286
+ i2++;
14287
+ }
14288
+ return { text: chars.join(""), map: map2 };
14289
+ }
14290
+ function replaceSpans(content, spans, replacement) {
14291
+ let updated = content;
14292
+ for (const span of [...spans].sort((a2, b) => b.start - a2.start)) {
14293
+ updated = updated.slice(0, span.start) + replacement + updated.slice(span.end);
14294
+ }
14295
+ return updated;
14296
+ }
14238
14297
  function replaceAllOccurrences(haystack, needle, replacement) {
14239
14298
  return haystack.split(needle).join(replacement);
14240
14299
  }
@@ -14419,7 +14478,17 @@ var init_file_edit = __esm({
14419
14478
  afterHash: beforeHash
14420
14479
  };
14421
14480
  }
14422
- const occurrences = countOccurrences(content, oldString);
14481
+ let occurrences = countOccurrences(content, oldString);
14482
+ let whitespaceAdjustedMatches = null;
14483
+ let diffOldString = oldString;
14484
+ if (occurrences === 0 && expectedHash) {
14485
+ const matches = findLineEndWhitespaceEquivalentSpans(content, oldString);
14486
+ if (matches.length > 0) {
14487
+ occurrences = matches.length;
14488
+ whitespaceAdjustedMatches = matches;
14489
+ diffOldString = matches[0]?.text ?? oldString;
14490
+ }
14491
+ }
14423
14492
  if (occurrences === 0) {
14424
14493
  const alreadyAppliedLines = newString.length > 0 ? findMatchLines(content, newString) : [];
14425
14494
  if (alreadyAppliedLines.length > 0) {
@@ -14471,18 +14540,19 @@ Use the EXACT current content above to construct a working old_string. Do not re
14471
14540
  };
14472
14541
  }
14473
14542
  if (!replaceAll && occurrences > 1) {
14474
- const matchLines = findMatchLines(content, oldString);
14475
- const offsets = findMatchOffsets(content, oldString);
14543
+ const matchLines = whitespaceAdjustedMatches ? whitespaceAdjustedMatches.map((match) => lineNumberAtOffset(content, match.start)) : findMatchLines(content, oldString);
14544
+ const offsets = whitespaceAdjustedMatches ? whitespaceAdjustedMatches.map((match) => match.start) : findMatchOffsets(content, oldString);
14476
14545
  const snippets = offsets.slice(0, 4).map((off) => {
14477
14546
  const s2 = snippetAtOffset(content, off, 3);
14478
14547
  return `
14479
14548
  --- match at line ${s2.lineNumber} ---
14480
14549
  ${s2.content}`;
14481
14550
  }).join("\n");
14551
+ const reason = whitespaceAdjustedMatches ? `old_string is not unique after ignoring trailing spaces/tabs at line ends — found ${occurrences} occurrences at lines ${matchLines.join(", ")}.` : `old_string is not unique — found ${occurrences} occurrences at lines ${matchLines.join(", ")}.`;
14482
14552
  return {
14483
14553
  success: false,
14484
14554
  output: "",
14485
- error: `old_string is not unique — found ${occurrences} occurrences at lines ${matchLines.join(", ")}.
14555
+ error: `${reason}
14486
14556
  ${snippets}
14487
14557
 
14488
14558
  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.`,
@@ -14504,16 +14574,27 @@ Add UNIQUE surrounding context (a function name, distinctive comment, or unique
14504
14574
  let updated;
14505
14575
  let editedLines;
14506
14576
  if (replaceAll) {
14507
- editedLines = findMatchLines(content, oldString);
14508
- updated = replaceAllOccurrences(content, oldString, newString);
14577
+ if (whitespaceAdjustedMatches) {
14578
+ editedLines = whitespaceAdjustedMatches.map((match) => lineNumberAtOffset(content, match.start));
14579
+ updated = replaceSpans(content, whitespaceAdjustedMatches, newString);
14580
+ } else {
14581
+ editedLines = findMatchLines(content, oldString);
14582
+ updated = replaceAllOccurrences(content, oldString, newString);
14583
+ }
14509
14584
  } else {
14510
- const index = content.indexOf(oldString);
14511
- const lineNumber = content.slice(0, index).split("\n").length;
14512
- editedLines = [lineNumber];
14513
- updated = content.slice(0, index) + newString + content.slice(index + oldString.length);
14585
+ if (whitespaceAdjustedMatches) {
14586
+ const match = whitespaceAdjustedMatches[0];
14587
+ editedLines = [lineNumberAtOffset(content, match.start)];
14588
+ updated = content.slice(0, match.start) + newString + content.slice(match.end);
14589
+ } else {
14590
+ const index = content.indexOf(oldString);
14591
+ const lineNumber = content.slice(0, index).split("\n").length;
14592
+ editedLines = [lineNumber];
14593
+ updated = content.slice(0, index) + newString + content.slice(index + oldString.length);
14594
+ }
14514
14595
  }
14515
14596
  const afterHash = contentHash(updated);
14516
- const diff = buildCompactDiff(oldString, newString);
14597
+ const diff = buildCompactDiff(diffOldString, newString);
14517
14598
  if (afterHash === beforeHash) {
14518
14599
  return {
14519
14600
  success: true,
@@ -14529,9 +14610,10 @@ Add UNIQUE surrounding context (a function name, distinctive comment, or unique
14529
14610
  }
14530
14611
  if (dryRun) {
14531
14612
  const linesInfo2 = editedLines.length === 1 ? `line ${editedLines[0]}` : `${editedLines.length} locations (lines ${editedLines.join(", ")})`;
14613
+ const matchNote2 = whitespaceAdjustedMatches ? " Matched by verified line-ending whitespace normalization." : "";
14532
14614
  return {
14533
14615
  success: true,
14534
- output: `[DRY RUN] file_edit validated for ${filePath} at ${linesInfo2}. File NOT modified. Call again with dry_run=false to apply.
14616
+ output: `[DRY RUN] file_edit validated for ${filePath} at ${linesInfo2}. File NOT modified. Call again with dry_run=false to apply.${matchNote2}
14535
14617
 
14536
14618
  ${diff}`,
14537
14619
  durationMs: performance.now() - start2,
@@ -14552,10 +14634,11 @@ ${diff}`,
14552
14634
  diff
14553
14635
  });
14554
14636
  const linesInfo = editedLines.length === 1 ? `line ${editedLines[0]}` : `${editedLines.length} locations (lines ${editedLines.join(", ")})`;
14555
- const lineNumberedDiff = buildLineNumberedDiff(oldString, newString, editedLines[0] ?? 1);
14637
+ const lineNumberedDiff = buildLineNumberedDiff(diffOldString, newString, editedLines[0] ?? 1);
14638
+ const matchNote = whitespaceAdjustedMatches ? " (matched by verified line-ending whitespace normalization)" : "";
14556
14639
  return {
14557
14640
  success: true,
14558
- output: `Edited ${filePath} at ${linesInfo} (sha256 ${beforeHash} → ${afterHash})
14641
+ output: `Edited ${filePath} at ${linesInfo}${matchNote} (sha256 ${beforeHash} → ${afterHash})
14559
14642
  ${lineNumberedDiff}`,
14560
14643
  durationMs: performance.now() - start2,
14561
14644
  mutated: true,
@@ -28885,12 +28968,82 @@ var init_file_patch = __esm({
28885
28968
  description: "Preview the diff without writing. Returns what would change. Default: false"
28886
28969
  }
28887
28970
  },
28888
- required: ["path", "start_line"]
28971
+ required: ["path"],
28972
+ allOf: [
28973
+ {
28974
+ if: { properties: { mode: { const: "delete" } }, required: ["mode"] },
28975
+ then: { required: ["start_line", "end_line"] }
28976
+ },
28977
+ {
28978
+ if: { properties: { mode: { const: "replace" } }, required: ["mode"] },
28979
+ then: { required: ["start_line", "end_line"] }
28980
+ },
28981
+ {
28982
+ if: { properties: { mode: { const: "insert_before" } }, required: ["mode"] },
28983
+ then: { required: ["start_line"] }
28984
+ },
28985
+ {
28986
+ if: { properties: { mode: { const: "insert_after" } }, required: ["mode"] },
28987
+ then: { required: ["start_line"] }
28988
+ }
28989
+ ]
28889
28990
  };
28890
28991
  workingDir;
28891
28992
  constructor(workingDir) {
28892
28993
  this.workingDir = workingDir;
28893
28994
  }
28995
+ validateInput(args) {
28996
+ if (args["operations"] !== void 0) {
28997
+ return {
28998
+ result: false,
28999
+ message: `file_patch accepts one line operation per call; do not pass operations. For deletion use: {"path":"...","mode":"delete","start_line":8,"end_line":13,"expected_hash":"..."}. For replacement use: {"path":"...","mode":"replace","start_line":8,"end_line":13,"new_content":"...","expected_hash":"..."}.`
29000
+ };
29001
+ }
29002
+ if (typeof args["path"] !== "string" || !args["path"].trim()) {
29003
+ return { result: false, message: "file_patch requires non-empty path." };
29004
+ }
29005
+ const mode = typeof args["mode"] === "string" ? args["mode"] : "replace";
29006
+ if (!["replace", "insert_before", "insert_after", "delete"].includes(mode)) {
29007
+ return {
29008
+ result: false,
29009
+ message: `file_patch mode="${String(args["mode"])}" is invalid. Use replace, insert_before, insert_after, or delete.`
29010
+ };
29011
+ }
29012
+ if (!Number.isInteger(args["start_line"]) || Number(args["start_line"]) < 1) {
29013
+ return {
29014
+ result: false,
29015
+ message: `file_patch requires start_line as a positive 1-based integer. For deleting lines, include mode="delete", start_line, end_line, and expected_hash.`
29016
+ };
29017
+ }
29018
+ const hasEndLine = args["end_line"] !== void 0 && args["end_line"] !== null;
29019
+ const hasNewContent = args["new_content"] !== void 0 || args["newContent"] !== void 0 || args["new_content_base64"] !== void 0 || args["newContentBase64"] !== void 0;
29020
+ const hasExpectedTarget = args["expected_hash"] !== void 0 || args["expectedHash"] !== void 0 || args["expected_old_content"] !== void 0;
29021
+ if ((mode === "replace" || mode === "delete") && !hasEndLine) {
29022
+ return {
29023
+ result: false,
29024
+ message: `file_patch mode=${mode} requires end_line. For deletion use mode="delete" with start_line and end_line; for replacement include end_line and new_content.`
29025
+ };
29026
+ }
29027
+ if ((mode === "replace" || mode === "insert_before" || mode === "insert_after") && !hasNewContent && args["allow_empty_content"] !== true) {
29028
+ return {
29029
+ result: false,
29030
+ message: `file_patch mode=${mode} requires new_content or new_content_base64. If the intended action is removal, use mode="delete" with start_line and end_line instead.`
29031
+ };
29032
+ }
29033
+ if ((mode === "replace" || mode === "delete") && !hasExpectedTarget) {
29034
+ return {
29035
+ result: false,
29036
+ message: `file_patch mode=${mode} requires expected_hash from file_read or expected_old_content copied from the target lines.`
29037
+ };
29038
+ }
29039
+ if ((mode === "insert_before" || mode === "insert_after") && args["expected_hash"] === void 0 && args["expectedHash"] === void 0) {
29040
+ return {
29041
+ result: false,
29042
+ message: `file_patch mode=${mode} requires expected_hash from the most recent file_read so line numbers are versioned.`
29043
+ };
29044
+ }
29045
+ return { result: true };
29046
+ }
28894
29047
  async execute(args) {
28895
29048
  const filePath = args["path"];
28896
29049
  const startLine = args["start_line"];
@@ -575230,6 +575383,9 @@ function violatesDirective(directive, input) {
575230
575383
  const summaryReportsDegraded = /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summaryText);
575231
575384
  const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || summaryReportsDegraded;
575232
575385
  if (directive.requiredNextAction === "report_blocked" || directive.requiredNextAction === "report_incomplete" || directive.state === "terminal_incomplete") {
575386
+ const completionGateDirective = directive.state !== "terminal_incomplete" && directive.forbiddenActionFamilies.includes("task_complete");
575387
+ if (completionGateDirective)
575388
+ return false;
575233
575389
  return !reportsDegradedCompletion;
575234
575390
  }
575235
575391
  return false;
@@ -575489,10 +575645,12 @@ var init_focusSupervisor = __esm({
575489
575645
  failureFamilies = /* @__PURE__ */ new Map();
575490
575646
  ignoredDirectiveStreak = 0;
575491
575647
  lastIgnoredDirectiveTurn = null;
575648
+ availableTools = null;
575492
575649
  constructor(options2 = {}) {
575493
575650
  this.mode = options2.mode ?? "auto";
575494
575651
  this.modelTier = options2.modelTier ?? "large";
575495
575652
  this.repeatGateMax = Math.max(0, Math.floor(options2.repeatGateMax ?? 3));
575653
+ this.availableTools = options2.availableTools ? new Set(Array.from(options2.availableTools)) : null;
575496
575654
  }
575497
575655
  get enabled() {
575498
575656
  return this.mode !== "off";
@@ -575500,6 +575658,15 @@ var init_focusSupervisor = __esm({
575500
575658
  get currentDirective() {
575501
575659
  return this.directive;
575502
575660
  }
575661
+ hasTool(name10) {
575662
+ return this.availableTools === null || this.availableTools.has(name10);
575663
+ }
575664
+ resolveRequiredNextAction(action) {
575665
+ if (action === "update_todos" && !this.hasTool("todo_write")) {
575666
+ return "report_blocked";
575667
+ }
575668
+ return action;
575669
+ }
575503
575670
  snapshot() {
575504
575671
  return {
575505
575672
  mode: this.mode,
@@ -575652,14 +575819,15 @@ var init_focusSupervisor = __esm({
575652
575819
  return this.inject(directive, `[FOCUS SUPERVISOR] ${directive.reason}. Required next action: ${directive.requiredNextAction}.`);
575653
575820
  }
575654
575821
  if (this.hasLowSignalContext(input.context) && input.isReadLike && !(this.directive?.requiredNextAction === "read_authoritative_target" || this.directive?.requiredNextAction === "use_cached_evidence")) {
575822
+ const requiredNextAction = this.resolveRequiredNextAction("update_todos");
575655
575823
  const directive = this.setDirective({
575656
575824
  turn: input.turn,
575657
575825
  state: "forced_replan",
575658
575826
  reason: "raw discovery is dominating active evidence in the context window",
575659
- requiredNextAction: "update_todos",
575827
+ requiredNextAction,
575660
575828
  forbiddenActionFamilies: [family]
575661
575829
  });
575662
- return this.inject(directive, "[FOCUS SUPERVISOR] Discovery noise is crowding the live context. Update the todo decomposition, then act on active evidence instead of broad rereads.");
575830
+ return this.inject(directive, requiredNextAction === "update_todos" ? "[FOCUS SUPERVISOR] Discovery noise is crowding the live context. Update the todo decomposition, then act on active evidence instead of broad rereads." : "[FOCUS SUPERVISOR] Discovery noise is crowding the live context, and todo_write is not available in this tool surface. Report blocked/incomplete with the concrete blocker, or proceed only if an available substantive tool can satisfy the task.");
575663
575831
  }
575664
575832
  this.lastDecision = "pass";
575665
575833
  this.lastReason = "";
@@ -575739,22 +575907,24 @@ var init_focusSupervisor = __esm({
575739
575907
  actionFamily(input.toolName, input.args),
575740
575908
  input.toolName
575741
575909
  ]) : input.toolName === "shell" ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)];
575910
+ const requiredNextAction = ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "use_cached_evidence" : this.resolveRequiredNextAction("update_todos");
575742
575911
  this.setDirective({
575743
575912
  turn: input.turn,
575744
575913
  state: "forced_replan",
575745
575914
  reason: `same ${input.toolName} failure family repeated ${next.count} times: ${next.sample}`,
575746
- requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "use_cached_evidence" : "update_todos",
575915
+ requiredNextAction,
575747
575916
  forbiddenActionFamilies: forbidden
575748
575917
  });
575749
575918
  } else if (next.count === 1 && !this.directive) {
575750
575919
  const ambiguousEditFailure = isEditTool(input.toolName) && next.errorClass === "stale_ambiguous_target";
575751
575920
  const staleEditFailure = isEditTool(input.toolName) && next.errorClass.startsWith("stale_");
575752
575921
  const shellFailure = input.toolName === "shell";
575922
+ const requiredNextAction = ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : shellFailure ? "use_cached_evidence" : this.resolveRequiredNextAction("update_todos");
575753
575923
  this.setDirective({
575754
575924
  turn: input.turn,
575755
575925
  state: shellFailure ? "cached_evidence" : "warn",
575756
575926
  reason: `first ${input.toolName} failure (${next.errorClass}): ${next.sample}`,
575757
- requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : shellFailure ? "use_cached_evidence" : "update_todos",
575927
+ requiredNextAction,
575758
575928
  forbiddenActionFamilies: shellFailure ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)]
575759
575929
  });
575760
575930
  }
@@ -575766,7 +575936,7 @@ var init_focusSupervisor = __esm({
575766
575936
  turn: input.turn,
575767
575937
  state: "verify_or_block",
575768
575938
  reason: input.reason,
575769
- requiredNextAction: input.requiredNextAction ?? "run_verification",
575939
+ requiredNextAction: this.resolveRequiredNextAction(input.requiredNextAction ?? "run_verification"),
575770
575940
  forbiddenActionFamilies: ["task_complete"]
575771
575941
  });
575772
575942
  }
@@ -577904,6 +578074,9 @@ import { join as _pathJoin, relative as _pathRelative, resolve as _pathResolve }
577904
578074
  import { tmpdir as _osTmpdir } from "node:os";
577905
578075
  import { homedir as _osHomedir } from "node:os";
577906
578076
  import { z as z17 } from "zod";
578077
+ function cleanToolActionReasonField(value2) {
578078
+ return String(value2 ?? "").replace(/\s+/g, " ").trim();
578079
+ }
577907
578080
  function parsePersistentMemoryMode(value2) {
577908
578081
  if (value2 === "full" || value2 === "deferred" || value2 === "off")
577909
578082
  return value2;
@@ -581694,6 +581867,22 @@ ${result.output ?? ""}`;
581694
581867
  return [];
581695
581868
  return todos.filter((t2) => t2.status !== "completed");
581696
581869
  }
581870
+ _taskCompleteArgsReportDegraded(args) {
581871
+ const status = completionStatusFromTaskCompleteArgs(args);
581872
+ if (status === "blocked" || status === "incomplete" || status === "needs_input") {
581873
+ return true;
581874
+ }
581875
+ const summary = args && typeof args["summary"] === "string" ? args["summary"] : "";
581876
+ return /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summary);
581877
+ }
581878
+ _shouldBypassTodoCompletionGuardForTaskComplete(args) {
581879
+ if (!this._taskCompleteArgsReportDegraded(args))
581880
+ return false;
581881
+ if (this._completionIncompleteVerification)
581882
+ return true;
581883
+ const directive = this._focusSupervisor?.snapshot().directive;
581884
+ return directive?.state === "terminal_incomplete" || directive?.requiredNextAction === "report_incomplete" || directive?.requiredNextAction === "report_blocked";
581885
+ }
581697
581886
  /**
581698
581887
  * Build a guard prompt injected when the model attempts to call task_complete
581699
581888
  * while there are open todo items. This asks the model to verify each item by
@@ -583790,8 +583979,14 @@ ${latest.output || ""}`.trim();
583790
583979
  const artifactLines = intEnv("OMNIUS_CONTEXT_TREE_ARTIFACT_LINES", 1800, 100, 4e3);
583791
583980
  try {
583792
583981
  const scan = scanWorkspace({ root, maxFiles, maxDirs });
583793
- const compactTree = renderWorkspaceTree(scan, { maxLines: contextLines, sortBy: "recent" });
583794
- const fullTree = renderWorkspaceTree(scan, { maxLines: artifactLines, sortBy: "recent" });
583982
+ const compactTree = renderWorkspaceTree(scan, {
583983
+ maxLines: contextLines,
583984
+ sortBy: "recent"
583985
+ });
583986
+ const fullTree = renderWorkspaceTree(scan, {
583987
+ maxLines: artifactLines,
583988
+ sortBy: "recent"
583989
+ });
583795
583990
  const totalBytes = scan.files.reduce((sum, file) => sum + file.bytes, 0);
583796
583991
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
583797
583992
  const focusSnapshot = this._focusSupervisor?.snapshot();
@@ -585892,7 +586087,8 @@ Respond with your assessment, then take action.`;
585892
586087
  this._focusSupervisor = this.options.focusSupervisor === "off" ? null : new FocusSupervisor({
585893
586088
  mode: this.options.focusSupervisor,
585894
586089
  modelTier: this.options.modelTier,
585895
- repeatGateMax: this._resolveRepeatGateMax()
586090
+ repeatGateMax: this._resolveRepeatGateMax(),
586091
+ availableTools: this.tools.keys()
585896
586092
  });
585897
586093
  if (!this._workingDirectory) {
585898
586094
  this._workingDirectory = _pathResolve(process.cwd());
@@ -591484,8 +591680,9 @@ ${sr.result.output}`;
591484
591680
  });
591485
591681
  continue;
591486
591682
  }
591683
+ const taskCompleteArgs = matchTc.arguments;
591487
591684
  const open2 = this.getOpenTodoItems();
591488
- if (open2.length > 0) {
591685
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591489
591686
  const guard = this.buildTodoCompletionGuard(open2);
591490
591687
  messages2.push({ role: "system", content: guard });
591491
591688
  this.emit({
@@ -591494,12 +591691,12 @@ ${sr.result.output}`;
591494
591691
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591495
591692
  });
591496
591693
  } else {
591497
- if (holdTaskCompleteGates(matchTc.arguments, turn)) {
591694
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591498
591695
  if (this._completionIncompleteVerification)
591499
591696
  break;
591500
591697
  continue;
591501
591698
  }
591502
- const _bp1 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(matchTc.arguments));
591699
+ const _bp1 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591503
591700
  if (_bp1 && !_bp1.proceed) {
591504
591701
  if (_bp1.feedback)
591505
591702
  emitBackwardPassAdvisory(_bp1.feedback, turn);
@@ -591508,7 +591705,7 @@ ${sr.result.output}`;
591508
591705
  continue;
591509
591706
  }
591510
591707
  completed = true;
591511
- summary = extractTaskCompleteSummary(matchTc.arguments);
591708
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591512
591709
  this._onTypedEvent?.({
591513
591710
  type: "completion_requested",
591514
591711
  runId: this._sessionId ?? "unknown",
@@ -591558,8 +591755,9 @@ ${sr.result.output}`;
591558
591755
  });
591559
591756
  continue;
591560
591757
  }
591758
+ const taskCompleteArgs = r2.tc.arguments;
591561
591759
  const open2 = this.getOpenTodoItems();
591562
- if (open2.length > 0) {
591760
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591563
591761
  const guard = this.buildTodoCompletionGuard(open2);
591564
591762
  messages2.push({ role: "system", content: guard });
591565
591763
  this.emit({
@@ -591568,12 +591766,12 @@ ${sr.result.output}`;
591568
591766
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591569
591767
  });
591570
591768
  } else {
591571
- if (holdTaskCompleteGates(r2.tc.arguments, turn)) {
591769
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591572
591770
  if (this._completionIncompleteVerification)
591573
591771
  break;
591574
591772
  continue;
591575
591773
  }
591576
- const _bp2 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(r2.tc.arguments));
591774
+ const _bp2 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591577
591775
  if (_bp2 && !_bp2.proceed) {
591578
591776
  if (_bp2.feedback)
591579
591777
  emitBackwardPassAdvisory(_bp2.feedback, turn);
@@ -591582,7 +591780,7 @@ ${sr.result.output}`;
591582
591780
  continue;
591583
591781
  }
591584
591782
  completed = true;
591585
- summary = extractTaskCompleteSummary(r2.tc.arguments);
591783
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591586
591784
  this._onTypedEvent?.({
591587
591785
  type: "completion_requested",
591588
591786
  runId: this._sessionId ?? "unknown",
@@ -591671,8 +591869,9 @@ ${sr.result.output}`;
591671
591869
  });
591672
591870
  continue;
591673
591871
  }
591872
+ const taskCompleteArgs = r2.tc.arguments;
591674
591873
  const open2 = this.getOpenTodoItems();
591675
- if (open2.length > 0) {
591874
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591676
591875
  const guard = this.buildTodoCompletionGuard(open2);
591677
591876
  messages2.push({ role: "system", content: guard });
591678
591877
  this.emit({
@@ -591681,12 +591880,12 @@ ${sr.result.output}`;
591681
591880
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591682
591881
  });
591683
591882
  } else {
591684
- if (holdTaskCompleteGates(r2.tc.arguments, turn)) {
591883
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591685
591884
  if (this._completionIncompleteVerification)
591686
591885
  break;
591687
591886
  continue;
591688
591887
  }
591689
- const _bp3 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(r2.tc.arguments));
591888
+ const _bp3 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591690
591889
  if (_bp3 && !_bp3.proceed) {
591691
591890
  if (_bp3.feedback)
591692
591891
  emitBackwardPassAdvisory(_bp3.feedback, turn);
@@ -591695,7 +591894,7 @@ ${sr.result.output}`;
591695
591894
  continue;
591696
591895
  }
591697
591896
  completed = true;
591698
- summary = extractTaskCompleteSummary(r2.tc.arguments);
591897
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591699
591898
  this._onTypedEvent?.({
591700
591899
  type: "completion_requested",
591701
591900
  runId: this._sessionId ?? "unknown",
@@ -592573,8 +592772,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592573
592772
  });
592574
592773
  continue;
592575
592774
  }
592775
+ const taskCompleteArgs = tc.arguments;
592576
592776
  const open2 = this.getOpenTodoItems();
592577
- if (open2.length > 0) {
592777
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
592578
592778
  const guard = this.buildTodoCompletionGuard(open2);
592579
592779
  messages2.push({ role: "system", content: guard });
592580
592780
  this.emit({
@@ -592583,12 +592783,12 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592583
592783
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592584
592784
  });
592585
592785
  } else {
592586
- if (holdTaskCompleteGates(tc.arguments, turn)) {
592786
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
592587
592787
  if (this._completionIncompleteVerification)
592588
592788
  break;
592589
592789
  continue;
592590
592790
  }
592591
- const _bp4 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(tc.arguments));
592791
+ const _bp4 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
592592
592792
  if (_bp4 && !_bp4.proceed) {
592593
592793
  if (_bp4.feedback)
592594
592794
  emitBackwardPassAdvisory(_bp4.feedback, turn);
@@ -592597,7 +592797,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592597
592797
  continue;
592598
592798
  }
592599
592799
  completed = true;
592600
- summary = extractTaskCompleteSummary(tc.arguments);
592800
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
592601
592801
  this._onTypedEvent?.({
592602
592802
  type: "completion_requested",
592603
592803
  runId: this._sessionId ?? "unknown",
@@ -592638,8 +592838,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592638
592838
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592639
592839
  });
592640
592840
  if (/task.?complete|all tests pass/i.test(content)) {
592841
+ const completionArgs = { summary: content };
592641
592842
  const open2 = this.getOpenTodoItems();
592642
- if (open2.length > 0) {
592843
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(completionArgs)) {
592643
592844
  const guard = this.buildTodoCompletionGuard(open2);
592644
592845
  messages2.push({ role: "system", content: guard });
592645
592846
  this.emit({
@@ -592648,7 +592849,6 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592648
592849
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592649
592850
  });
592650
592851
  } else {
592651
- const completionArgs = { summary: content };
592652
592852
  if (holdTaskCompleteGates(completionArgs, turn)) {
592653
592853
  if (this._completionIncompleteVerification)
592654
592854
  break;
@@ -593987,7 +594187,7 @@ ${marker}` : marker);
593987
594187
  try {
593988
594188
  raw = JSON.parse(trimmed);
593989
594189
  } catch {
593990
- return null;
594190
+ return { intent: trimmed };
593991
594191
  }
593992
594192
  }
593993
594193
  if (!raw || typeof raw !== "object" || Array.isArray(raw))
@@ -594004,8 +594204,9 @@ ${marker}` : marker);
594004
594204
  }
594005
594205
  _toolActionReasonForCall(toolName, args) {
594006
594206
  const explicit = this._extractToolActionReason(args);
594007
- if (explicit)
594008
- return explicit;
594207
+ if (explicit) {
594208
+ return this._completeToolActionReason(toolName, args, explicit);
594209
+ }
594009
594210
  if (this._shouldSynthesizeMissingToolActionReason(toolName, args)) {
594010
594211
  return this._synthesizeStateUpdateToolActionReason(toolName, args);
594011
594212
  }
@@ -594013,6 +594214,48 @@ ${marker}` : marker);
594013
594214
  return null;
594014
594215
  return this._synthesizeReadOnlyToolActionReason(toolName, args);
594015
594216
  }
594217
+ _completeToolActionReason(toolName, args, partial) {
594218
+ const canonical = this.lookupRegisteredTool(toolName)?.name ?? toolName;
594219
+ const goal = this._taskState.currentStep || this._taskState.nextAction || this._taskState.goal || this._taskState.originalGoal || "current task";
594220
+ const targetPaths = this._extractToolTargetPaths(canonical, args).slice(0, 3);
594221
+ const targetText = targetPaths.length > 0 ? ` for ${targetPaths.join(", ")}` : "";
594222
+ const scope = TOOL_ACTION_REASON_SCOPES.has(String(partial.scope ?? "")) ? String(partial.scope) : this._defaultToolActionReasonScope(canonical);
594223
+ return {
594224
+ task_anchor: cleanToolActionReasonField(partial.task_anchor) || String(goal).replace(/\s+/g, " ").trim().slice(0, 160),
594225
+ intent: cleanToolActionReasonField(partial.intent) || `Run ${canonical}${targetText} for the active task.`,
594226
+ evidence: cleanToolActionReasonField(partial.evidence) || "Runtime completed partial public action metadata from the tool call contract before execution.",
594227
+ expected_result: cleanToolActionReasonField(partial.expected_result) || this._defaultToolActionExpectedResult(canonical, targetText),
594228
+ scope
594229
+ };
594230
+ }
594231
+ _defaultToolActionReasonScope(toolName) {
594232
+ if (toolName === "file_edit" || toolName === "file_patch" || toolName === "batch_edit") {
594233
+ return "targeted_patch";
594234
+ }
594235
+ if (toolName === "create_structured_file")
594236
+ return "new_file";
594237
+ if (toolName === "task_complete")
594238
+ return "completion";
594239
+ if (READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS.has(toolName))
594240
+ return "discovery";
594241
+ return "other";
594242
+ }
594243
+ _defaultToolActionExpectedResult(toolName, targetText) {
594244
+ switch (toolName) {
594245
+ case "file_edit":
594246
+ return `Requested exact edit is applied${targetText}, or a precise edit error is returned.`;
594247
+ case "file_patch":
594248
+ return `Requested line patch is applied${targetText}, or a precise patch error is returned.`;
594249
+ case "batch_edit":
594250
+ return `Requested edit batch is applied${targetText}, or per-edit failure evidence is returned.`;
594251
+ case "file_write":
594252
+ return `Requested file content is written${targetText}, or a precise write error is returned.`;
594253
+ case "shell":
594254
+ return "Command exits with output evidence for the next decision.";
594255
+ default:
594256
+ return `${toolName} completes and returns success or failure evidence.`;
594257
+ }
594258
+ }
594016
594259
  _shouldSynthesizeMissingToolActionReason(toolName, _args) {
594017
594260
  if (this._toolActionReasonPolicy(toolName, _args) === "disabled") {
594018
594261
  return false;
@@ -594063,24 +594306,13 @@ ${marker}` : marker);
594063
594306
  `scope must be one of: ${Array.from(TOOL_ACTION_REASON_SCOPES).join(", ")}.`
594064
594307
  ].join("\n");
594065
594308
  }
594066
- const reason = this._extractToolActionReason(args);
594067
- if (!reason) {
594309
+ const partialReason = this._extractToolActionReason(args);
594310
+ if (!partialReason) {
594068
594311
  if (policy === "optional")
594069
594312
  return null;
594070
594313
  return "[TOOL ACTION REASON CONTRACT] action_reason must be an object with compact public task-state fields.";
594071
594314
  }
594072
- const missing = [
594073
- "task_anchor",
594074
- "intent",
594075
- "evidence",
594076
- "expected_result",
594077
- "scope"
594078
- ].filter((key) => reason[key].length < 3);
594079
- if (missing.length > 0) {
594080
- if (policy === "optional")
594081
- return null;
594082
- return `[TOOL ACTION REASON CONTRACT] action_reason missing non-empty field(s): ${missing.join(", ")}.`;
594083
- }
594315
+ const reason = this._completeToolActionReason(toolName, args, partialReason);
594084
594316
  if (!TOOL_ACTION_REASON_SCOPES.has(reason.scope)) {
594085
594317
  if (policy === "optional")
594086
594318
  return null;
@@ -688031,7 +688263,7 @@ function renderTelegramSubAgentError(username, error) {
688031
688263
  `${c3.red("✘")} ${c3.bold(`@${username}`)}: ${c3.dim(preview)}`
688032
688264
  );
688033
688265
  }
688034
- var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, TELEGRAM_EVIDENCE_SUFFICIENCY_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_LINK_INTEGRITY_CONTRACT, TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT, TELEGRAM_INTERACTION_DECISION_MINIMAL_SCHEMA, TELEGRAM_INTERACTION_DECISION_REPAIR_SCHEMA, TELEGRAM_CHAT_REPLY_RESPONSE_FORMAT, TELEGRAM_SPACED_URL_RE, TELEGRAM_HTTP_URL_RE, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_FAST_OPTIONS, TELEGRAM_ADMIN_EVIDENCE_OPTIONS, TELEGRAM_SUB_AGENT_DEFAULT_LIMIT, TELEGRAM_SUB_AGENT_MAX_LIMIT, TELEGRAM_SUB_AGENT_BURST_CONTEXT_LIMIT, TELEGRAM_ADMIN_LIVE_PANEL_PAGES, TELEGRAM_ADMIN_LIVE_MUTATION_TOOLS, TELEGRAM_PUBLIC_HELP_COMMANDS2, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_ALLOWED_UPDATES, TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS, TELEGRAM_ROUTER_AUTO_MIN_PARAMETERS_B, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
688266
+ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, TELEGRAM_PUBLIC_VISION_STACK_CONTRACT, TELEGRAM_EVIDENCE_SUFFICIENCY_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT, TELEGRAM_LINK_INTEGRITY_CONTRACT, TELEGRAM_INTERACTION_DECISION_RESPONSE_FORMAT, TELEGRAM_INTERACTION_DECISION_MINIMAL_SCHEMA, TELEGRAM_INTERACTION_DECISION_REPAIR_SCHEMA, TELEGRAM_CHAT_REPLY_RESPONSE_FORMAT, TELEGRAM_SPACED_URL_RE, TELEGRAM_HTTP_URL_RE, TELEGRAM_STUCK_SELF_TALK_PREFIXES, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_ASSOCIATIVE_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_USER_FACT_LIMIT, TELEGRAM_ASSOCIATIVE_ACTION_LIMIT, TELEGRAM_ASSOCIATIVE_RELATION_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_MEMORY_GENERIC_QUERY_TOKENS, TELEGRAM_SUB_AGENT_BOUNDED_OPTIONS, TELEGRAM_PUBLIC_FAST_OPTIONS, TELEGRAM_ADMIN_EVIDENCE_OPTIONS, TELEGRAM_SUB_AGENT_DEFAULT_LIMIT, TELEGRAM_SUB_AGENT_MAX_LIMIT, TELEGRAM_SUB_AGENT_BURST_CONTEXT_LIMIT, TELEGRAM_ADMIN_LIVE_PANEL_PAGES, TELEGRAM_ADMIN_LIVE_MUTATION_TOOLS, TELEGRAM_PUBLIC_HELP_COMMANDS2, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_PUBLIC_BOT_COMMAND_NAMES, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TELEGRAM_ALLOWED_UPDATES, TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS, TELEGRAM_PUBLIC_TOOL_QUOTAS, TelegramBridge;
688035
688267
  var init_telegram_bridge = __esm({
688036
688268
  "packages/cli/src/tui/telegram-bridge.ts"() {
688037
688269
  "use strict";
@@ -688559,7 +688791,6 @@ Telegram link integrity contract:
688559
688791
  "message_reaction_count"
688560
688792
  ];
688561
688793
  TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS = 50;
688562
- TELEGRAM_ROUTER_AUTO_MIN_PARAMETERS_B = 8;
688563
688794
  TELEGRAM_PUBLIC_TOOL_QUOTAS = {
688564
688795
  web: { limit: 20, windowMs: 60 * 6e4 },
688565
688796
  media: { limit: 30, windowMs: 60 * 6e4 },
@@ -688614,7 +688845,6 @@ Telegram link integrity contract:
688614
688845
  pollLoopPromise = null;
688615
688846
  pollFatalNotified = false;
688616
688847
  lastUpdateId = 0;
688617
- telegramRouterModelCache = null;
688618
688848
  /**
688619
688849
  * Snapshot of the main agent inference config captured at construction. The
688620
688850
  * effective `agentConfig` is this base overlaid with the Telegram-isolated
@@ -688865,7 +689095,6 @@ Telegram link integrity contract:
688865
689095
  ...next.backendUrl !== void 0 ? { apiKey: next.apiKey ?? "" } : {}
688866
689096
  };
688867
689097
  this.agentConfig = effective;
688868
- this.telegramRouterModelCache = null;
688869
689098
  return effective;
688870
689099
  }
688871
689100
  /** The Telegram-isolated inference override currently in effect (if any). */
@@ -689659,9 +689888,17 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
689659
689888
  this.refreshActiveTelegramInteractionCount();
689660
689889
  return queued;
689661
689890
  }
689891
+ telegramMessageExplicitlyAddressesBot(msg) {
689892
+ if (this.telegramMessageRepliesToBot(msg)) return true;
689893
+ const bot = this.state.botUsername.trim().replace(/^@/, "").toLowerCase();
689894
+ if (!bot) return false;
689895
+ return (msg.mentionedUsernames ?? []).some(
689896
+ (name10) => name10.trim().replace(/^@/, "").toLowerCase() === bot
689897
+ );
689898
+ }
689662
689899
  shouldFailOpenTelegramRouterUnavailable(msg, toolContext) {
689663
689900
  if (msg.isBot) return false;
689664
- return toolContext === "telegram-admin-dm" || msg.chatType === "private";
689901
+ return toolContext === "telegram-admin-dm" || msg.chatType === "private" || this.telegramMessageExplicitlyAddressesBot(msg);
689665
689902
  }
689666
689903
  buildTelegramRouterUnavailableDecision(msg, toolContext, params) {
689667
689904
  const forcedRoute = this.interactionMode === "chat" || this.interactionMode === "action" ? this.interactionMode : null;
@@ -689674,9 +689911,9 @@ No scoped reflection artifact exists yet for this chat. Use <code>/reflect</code
689674
689911
  route,
689675
689912
  shouldReply: failOpen,
689676
689913
  confidence: failOpen ? 0.55 : 0,
689677
- reason: failOpen ? `${params.reason}; direct private/admin delivery uses degraded visible reply instead of silent halt` : params.reason,
689914
+ reason: failOpen ? `${params.reason}; direct/high-salience delivery uses degraded visible reply instead of silent halt` : params.reason,
689678
689915
  source: "inference-unavailable",
689679
- silentDisposition: failOpen ? "router unavailable; attempting a visible degraded reply for a direct private/admin message" : params.silentDisposition ?? "retained as context without replying because the router decision could not be derived",
689916
+ silentDisposition: failOpen ? "router unavailable; attempting a visible degraded reply for a direct/high-salience message" : params.silentDisposition ?? "retained as context without replying because the router decision could not be derived",
689680
689917
  diagnosticNote: params.diagnosticNote,
689681
689918
  raw: params.raw
689682
689919
  };
@@ -695275,90 +695512,9 @@ ${retryText}`,
695275
695512
  this.dispatchQueuedTelegramSessionWorkSoon();
695276
695513
  }
695277
695514
  }
695278
- telegramRouterAutoModelEnabled() {
695279
- const raw = (process.env["OMNIUS_TG_ROUTER_AUTO_MODEL"] ?? "").trim().toLowerCase();
695280
- return raw === "1" || raw === "true" || raw === "on";
695281
- }
695282
- telegramRouterCandidateModels() {
695283
- const raw = (process.env["OMNIUS_TG_ROUTER_MODEL_CANDIDATES"] ?? "").trim();
695284
- const candidates = raw ? raw.split(/[,\s]+/).map((part) => part.trim()).filter(Boolean) : [];
695285
- return Array.from(new Set(candidates));
695286
- }
695287
- telegramRouterAllowThinkHeavyAutoModels() {
695288
- const raw = (process.env["OMNIUS_TG_ROUTER_ALLOW_THINK_MODELS"] ?? "").trim().toLowerCase();
695289
- return raw === "1" || raw === "true" || raw === "on";
695290
- }
695291
- telegramRouterModelLooksThinkHeavy(name10) {
695292
- return /\b(?:qwen3|qwq|deepseek-r1|r1-|reasoning)\b/i.test(name10);
695293
- }
695294
- normalizeOllamaModelNameForMatch(name10) {
695295
- return name10.trim().toLowerCase().replace(/:latest$/, "");
695296
- }
695297
- async fetchOllamaInstalledModels(baseUrl2) {
695298
- const url = `${baseUrl2.replace(/\/+$/, "")}/api/tags`;
695299
- const timeoutFn = AbortSignal.timeout;
695300
- const res = await fetch(url, {
695301
- signal: typeof timeoutFn === "function" ? timeoutFn(2e3) : void 0
695302
- });
695303
- if (!res.ok)
695304
- throw new Error(`ollama /api/tags returned HTTP ${res.status}`);
695305
- const data = await res.json();
695306
- return Array.isArray(data.models) ? data.models.map((model) => ({
695307
- name: typeof model.name === "string" ? model.name : "",
695308
- sizeBytes: typeof model.size === "number" ? model.size : void 0,
695309
- parameterSize: typeof model.details?.parameter_size === "string" ? model.details.parameter_size : void 0
695310
- })).filter((model) => Boolean(model.name)) : [];
695311
- }
695312
- telegramModelParameterBillions(model) {
695313
- const haystack = `${model.name} ${model.parameterSize ?? ""}`.toLowerCase();
695314
- const billion = haystack.match(/(\d+(?:\.\d+)?)\s*(?:b|bn)\b/);
695315
- if (billion) return Number(billion[1]);
695316
- const million = haystack.match(/(\d+(?:\.\d+)?)\s*m\b/);
695317
- if (million) return Number(million[1]) / 1e3;
695318
- return null;
695319
- }
695320
- scoreTelegramInstalledRouterModel(model) {
695321
- const name10 = model.name.toLowerCase();
695322
- if (/(?:embed|embedding|nomic|bge|e5-|clip|rerank|moondream|llava|vision|vl\b|minicpm|whisper|tts|sdxl|diffusion)/i.test(
695323
- name10
695324
- )) {
695325
- return Number.NEGATIVE_INFINITY;
695326
- }
695327
- const paramsB = this.telegramModelParameterBillions(model);
695328
- if (paramsB !== null && paramsB < TELEGRAM_ROUTER_AUTO_MIN_PARAMETERS_B) {
695329
- return Number.NEGATIVE_INFINITY;
695330
- }
695331
- if (paramsB === null && (model.sizeBytes ?? 0) < 5e9) {
695332
- return Number.NEGATIVE_INFINITY;
695333
- }
695334
- let score = 0;
695335
- if (paramsB !== null) score += paramsB * 10;
695336
- else score += Math.min(80, (model.sizeBytes ?? 0) / 1e9);
695337
- if (/qwen|huihui|qwq/i.test(name10)) score += 80;
695338
- else if (/deepseek|nemotron|llama|mistral|mixtral|command-r|devstral/i.test(name10))
695339
- score += 50;
695340
- else if (/gemma/i.test(name10)) score += 20;
695341
- if (/:latest$/i.test(model.name)) score += 1;
695342
- if (this.telegramRouterModelLooksThinkHeavy(model.name) && !this.telegramRouterAllowThinkHeavyAutoModels()) {
695343
- score -= 15;
695344
- }
695345
- return score;
695346
- }
695347
695515
  async resolveTelegramRouterBackend(config) {
695348
695516
  const explicit = (process.env["OMNIUS_TG_ROUTER_MODEL"] ?? "").trim();
695349
- if (explicit && !/^(?:0|false|off|same|main)$/i.test(explicit)) {
695350
- return {
695351
- backend: new OllamaAgenticBackend(
695352
- config.backendUrl,
695353
- explicit,
695354
- config.apiKey
695355
- ),
695356
- model: explicit,
695357
- source: "env",
695358
- detail: "OMNIUS_TG_ROUTER_MODEL"
695359
- };
695360
- }
695361
- if (config.backendType !== "ollama") {
695517
+ if (/^(?:same|main)$/i.test(explicit)) {
695362
695518
  return {
695363
695519
  backend: new OllamaAgenticBackend(
695364
695520
  config.backendUrl,
@@ -695366,128 +695522,23 @@ ${retryText}`,
695366
695522
  config.apiKey
695367
695523
  ),
695368
695524
  model: config.model,
695369
- source: "main"
695370
- };
695371
- }
695372
- const autoModelEnabled = this.telegramRouterAutoModelEnabled();
695373
- const candidateFilter = this.telegramRouterCandidateModels();
695374
- const candidates = new Set(
695375
- candidateFilter.map(
695376
- (candidate) => this.normalizeOllamaModelNameForMatch(candidate)
695377
- )
695378
- );
695379
- const cacheKey = `${config.backendUrl}
695380
- ${config.model}
695381
- auto=${autoModelEnabled ? "1" : "0"}
695382
- ${candidateFilter.join(",")}`;
695383
- const now2 = Date.now();
695384
- if (this.telegramRouterModelCache && this.telegramRouterModelCache.cacheKey === cacheKey && now2 - this.telegramRouterModelCache.atMs < 6e4) {
695385
- const cached = this.telegramRouterModelCache;
695386
- return {
695387
- backend: new OllamaAgenticBackend(
695388
- config.backendUrl,
695389
- cached.model,
695390
- config.apiKey
695391
- ),
695392
- model: cached.model,
695393
- source: cached.source,
695394
- detail: cached.detail
695395
- };
695396
- }
695397
- if (!autoModelEnabled) {
695398
- const detail2 = "Telegram router auto-model selection is disabled by default; using main model";
695399
- this.telegramRouterModelCache = {
695400
- cacheKey,
695401
- atMs: now2,
695402
- model: config.model,
695403
695525
  source: "main",
695404
- detail: detail2
695526
+ detail: `OMNIUS_TG_ROUTER_MODEL=${explicit}`
695405
695527
  };
695528
+ }
695529
+ if (explicit && !/^(?:0|false|off)$/i.test(explicit)) {
695406
695530
  return {
695407
695531
  backend: new OllamaAgenticBackend(
695408
695532
  config.backendUrl,
695409
- config.model,
695533
+ explicit,
695410
695534
  config.apiKey
695411
695535
  ),
695412
- model: config.model,
695413
- source: "main",
695414
- detail: detail2
695536
+ model: explicit,
695537
+ source: "env",
695538
+ detail: "OMNIUS_TG_ROUTER_MODEL"
695415
695539
  };
695416
695540
  }
695417
- try {
695418
- const installed = await this.fetchOllamaInstalledModels(
695419
- config.backendUrl
695420
- );
695421
- const installedByNormalized = /* @__PURE__ */ new Map();
695422
- for (const model of installed) {
695423
- installedByNormalized.set(
695424
- this.normalizeOllamaModelNameForMatch(model.name),
695425
- model
695426
- );
695427
- }
695428
- const installedMain = installedByNormalized.get(
695429
- this.normalizeOllamaModelNameForMatch(config.model)
695430
- );
695431
- if (installedMain) {
695432
- const resolved = {
695433
- cacheKey,
695434
- atMs: now2,
695435
- model: installedMain.name,
695436
- source: "main",
695437
- detail: "main Telegram router model is installed in Ollama /api/tags; using main model by policy"
695438
- };
695439
- this.telegramRouterModelCache = resolved;
695440
- return {
695441
- backend: new OllamaAgenticBackend(
695442
- config.backendUrl,
695443
- installedMain.name,
695444
- config.apiKey
695445
- ),
695446
- model: installedMain.name,
695447
- source: "main",
695448
- detail: resolved.detail
695449
- };
695450
- }
695451
- if (autoModelEnabled) {
695452
- const pool3 = candidateFilter.length > 0 ? installed.filter(
695453
- (model) => candidates.has(
695454
- this.normalizeOllamaModelNameForMatch(model.name)
695455
- )
695456
- ) : installed;
695457
- const selected = pool3.map((model) => ({
695458
- model,
695459
- score: this.scoreTelegramInstalledRouterModel(model)
695460
- })).filter((entry) => Number.isFinite(entry.score)).sort((a2, b) => b.score - a2.score)[0]?.model;
695461
- if (selected) {
695462
- const resolved = {
695463
- cacheKey,
695464
- atMs: now2,
695465
- model: selected.name,
695466
- source: "auto-installed",
695467
- detail: `main Telegram router model ${JSON.stringify(config.model)} was not found in Ollama /api/tags; selected best installed capable router model dynamically (minimum ${TELEGRAM_ROUTER_AUTO_MIN_PARAMETERS_B}B, excludes embeddings/vision/tiny models)`
695468
- };
695469
- this.telegramRouterModelCache = resolved;
695470
- return {
695471
- backend: new OllamaAgenticBackend(
695472
- config.backendUrl,
695473
- selected.name,
695474
- config.apiKey
695475
- ),
695476
- model: selected.name,
695477
- source: "auto-installed",
695478
- detail: resolved.detail
695479
- };
695480
- }
695481
- }
695482
- } catch (err) {
695483
- const detail2 = `router model auto-detect failed: ${err instanceof Error ? err.message : String(err)}`;
695484
- this.telegramRouterModelCache = {
695485
- cacheKey,
695486
- atMs: now2,
695487
- model: config.model,
695488
- source: "main",
695489
- detail: detail2
695490
- };
695541
+ if (config.backendType !== "ollama") {
695491
695542
  return {
695492
695543
  backend: new OllamaAgenticBackend(
695493
695544
  config.backendUrl,
@@ -695495,18 +695546,9 @@ ${candidateFilter.join(",")}`;
695495
695546
  config.apiKey
695496
695547
  ),
695497
695548
  model: config.model,
695498
- source: "main",
695499
- detail: detail2
695549
+ source: "main"
695500
695550
  };
695501
695551
  }
695502
- const detail = `no installed capable Telegram router model met the dynamic minimum (${TELEGRAM_ROUTER_AUTO_MIN_PARAMETERS_B}B); using main model`;
695503
- this.telegramRouterModelCache = {
695504
- cacheKey,
695505
- atMs: now2,
695506
- model: config.model,
695507
- source: "main",
695508
- detail
695509
- };
695510
695552
  return {
695511
695553
  backend: new OllamaAgenticBackend(
695512
695554
  config.backendUrl,
@@ -695515,7 +695557,7 @@ ${candidateFilter.join(",")}`;
695515
695557
  ),
695516
695558
  model: config.model,
695517
695559
  source: "main",
695518
- detail
695560
+ detail: "using selected Telegram model; automatic router model fallback is disabled"
695519
695561
  };
695520
695562
  }
695521
695563
  async inferTelegramInteractionDecision(msg, toolContext) {
@@ -695774,7 +695816,7 @@ ${this.quoteTelegramContextBlock(msg.text, 1200)}`
695774
695816
  diagnosticNote: this.composeTelegramRouterDiagnosticNote(
695775
695817
  void 0,
695776
695818
  failureNarrative2,
695777
- "router produced no visible attention decision content; repair/strict retry skipped for direct private/admin fail-open",
695819
+ "router produced no visible attention decision content; repair/strict retry skipped for direct/high-salience fail-open",
695778
695820
  diagnostics
695779
695821
  ),
695780
695822
  raw: text2
@@ -737632,6 +737674,11 @@ function createTaskCompleteTool(modelTier2, repoRoot, skipSessionGuard) {
737632
737674
  required: ["summary"]
737633
737675
  },
737634
737676
  async execute(args) {
737677
+ const completionStatus = typeof args["status"] === "string" ? args["status"].trim().toLowerCase().replace(/[\s-]+/g, "_") : "";
737678
+ const summaryText = typeof args["summary"] === "string" ? args["summary"] : "";
737679
+ const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(
737680
+ summaryText
737681
+ );
737635
737682
  if (_interactiveSessionActive && !skipSessionGuard) {
737636
737683
  return {
737637
737684
  success: false,
@@ -737646,7 +737693,7 @@ function createTaskCompleteTool(modelTier2, repoRoot, skipSessionGuard) {
737646
737693
  const incomplete = todos.filter(
737647
737694
  (t2) => t2.status === "pending" || t2.status === "in_progress" || t2.status === "blocked"
737648
737695
  );
737649
- if (incomplete.length > 0) {
737696
+ if (incomplete.length > 0 && !reportsDegradedCompletion) {
737650
737697
  const incompleteList = incomplete.slice(0, 20).map(
737651
737698
  (t2) => ` - [${t2.status}] ${t2.content}${t2.blocker ? ` (blocked: ${t2.blocker})` : ""}`
737652
737699
  ).join("\n");
@@ -737672,9 +737719,11 @@ ${incompleteList}${more}
737672
737719
  ` + guidance
737673
737720
  };
737674
737721
  }
737675
- try {
737676
- writeTodos(sessionId, []);
737677
- } catch {
737722
+ if (incomplete.length === 0) {
737723
+ try {
737724
+ writeTodos(sessionId, []);
737725
+ } catch {
737726
+ }
737678
737727
  }
737679
737728
  }
737680
737729
  } catch {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.466",
3
+ "version": "1.0.468",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.466",
9
+ "version": "1.0.468",
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.466",
3
+ "version": "1.0.468",
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",