omnius 1.0.465 → 1.0.467

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
@@ -566840,7 +566840,9 @@ function loadIgnoreFile(path12) {
566840
566840
  return [];
566841
566841
  }
566842
566842
  }
566843
- function shouldIgnore(relPath, base3, patterns) {
566843
+ function shouldIgnore(relPath, base3, absPath, patterns) {
566844
+ if (existsSync92(join103(absPath, "pyvenv.cfg")))
566845
+ return true;
566844
566846
  for (const p2 of patterns) {
566845
566847
  if (!p2)
566846
566848
  continue;
@@ -566873,16 +566875,32 @@ function scanWorkspace(opts) {
566873
566875
  let dirsVisited = 0;
566874
566876
  const stack = [root];
566875
566877
  const visited = /* @__PURE__ */ new Set();
566876
- while (stack.length > 0) {
566877
- if (files.length >= maxFiles) {
566878
- truncated = true;
566879
- break;
566878
+ const considerFile = (file) => {
566879
+ totalBytes += file.bytes;
566880
+ if (files.length < maxFiles) {
566881
+ files.push(file);
566882
+ return;
566880
566883
  }
566884
+ truncated = true;
566885
+ let oldestIndex = 0;
566886
+ for (let i2 = 1; i2 < files.length; i2++) {
566887
+ if (files[i2].mtimeMs < files[oldestIndex].mtimeMs)
566888
+ oldestIndex = i2;
566889
+ }
566890
+ if (file.mtimeMs > files[oldestIndex].mtimeMs) {
566891
+ files[oldestIndex] = file;
566892
+ }
566893
+ };
566894
+ while (stack.length > 0) {
566881
566895
  const dir = stack.pop();
566882
566896
  if (visited.has(dir))
566883
566897
  continue;
566884
566898
  visited.add(dir);
566885
566899
  dirsVisited++;
566900
+ if (dirsVisited > maxDirs) {
566901
+ truncated = true;
566902
+ break;
566903
+ }
566886
566904
  if (dirs.length < maxDirs) {
566887
566905
  try {
566888
566906
  const st = statSync32(dir);
@@ -566901,15 +566919,12 @@ function scanWorkspace(opts) {
566901
566919
  continue;
566902
566920
  }
566903
566921
  entries.sort();
566922
+ const dirsToVisit = [];
566904
566923
  for (const entry of entries) {
566905
- if (files.length >= maxFiles) {
566906
- truncated = true;
566907
- break;
566908
- }
566909
566924
  const abs = join103(dir, entry);
566910
566925
  const rel = relative8(root, abs);
566911
566926
  const base3 = basename22(abs);
566912
- if (shouldIgnore(rel, base3, patterns))
566927
+ if (shouldIgnore(rel, base3, abs, patterns))
566913
566928
  continue;
566914
566929
  let st;
566915
566930
  try {
@@ -566918,13 +566933,15 @@ function scanWorkspace(opts) {
566918
566933
  continue;
566919
566934
  }
566920
566935
  if (st.isDirectory()) {
566921
- stack.push(abs);
566936
+ dirsToVisit.push(abs);
566922
566937
  continue;
566923
566938
  }
566924
566939
  if (!st.isFile())
566925
566940
  continue;
566926
- files.push({ abs, rel, bytes: st.size, mtimeMs: st.mtimeMs });
566927
- totalBytes += st.size;
566941
+ considerFile({ abs, rel, bytes: st.size, mtimeMs: st.mtimeMs });
566942
+ }
566943
+ for (let i2 = dirsToVisit.length - 1; i2 >= 0; i2--) {
566944
+ stack.push(dirsToVisit[i2]);
566928
566945
  }
566929
566946
  }
566930
566947
  files.sort((a2, b) => a2.rel.localeCompare(b.rel));
@@ -566955,6 +566972,7 @@ function ensureTreeNode(root, rel) {
566955
566972
  function renderWorkspaceTree(scan, opts = {}) {
566956
566973
  const maxLines = Math.max(10, Math.min(2e3, opts.maxLines ?? 120));
566957
566974
  const includeFileSizes = opts.includeFileSizes !== false;
566975
+ const sortBy = opts.sortBy ?? "path";
566958
566976
  const root = {
566959
566977
  name: ".",
566960
566978
  children: /* @__PURE__ */ new Map(),
@@ -566975,10 +566993,38 @@ function renderWorkspaceTree(scan, opts = {}) {
566975
566993
  directory: false
566976
566994
  });
566977
566995
  }
566996
+ const annotate = (node) => {
566997
+ if (!node.directory && node.file) {
566998
+ node.latestMtimeMs = node.file.mtimeMs;
566999
+ node.selectedFileCount = 1;
567000
+ return { latest: node.file.mtimeMs, files: 1 };
567001
+ }
567002
+ let latest = 0;
567003
+ let files = 0;
567004
+ for (const child of node.children.values()) {
567005
+ const stats = annotate(child);
567006
+ latest = Math.max(latest, stats.latest);
567007
+ files += stats.files;
567008
+ }
567009
+ node.latestMtimeMs = latest;
567010
+ node.selectedFileCount = files;
567011
+ return { latest, files };
567012
+ };
567013
+ annotate(root);
566978
567014
  const lines = ["./"];
566979
567015
  let truncated = false;
566980
567016
  const walk2 = (node, depth) => {
566981
567017
  const children2 = [...node.children.values()].sort((a2, b) => {
567018
+ if (sortBy === "recent") {
567019
+ const aFiles = a2.selectedFileCount ?? 0;
567020
+ const bFiles = b.selectedFileCount ?? 0;
567021
+ if (aFiles > 0 !== bFiles > 0)
567022
+ return aFiles > 0 ? -1 : 1;
567023
+ const aMtime = a2.latestMtimeMs ?? 0;
567024
+ const bMtime = b.latestMtimeMs ?? 0;
567025
+ if (aMtime !== bMtime)
567026
+ return bMtime - aMtime;
567027
+ }
566982
567028
  if (a2.directory !== b.directory)
566983
567029
  return a2.directory ? -1 : 1;
566984
567030
  return a2.name.localeCompare(b.name);
@@ -567057,12 +567103,23 @@ var init_world_state_disk_scan = __esm({
567057
567103
  ".venv",
567058
567104
  "venv",
567059
567105
  "env",
567106
+ "site-packages",
567107
+ ".eggs",
567108
+ "__pypackages__",
567109
+ // Embedded/device and package-manager dependency/build caches
567110
+ ".pio",
567111
+ ".platformio",
567112
+ "libdeps",
567060
567113
  // Rust / Go test / build outputs are caught by "target"/"build"
567061
567114
  // Editor swap / lock-only files — case-by-case skip, see file filter below
567062
567115
  // Omnius's own state dirs
567063
567116
  ".omnius",
567064
567117
  ".aiwg",
567065
567118
  ".ralph",
567119
+ ".claude",
567120
+ ".codex",
567121
+ ".factory",
567122
+ ".warp",
567066
567123
  // Next/Vite/etc. caches (broadly cross-framework — every JS bundler has one)
567067
567124
  ".next",
567068
567125
  ".nuxt",
@@ -575173,6 +575230,9 @@ function violatesDirective(directive, input) {
575173
575230
  const summaryReportsDegraded = /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summaryText);
575174
575231
  const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || summaryReportsDegraded;
575175
575232
  if (directive.requiredNextAction === "report_blocked" || directive.requiredNextAction === "report_incomplete" || directive.state === "terminal_incomplete") {
575233
+ const completionGateDirective = directive.state !== "terminal_incomplete" && directive.forbiddenActionFamilies.includes("task_complete");
575234
+ if (completionGateDirective)
575235
+ return false;
575176
575236
  return !reportsDegradedCompletion;
575177
575237
  }
575178
575238
  return false;
@@ -575432,10 +575492,12 @@ var init_focusSupervisor = __esm({
575432
575492
  failureFamilies = /* @__PURE__ */ new Map();
575433
575493
  ignoredDirectiveStreak = 0;
575434
575494
  lastIgnoredDirectiveTurn = null;
575495
+ availableTools = null;
575435
575496
  constructor(options2 = {}) {
575436
575497
  this.mode = options2.mode ?? "auto";
575437
575498
  this.modelTier = options2.modelTier ?? "large";
575438
575499
  this.repeatGateMax = Math.max(0, Math.floor(options2.repeatGateMax ?? 3));
575500
+ this.availableTools = options2.availableTools ? new Set(Array.from(options2.availableTools)) : null;
575439
575501
  }
575440
575502
  get enabled() {
575441
575503
  return this.mode !== "off";
@@ -575443,6 +575505,15 @@ var init_focusSupervisor = __esm({
575443
575505
  get currentDirective() {
575444
575506
  return this.directive;
575445
575507
  }
575508
+ hasTool(name10) {
575509
+ return this.availableTools === null || this.availableTools.has(name10);
575510
+ }
575511
+ resolveRequiredNextAction(action) {
575512
+ if (action === "update_todos" && !this.hasTool("todo_write")) {
575513
+ return "report_blocked";
575514
+ }
575515
+ return action;
575516
+ }
575446
575517
  snapshot() {
575447
575518
  return {
575448
575519
  mode: this.mode,
@@ -575515,7 +575586,7 @@ var init_focusSupervisor = __esm({
575515
575586
  prior.ignoredCount++;
575516
575587
  this.ignoredDirectiveStreak++;
575517
575588
  this.lastIgnoredDirectiveTurn = input.turn;
575518
- if ((prior.requiredNextAction === "read_authoritative_target" || prior.requiredNextAction === "use_cached_evidence") && input.cachedResult && (input.duplicateHitCount ?? 0) >= Math.max(1, this.repeatGateMax - 1)) {
575589
+ if (prior.requiredNextAction === "read_authoritative_target" && input.cachedResult && (input.duplicateHitCount ?? 0) >= Math.max(1, this.repeatGateMax - 1)) {
575519
575590
  const deadlockDirective = this.setDirective({
575520
575591
  turn: input.turn,
575521
575592
  state: "terminal_incomplete",
@@ -575595,14 +575666,15 @@ var init_focusSupervisor = __esm({
575595
575666
  return this.inject(directive, `[FOCUS SUPERVISOR] ${directive.reason}. Required next action: ${directive.requiredNextAction}.`);
575596
575667
  }
575597
575668
  if (this.hasLowSignalContext(input.context) && input.isReadLike && !(this.directive?.requiredNextAction === "read_authoritative_target" || this.directive?.requiredNextAction === "use_cached_evidence")) {
575669
+ const requiredNextAction = this.resolveRequiredNextAction("update_todos");
575598
575670
  const directive = this.setDirective({
575599
575671
  turn: input.turn,
575600
575672
  state: "forced_replan",
575601
575673
  reason: "raw discovery is dominating active evidence in the context window",
575602
- requiredNextAction: "update_todos",
575674
+ requiredNextAction,
575603
575675
  forbiddenActionFamilies: [family]
575604
575676
  });
575605
- 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.");
575677
+ 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.");
575606
575678
  }
575607
575679
  this.lastDecision = "pass";
575608
575680
  this.lastReason = "";
@@ -575682,22 +575754,24 @@ var init_focusSupervisor = __esm({
575682
575754
  actionFamily(input.toolName, input.args),
575683
575755
  input.toolName
575684
575756
  ]) : input.toolName === "shell" ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)];
575757
+ const requiredNextAction = ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "use_cached_evidence" : this.resolveRequiredNextAction("update_todos");
575685
575758
  this.setDirective({
575686
575759
  turn: input.turn,
575687
575760
  state: "forced_replan",
575688
575761
  reason: `same ${input.toolName} failure family repeated ${next.count} times: ${next.sample}`,
575689
- requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : input.toolName === "shell" ? "use_cached_evidence" : "update_todos",
575762
+ requiredNextAction,
575690
575763
  forbiddenActionFamilies: forbidden
575691
575764
  });
575692
575765
  } else if (next.count === 1 && !this.directive) {
575693
575766
  const ambiguousEditFailure = isEditTool(input.toolName) && next.errorClass === "stale_ambiguous_target";
575694
575767
  const staleEditFailure = isEditTool(input.toolName) && next.errorClass.startsWith("stale_");
575695
575768
  const shellFailure = input.toolName === "shell";
575769
+ const requiredNextAction = ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : shellFailure ? "use_cached_evidence" : this.resolveRequiredNextAction("update_todos");
575696
575770
  this.setDirective({
575697
575771
  turn: input.turn,
575698
575772
  state: shellFailure ? "cached_evidence" : "warn",
575699
575773
  reason: `first ${input.toolName} failure (${next.errorClass}): ${next.sample}`,
575700
- requiredNextAction: ambiguousEditFailure ? "disambiguate_edit_match" : staleEditFailure ? "read_authoritative_target" : shellFailure ? "use_cached_evidence" : "update_todos",
575774
+ requiredNextAction,
575701
575775
  forbiddenActionFamilies: shellFailure ? [actionFamily(input.toolName, input.args)] : [actionFamily(input.toolName, input.args)]
575702
575776
  });
575703
575777
  }
@@ -575709,7 +575783,7 @@ var init_focusSupervisor = __esm({
575709
575783
  turn: input.turn,
575710
575784
  state: "verify_or_block",
575711
575785
  reason: input.reason,
575712
- requiredNextAction: input.requiredNextAction ?? "run_verification",
575786
+ requiredNextAction: this.resolveRequiredNextAction(input.requiredNextAction ?? "run_verification"),
575713
575787
  forbiddenActionFamilies: ["task_complete"]
575714
575788
  });
575715
575789
  }
@@ -578528,7 +578602,7 @@ function classifyThinkOutcome(raw) {
578528
578602
  }
578529
578603
  return null;
578530
578604
  }
578531
- var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, TOOL_ACTION_REASON_KEYS, TOOL_ACTION_REASON_SCOPES, READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS, TOOL_ACTION_REASON_SCHEMA, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578605
+ var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, TOOL_ACTION_REASON_KEYS, TOOL_ACTION_REASON_SCOPES, READ_ONLY_ACTION_REASON_OPTIONAL_TOOLS, STATE_UPDATE_ACTION_REASON_SYNTHESIS_TOOLS, TOOL_ACTION_REASON_SCHEMA, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578532
578606
  var init_agenticRunner = __esm({
578533
578607
  "packages/orchestrator/dist/agenticRunner.js"() {
578534
578608
  "use strict";
@@ -578652,6 +578726,9 @@ var init_agenticRunner = __esm({
578652
578726
  "web_fetch",
578653
578727
  "tool_search"
578654
578728
  ]);
578729
+ STATE_UPDATE_ACTION_REASON_SYNTHESIS_TOOLS = /* @__PURE__ */ new Set([
578730
+ "todo_write"
578731
+ ]);
578655
578732
  TOOL_ACTION_REASON_SCHEMA = {
578656
578733
  type: "object",
578657
578734
  description: "Compact public action contract for this exact tool call. Do not include hidden chain-of-thought; use concise task-state facts.",
@@ -580012,10 +580089,13 @@ ${parts.join("\n")}
580012
580089
  }
580013
580090
  _maybeMarkFocusTerminalIncomplete(input) {
580014
580091
  if (input.decision.kind === "pass")
580015
- return;
580092
+ return null;
580016
580093
  const directive = input.decision.directive;
580017
580094
  if (directive.state !== "terminal_incomplete" || directive.requiredNextAction !== "report_incomplete") {
580018
- return;
580095
+ return null;
580096
+ }
580097
+ if (this._completionIncompleteVerification) {
580098
+ return this._completionIncompleteVerification.summary;
580019
580099
  }
580020
580100
  const reason = directive.reason;
580021
580101
  const blocked = input.blockedTool ? `Blocked tool: ${input.blockedTool}.` : "";
@@ -580033,6 +580113,10 @@ ${parts.join("\n")}
580033
580113
  "Next action:",
580034
580114
  'Call task_complete with status="incomplete" or status="blocked", or provide the missing evidence if a valid recovery action remains.'
580035
580115
  ].filter((line) => line.length > 0).join("\n");
580116
+ this._completionIncompleteVerification = {
580117
+ reason: "focus_supervisor_terminal",
580118
+ summary: focusTerminalSummary
580119
+ };
580036
580120
  this.emit({
580037
580121
  type: "status",
580038
580122
  content: `Focus supervisor terminal directive: ${reason.slice(0, 220)}`,
@@ -580051,6 +580135,7 @@ ${parts.join("\n")}
580051
580135
  this._saveCompletionLedgerSafe();
580052
580136
  this._focusTerminalLedgerRecorded = true;
580053
580137
  }
580138
+ return focusTerminalSummary;
580054
580139
  }
580055
580140
  _buildFocusContextSnapshot() {
580056
580141
  const last2 = this._lastFocusContextSnapshot;
@@ -581626,6 +581711,22 @@ ${result.output ?? ""}`;
581626
581711
  return [];
581627
581712
  return todos.filter((t2) => t2.status !== "completed");
581628
581713
  }
581714
+ _taskCompleteArgsReportDegraded(args) {
581715
+ const status = completionStatusFromTaskCompleteArgs(args);
581716
+ if (status === "blocked" || status === "incomplete" || status === "needs_input") {
581717
+ return true;
581718
+ }
581719
+ const summary = args && typeof args["summary"] === "string" ? args["summary"] : "";
581720
+ return /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summary);
581721
+ }
581722
+ _shouldBypassTodoCompletionGuardForTaskComplete(args) {
581723
+ if (!this._taskCompleteArgsReportDegraded(args))
581724
+ return false;
581725
+ if (this._completionIncompleteVerification)
581726
+ return true;
581727
+ const directive = this._focusSupervisor?.snapshot().directive;
581728
+ return directive?.state === "terminal_incomplete" || directive?.requiredNextAction === "report_incomplete" || directive?.requiredNextAction === "report_blocked";
581729
+ }
581629
581730
  /**
581630
581731
  * Build a guard prompt injected when the model attempts to call task_complete
581631
581732
  * while there are open todo items. This asks the model to verify each item by
@@ -583722,22 +583823,30 @@ ${latest.output || ""}`.trim();
583722
583823
  const artifactLines = intEnv("OMNIUS_CONTEXT_TREE_ARTIFACT_LINES", 1800, 100, 4e3);
583723
583824
  try {
583724
583825
  const scan = scanWorkspace({ root, maxFiles, maxDirs });
583725
- const compactTree = renderWorkspaceTree(scan, { maxLines: contextLines });
583726
- const fullTree = renderWorkspaceTree(scan, { maxLines: artifactLines });
583826
+ const compactTree = renderWorkspaceTree(scan, {
583827
+ maxLines: contextLines,
583828
+ sortBy: "recent"
583829
+ });
583830
+ const fullTree = renderWorkspaceTree(scan, {
583831
+ maxLines: artifactLines,
583832
+ sortBy: "recent"
583833
+ });
583727
583834
  const totalBytes = scan.files.reduce((sum, file) => sum + file.bytes, 0);
583728
583835
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
583729
583836
  const focusSnapshot = this._focusSupervisor?.snapshot();
583730
- const recentActions = this._adversaryToolOutcomes.slice(-12).map((item) => ({
583837
+ const recentActions = this._recentToolActionsForWorldState().slice(-12).map((item) => ({
583731
583838
  turn: item.turn,
583732
583839
  tool: item.tool,
583733
583840
  success: item.succeeded,
583734
583841
  path: item.path,
583735
583842
  stateVersion: item.stateVersion,
583736
- preview: item.preview.slice(0, 240)
583843
+ preview: item.preview.slice(0, 240),
583844
+ actionReason: item.actionReason ?? void 0
583737
583845
  }));
583738
583846
  const recentActionLines = recentActions.slice(-6).map((item) => {
583739
583847
  const path12 = item.path ? ` path=${item.path}` : "";
583740
- return `- turn=${item.turn} ${item.tool} ${item.success ? "ok" : "err"}${path12}: ${item.preview.replace(/\s+/g, " ").slice(0, 160)}`;
583848
+ const reason = item.actionReason ? ` reason=${[item.actionReason.scope, item.actionReason.intent].filter(Boolean).join(":").replace(/\s+/g, " ").slice(0, 120)}` : "";
583849
+ return `- turn=${item.turn} ${item.tool} ${item.success ? "ok" : "err"}${path12}${reason}: ${item.preview.replace(/\s+/g, " ").slice(0, 160)}`;
583741
583850
  });
583742
583851
  const treeDir = _pathJoin(this.omniusStateDir(), "world-state", "tree");
583743
583852
  const turnName = `turn-${String(turn).padStart(4, "0")}`;
@@ -585822,7 +585931,8 @@ Respond with your assessment, then take action.`;
585822
585931
  this._focusSupervisor = this.options.focusSupervisor === "off" ? null : new FocusSupervisor({
585823
585932
  mode: this.options.focusSupervisor,
585824
585933
  modelTier: this.options.modelTier,
585825
- repeatGateMax: this._resolveRepeatGateMax()
585934
+ repeatGateMax: this._resolveRepeatGateMax(),
585935
+ availableTools: this.tools.keys()
585826
585936
  });
585827
585937
  if (!this._workingDirectory) {
585828
585938
  this._workingDirectory = _pathResolve(process.cwd());
@@ -589780,6 +589890,14 @@ Respond with EXACTLY this structure before your next tool call:
589780
589890
  const realFileMutation = this._isRealProjectMutation(tc.name, result);
589781
589891
  const observedStateMutation = realFileMutation || shellFilesystemMutation || result.success === true && result.mutated === true;
589782
589892
  const realMutationPaths = realFileMutation ? this._extractToolTargetPaths(tc.name, tc.arguments, result) : [];
589893
+ this._recordRecentToolOutcomeForWorldState({
589894
+ turn,
589895
+ toolName: resolvedTool?.name ?? tc.name,
589896
+ toolCallId: tc.id,
589897
+ args: tc.arguments,
589898
+ result,
589899
+ actionReason: toolActionReason
589900
+ });
589783
589901
  if (observedStateMutation) {
589784
589902
  this._markAdversaryObservedStateMutation();
589785
589903
  }
@@ -591406,8 +591524,9 @@ ${sr.result.output}`;
591406
591524
  });
591407
591525
  continue;
591408
591526
  }
591527
+ const taskCompleteArgs = matchTc.arguments;
591409
591528
  const open2 = this.getOpenTodoItems();
591410
- if (open2.length > 0) {
591529
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591411
591530
  const guard = this.buildTodoCompletionGuard(open2);
591412
591531
  messages2.push({ role: "system", content: guard });
591413
591532
  this.emit({
@@ -591416,12 +591535,12 @@ ${sr.result.output}`;
591416
591535
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591417
591536
  });
591418
591537
  } else {
591419
- if (holdTaskCompleteGates(matchTc.arguments, turn)) {
591538
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591420
591539
  if (this._completionIncompleteVerification)
591421
591540
  break;
591422
591541
  continue;
591423
591542
  }
591424
- const _bp1 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(matchTc.arguments));
591543
+ const _bp1 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591425
591544
  if (_bp1 && !_bp1.proceed) {
591426
591545
  if (_bp1.feedback)
591427
591546
  emitBackwardPassAdvisory(_bp1.feedback, turn);
@@ -591430,7 +591549,7 @@ ${sr.result.output}`;
591430
591549
  continue;
591431
591550
  }
591432
591551
  completed = true;
591433
- summary = extractTaskCompleteSummary(matchTc.arguments);
591552
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591434
591553
  this._onTypedEvent?.({
591435
591554
  type: "completion_requested",
591436
591555
  runId: this._sessionId ?? "unknown",
@@ -591480,8 +591599,9 @@ ${sr.result.output}`;
591480
591599
  });
591481
591600
  continue;
591482
591601
  }
591602
+ const taskCompleteArgs = r2.tc.arguments;
591483
591603
  const open2 = this.getOpenTodoItems();
591484
- if (open2.length > 0) {
591604
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591485
591605
  const guard = this.buildTodoCompletionGuard(open2);
591486
591606
  messages2.push({ role: "system", content: guard });
591487
591607
  this.emit({
@@ -591490,12 +591610,12 @@ ${sr.result.output}`;
591490
591610
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591491
591611
  });
591492
591612
  } else {
591493
- if (holdTaskCompleteGates(r2.tc.arguments, turn)) {
591613
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591494
591614
  if (this._completionIncompleteVerification)
591495
591615
  break;
591496
591616
  continue;
591497
591617
  }
591498
- const _bp2 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(r2.tc.arguments));
591618
+ const _bp2 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591499
591619
  if (_bp2 && !_bp2.proceed) {
591500
591620
  if (_bp2.feedback)
591501
591621
  emitBackwardPassAdvisory(_bp2.feedback, turn);
@@ -591504,7 +591624,7 @@ ${sr.result.output}`;
591504
591624
  continue;
591505
591625
  }
591506
591626
  completed = true;
591507
- summary = extractTaskCompleteSummary(r2.tc.arguments);
591627
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591508
591628
  this._onTypedEvent?.({
591509
591629
  type: "completion_requested",
591510
591630
  runId: this._sessionId ?? "unknown",
@@ -591549,6 +591669,8 @@ ${sr.result.output}`;
591549
591669
  }
591550
591670
  }
591551
591671
  const results = await executeBatch(batch2, async (call) => {
591672
+ if (this._completionIncompleteVerification)
591673
+ return null;
591552
591674
  const originalTc = rawToolCalls.find((tc) => tc.id === call.id);
591553
591675
  const fp = buildBatchFp(call);
591554
591676
  const firstId = batchFingerprintFirstId.get(fp);
@@ -591591,8 +591713,9 @@ ${sr.result.output}`;
591591
591713
  });
591592
591714
  continue;
591593
591715
  }
591716
+ const taskCompleteArgs = r2.tc.arguments;
591594
591717
  const open2 = this.getOpenTodoItems();
591595
- if (open2.length > 0) {
591718
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
591596
591719
  const guard = this.buildTodoCompletionGuard(open2);
591597
591720
  messages2.push({ role: "system", content: guard });
591598
591721
  this.emit({
@@ -591601,12 +591724,12 @@ ${sr.result.output}`;
591601
591724
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591602
591725
  });
591603
591726
  } else {
591604
- if (holdTaskCompleteGates(r2.tc.arguments, turn)) {
591727
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
591605
591728
  if (this._completionIncompleteVerification)
591606
591729
  break;
591607
591730
  continue;
591608
591731
  }
591609
- const _bp3 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(r2.tc.arguments));
591732
+ const _bp3 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
591610
591733
  if (_bp3 && !_bp3.proceed) {
591611
591734
  if (_bp3.feedback)
591612
591735
  emitBackwardPassAdvisory(_bp3.feedback, turn);
@@ -591615,7 +591738,7 @@ ${sr.result.output}`;
591615
591738
  continue;
591616
591739
  }
591617
591740
  completed = true;
591618
- summary = extractTaskCompleteSummary(r2.tc.arguments);
591741
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
591619
591742
  this._onTypedEvent?.({
591620
591743
  type: "completion_requested",
591621
591744
  runId: this._sessionId ?? "unknown",
@@ -592493,8 +592616,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592493
592616
  });
592494
592617
  continue;
592495
592618
  }
592619
+ const taskCompleteArgs = tc.arguments;
592496
592620
  const open2 = this.getOpenTodoItems();
592497
- if (open2.length > 0) {
592621
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(taskCompleteArgs)) {
592498
592622
  const guard = this.buildTodoCompletionGuard(open2);
592499
592623
  messages2.push({ role: "system", content: guard });
592500
592624
  this.emit({
@@ -592503,12 +592627,12 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592503
592627
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592504
592628
  });
592505
592629
  } else {
592506
- if (holdTaskCompleteGates(tc.arguments, turn)) {
592630
+ if (holdTaskCompleteGates(taskCompleteArgs, turn)) {
592507
592631
  if (this._completionIncompleteVerification)
592508
592632
  break;
592509
592633
  continue;
592510
592634
  }
592511
- const _bp4 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(tc.arguments));
592635
+ const _bp4 = await this._runBackwardPassReview(turn, toolCallLog, extractTaskCompleteSummary(taskCompleteArgs));
592512
592636
  if (_bp4 && !_bp4.proceed) {
592513
592637
  if (_bp4.feedback)
592514
592638
  emitBackwardPassAdvisory(_bp4.feedback, turn);
@@ -592517,7 +592641,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592517
592641
  continue;
592518
592642
  }
592519
592643
  completed = true;
592520
- summary = extractTaskCompleteSummary(tc.arguments);
592644
+ summary = extractTaskCompleteSummary(taskCompleteArgs);
592521
592645
  this._onTypedEvent?.({
592522
592646
  type: "completion_requested",
592523
592647
  runId: this._sessionId ?? "unknown",
@@ -592558,8 +592682,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592558
592682
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592559
592683
  });
592560
592684
  if (/task.?complete|all tests pass/i.test(content)) {
592685
+ const completionArgs = { summary: content };
592561
592686
  const open2 = this.getOpenTodoItems();
592562
- if (open2.length > 0) {
592687
+ if (open2.length > 0 && !this._shouldBypassTodoCompletionGuardForTaskComplete(completionArgs)) {
592563
592688
  const guard = this.buildTodoCompletionGuard(open2);
592564
592689
  messages2.push({ role: "system", content: guard });
592565
592690
  this.emit({
@@ -592568,7 +592693,6 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592568
592693
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
592569
592694
  });
592570
592695
  } else {
592571
- const completionArgs = { summary: content };
592572
592696
  if (holdTaskCompleteGates(completionArgs, turn)) {
592573
592697
  if (this._completionIncompleteVerification)
592574
592698
  break;
@@ -593926,10 +594050,32 @@ ${marker}` : marker);
593926
594050
  const explicit = this._extractToolActionReason(args);
593927
594051
  if (explicit)
593928
594052
  return explicit;
594053
+ if (this._shouldSynthesizeMissingToolActionReason(toolName, args)) {
594054
+ return this._synthesizeStateUpdateToolActionReason(toolName, args);
594055
+ }
593929
594056
  if (this._toolActionReasonPolicy(toolName, args) !== "optional")
593930
594057
  return null;
593931
594058
  return this._synthesizeReadOnlyToolActionReason(toolName, args);
593932
594059
  }
594060
+ _shouldSynthesizeMissingToolActionReason(toolName, _args) {
594061
+ if (this._toolActionReasonPolicy(toolName, _args) === "disabled") {
594062
+ return false;
594063
+ }
594064
+ const canonical = this.lookupRegisteredTool(toolName)?.name ?? toolName;
594065
+ return STATE_UPDATE_ACTION_REASON_SYNTHESIS_TOOLS.has(canonical);
594066
+ }
594067
+ _synthesizeStateUpdateToolActionReason(toolName, args) {
594068
+ const canonical = this.lookupRegisteredTool(toolName)?.name ?? toolName;
594069
+ const goal = this._taskState.currentStep || this._taskState.nextAction || this._taskState.goal || this._taskState.originalGoal || "current task";
594070
+ const todoCount = Array.isArray(args["todos"]) ? args["todos"].length : null;
594071
+ return {
594072
+ task_anchor: String(goal).replace(/\s+/g, " ").trim().slice(0, 160),
594073
+ intent: `Update visible task state with ${canonical}.`,
594074
+ evidence: todoCount === null ? "Model omitted public action_reason; runtime synthesized bookkeeping metadata without authorizing project mutation." : `Model supplied ${todoCount} todo item(s); runtime synthesized bookkeeping metadata without authorizing project mutation.`,
594075
+ expected_result: "Session checklist reflects the current task state.",
594076
+ scope: "state_update"
594077
+ };
594078
+ }
593933
594079
  _synthesizeReadOnlyToolActionReason(toolName, args) {
593934
594080
  const canonical = this.lookupRegisteredTool(toolName)?.name ?? toolName;
593935
594081
  const goal = this._taskState.currentStep || this._taskState.nextAction || this._taskState.goal || this._taskState.originalGoal || "current task";
@@ -593951,6 +594097,9 @@ ${marker}` : marker);
593951
594097
  if (!raw || typeof raw === "string" && !raw.trim()) {
593952
594098
  if (policy === "optional")
593953
594099
  return null;
594100
+ if (this._shouldSynthesizeMissingToolActionReason(toolName, args)) {
594101
+ return null;
594102
+ }
593954
594103
  return [
593955
594104
  "[TOOL ACTION REASON CONTRACT]",
593956
594105
  `This side-effectful tool call must include action_reason: { task_anchor, intent, evidence, expected_result, scope }.`,
@@ -595798,6 +595947,10 @@ ${trimmedNew}`;
595798
595947
  // we add a second analysis path that catches mismatches in real-time.
595799
595948
  /** Track recent tool outcomes for the adversary */
595800
595949
  _adversaryToolOutcomes = [];
595950
+ /** Execution-time action ledger used by world-state artifacts. Unlike the
595951
+ * adversary buffer, this is populated directly after tool execution and does
595952
+ * not depend on raw tool messages surviving compaction. */
595953
+ _recentToolOutcomes = [];
595801
595954
  /** Monotonic counter for observed local state changes.
595802
595955
  * Redundant-action classification is only valid within the same state
595803
595956
  * version; a repeated verifier after an edit is fresh evidence. */
@@ -595902,6 +596055,38 @@ ${trimmedNew}`;
595902
596055
  }
595903
596056
  return { preview: snippet || content.slice(0, 160) };
595904
596057
  }
596058
+ _recordRecentToolOutcomeForWorldState(input) {
596059
+ const content = input.result.success === true ? String(input.result.output ?? "") : String(input.result.error ?? input.result.output ?? "");
596060
+ const outcomeEvidence = this.buildAdversaryToolOutcomeEvidence(input.toolName, input.args, content, input.result.success === true);
596061
+ const fingerprint = input.args ? this._buildToolFingerprint(input.toolName, input.args) : void 0;
596062
+ const alreadySeen = this._recentToolOutcomes.some((outcome) => {
596063
+ if (input.toolCallId && outcome.toolCallId === input.toolCallId) {
596064
+ return true;
596065
+ }
596066
+ return outcome.turn === input.turn && outcome.tool === input.toolName && outcome.fingerprint === fingerprint;
596067
+ });
596068
+ if (alreadySeen)
596069
+ return;
596070
+ this._recentToolOutcomes.push({
596071
+ turn: input.turn,
596072
+ tool: input.toolName,
596073
+ toolCallId: input.toolCallId,
596074
+ argsKey: input.args ? this._buildExactArgsKey(input.args) : void 0,
596075
+ fingerprint,
596076
+ stateVersion: this._adversaryStateVersion,
596077
+ succeeded: input.result.success === true,
596078
+ actionReason: input.actionReason,
596079
+ ...outcomeEvidence
596080
+ });
596081
+ while (this._recentToolOutcomes.length > 40) {
596082
+ this._recentToolOutcomes.shift();
596083
+ }
596084
+ }
596085
+ _recentToolActionsForWorldState() {
596086
+ if (this._recentToolOutcomes.length > 0)
596087
+ return this._recentToolOutcomes;
596088
+ return this._adversaryToolOutcomes;
596089
+ }
595905
596090
  /**
595906
596091
  * Adversary: post-turn meta-analysis.
595907
596092
  *
@@ -737491,6 +737676,11 @@ function createTaskCompleteTool(modelTier2, repoRoot, skipSessionGuard) {
737491
737676
  required: ["summary"]
737492
737677
  },
737493
737678
  async execute(args) {
737679
+ const completionStatus = typeof args["status"] === "string" ? args["status"].trim().toLowerCase().replace(/[\s-]+/g, "_") : "";
737680
+ const summaryText = typeof args["summary"] === "string" ? args["summary"] : "";
737681
+ const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(
737682
+ summaryText
737683
+ );
737494
737684
  if (_interactiveSessionActive && !skipSessionGuard) {
737495
737685
  return {
737496
737686
  success: false,
@@ -737505,7 +737695,7 @@ function createTaskCompleteTool(modelTier2, repoRoot, skipSessionGuard) {
737505
737695
  const incomplete = todos.filter(
737506
737696
  (t2) => t2.status === "pending" || t2.status === "in_progress" || t2.status === "blocked"
737507
737697
  );
737508
- if (incomplete.length > 0) {
737698
+ if (incomplete.length > 0 && !reportsDegradedCompletion) {
737509
737699
  const incompleteList = incomplete.slice(0, 20).map(
737510
737700
  (t2) => ` - [${t2.status}] ${t2.content}${t2.blocker ? ` (blocked: ${t2.blocker})` : ""}`
737511
737701
  ).join("\n");
@@ -737531,9 +737721,11 @@ ${incompleteList}${more}
737531
737721
  ` + guidance
737532
737722
  };
737533
737723
  }
737534
- try {
737535
- writeTodos(sessionId, []);
737536
- } catch {
737724
+ if (incomplete.length === 0) {
737725
+ try {
737726
+ writeTodos(sessionId, []);
737727
+ } catch {
737728
+ }
737537
737729
  }
737538
737730
  }
737539
737731
  } catch {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.465",
3
+ "version": "1.0.467",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.465",
9
+ "version": "1.0.467",
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.465",
3
+ "version": "1.0.467",
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",