omnius 1.0.452 → 1.0.453

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
@@ -14361,6 +14361,22 @@ var init_file_edit = __esm({
14361
14361
  }
14362
14362
  const occurrences = countOccurrences(content, oldString);
14363
14363
  if (occurrences === 0) {
14364
+ const alreadyAppliedLines = newString.length > 0 ? findMatchLines(content, newString) : [];
14365
+ if (alreadyAppliedLines.length > 0) {
14366
+ const lineInfo = alreadyAppliedLines.length === 1 ? `line ${alreadyAppliedLines[0]}` : `${alreadyAppliedLines.length} locations (lines ${alreadyAppliedLines.join(", ")})`;
14367
+ return {
14368
+ success: true,
14369
+ output: `[ALREADY APPLIED — ${filePath} already contains the requested new_string at ${lineInfo}; old_string is absent. No disk changes were made.]`,
14370
+ durationMs: performance.now() - start2,
14371
+ mutated: false,
14372
+ mutatedFiles: [],
14373
+ diff: buildCompactDiff(oldString, newString),
14374
+ noop: true,
14375
+ alreadyApplied: true,
14376
+ beforeHash,
14377
+ afterHash: beforeHash
14378
+ };
14379
+ }
14364
14380
  const snippet = findClosestSnippet(content, oldString, 5);
14365
14381
  let errorMsg = `old_string not found in ${filePath}.`;
14366
14382
  if (snippet) {
@@ -28439,6 +28455,9 @@ function findMatchOffsetsBatch(haystack, needle) {
28439
28455
  }
28440
28456
  return offsets;
28441
28457
  }
28458
+ function findMatchLinesBatch(haystack, needle) {
28459
+ return findMatchOffsetsBatch(haystack, needle).map((offset) => haystack.slice(0, offset).split("\n").length);
28460
+ }
28442
28461
  function countOccurrences2(haystack, needle) {
28443
28462
  let count = 0;
28444
28463
  let pos = 0;
@@ -28587,6 +28606,7 @@ var init_batch_edit = __esm({
28587
28606
  }
28588
28607
  const results = [];
28589
28608
  let successCount = 0;
28609
+ let alreadyAppliedCount = 0;
28590
28610
  let failCount = 0;
28591
28611
  const pendingWrites = [];
28592
28612
  for (const [fullPath, fileEdits] of byFile) {
@@ -28607,10 +28627,18 @@ var init_batch_edit = __esm({
28607
28627
  }
28608
28628
  const originalContent = content;
28609
28629
  let fileSuccessCount = 0;
28630
+ let fileAlreadyAppliedCount = 0;
28610
28631
  let fileFailed = false;
28611
28632
  for (const edit of fileEdits) {
28612
28633
  const occurrences = countOccurrences2(content, edit.old_string);
28613
28634
  if (occurrences === 0) {
28635
+ const alreadyAppliedLines = edit.new_string.length > 0 ? findMatchLinesBatch(content, edit.new_string) : [];
28636
+ if (alreadyAppliedLines.length > 0) {
28637
+ const lineInfo = alreadyAppliedLines.length === 1 ? `line ${alreadyAppliedLines[0]}` : `${alreadyAppliedLines.length} locations (lines ${alreadyAppliedLines.join(", ")})`;
28638
+ results.push(`ALREADY_APPLIED: ${edit.relPath} already contains requested new_string at ${lineInfo}; old_string is absent`);
28639
+ fileAlreadyAppliedCount++;
28640
+ continue;
28641
+ }
28614
28642
  const snippet = findClosestSnippet(content, edit.old_string, 5);
28615
28643
  if (snippet) {
28616
28644
  const pct = Math.round(snippet.similarity * 100);
@@ -28657,6 +28685,8 @@ ${s2.content}`;
28657
28685
  const afterHash = contentHash(content);
28658
28686
  if (afterHash === beforeHash) {
28659
28687
  results.push(`NO-OP: ${fileEdits[0].relPath} batch produced no disk changes`);
28688
+ successCount += fileAlreadyAppliedCount;
28689
+ alreadyAppliedCount += fileAlreadyAppliedCount;
28660
28690
  continue;
28661
28691
  }
28662
28692
  pendingWrites.push({
@@ -28667,7 +28697,8 @@ ${s2.content}`;
28667
28697
  afterHash,
28668
28698
  editCount: fileSuccessCount
28669
28699
  });
28670
- successCount += fileSuccessCount;
28700
+ successCount += fileSuccessCount + fileAlreadyAppliedCount;
28701
+ alreadyAppliedCount += fileAlreadyAppliedCount;
28671
28702
  } catch (error) {
28672
28703
  results.push(`ERROR: ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
28673
28704
  failCount++;
@@ -28692,7 +28723,7 @@ ${s2.content}`;
28692
28723
  ].join("\n") : dryRun ? [
28693
28724
  `BATCH_VALIDATED_ONLY: ${appliedCount} edit(s) validated, 0 failed across ${byFile.size} file(s).`,
28694
28725
  `NO FILES WERE MODIFIED. Call again with dry_run=false to apply this atomic batch.`
28695
- ].join("\n") : `BATCH_COMMITTED: ${appliedCount} edit(s) applied, 0 failed across ${byFile.size} file(s).`;
28726
+ ].join("\n") : pendingWrites.length === 0 && alreadyAppliedCount > 0 ? `BATCH_NOOP_ALREADY_APPLIED: ${alreadyAppliedCount} edit(s) already satisfied, 0 failed across ${byFile.size} file(s).` : `BATCH_COMMITTED: ${appliedCount} edit(s) satisfied (${pendingWrites.reduce((sum, write2) => sum + write2.editCount, 0)} applied, ${alreadyAppliedCount} already applied), 0 failed across ${byFile.size} file(s).`;
28696
28727
  const detailOutput = failCount === 0 ? dryRun ? results.join("\n") : results.map((line) => line.replace(/^VALIDATED_ONLY:/, "APPLIED:").replace(/ pending atomic commit/g, "")).join("\n") : results.join("\n");
28697
28728
  return {
28698
28729
  success: failCount === 0,
@@ -28703,6 +28734,7 @@ ${detailOutput}`,
28703
28734
  mutated: failCount === 0 && !dryRun && pendingWrites.length > 0,
28704
28735
  mutatedFiles: failCount === 0 && !dryRun ? pendingWrites.map((w) => w.relPath) : [],
28705
28736
  noop: failCount === 0 && pendingWrites.length === 0,
28737
+ alreadyApplied: failCount === 0 && alreadyAppliedCount > 0,
28706
28738
  dryRun: dryRun || void 0,
28707
28739
  partial: false,
28708
28740
  beforeHash: pendingWrites.length === 1 ? pendingWrites[0].beforeHash : void 0,
@@ -298462,11 +298494,12 @@ var init_todo_write = __esm({
298462
298494
 
298463
298495
  ## Nested decomposition
298464
298496
  - Use stable ids plus parentId to create subtasks under a parent objective.
298497
+ - Prefer a parent + child leaf todos for any active objective that has multiple concrete actions; the TUI renders the active branch indented for the user and for your next-turn context.
298465
298498
  - Work on leaf subtasks; a parent is completed only after every child is completed with evidence.
298466
298499
  - When evidence changes the plan, rewrite the tree: add new child todos, block invalidated children with root cause, and keep the parent in_progress/blocked until the child set is truthful.
298467
298500
  - If a tool fails for a child, do not mark that child completed. Reconfigure it: re-read/observe current state, choose a different target/tool, or leave it blocked.
298468
298501
 
298469
- Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark completed if tests are failing or implementation is partial. The user watches this list in the chat UI in real time. Canonical call shape: todo_write({"todos":[{"id":"p1","content":"Implement feature","status":"in_progress"},{"id":"c1","parentId":"p1","content":"Inspect files","status":"in_progress"},{"id":"c2","parentId":"p1","content":"Make changes","status":"pending"},{"id":"c3","parentId":"p1","content":"Verify results","status":"pending"}]})`;
298502
+ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark completed if tests are failing or implementation is partial. The user watches this list in the chat UI in real time. Canonical nested shape: todo_write({"todos":[{"id":"p1","content":"Implement feature","status":"in_progress","children":[{"id":"c1","content":"Inspect files","status":"in_progress"},{"id":"c2","content":"Make changes","status":"pending"},{"id":"c3","content":"Verify results","status":"pending"}]}]})`;
298470
298503
  parameters = {
298471
298504
  type: "object",
298472
298505
  required: ["todos"],
@@ -298643,11 +298676,15 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
298643
298676
  const reminder = "Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Mark the current task in_progress and the next task pending. Proceed with the current task.";
298644
298677
  const payload = {
298645
298678
  reminder,
298646
- decompositionContract: "Use parentId for sub-todos. Keep parents in_progress/blocked until all children are completed with objective evidence. When tool evidence invalidates a child, rewrite that child as blocked or split it into new children instead of overclaiming completion.",
298679
+ decompositionContract: "Use parentId for sub-todos. Split the active objective into concrete child leaf todos whenever it contains multiple remaining actions. Keep parents in_progress/blocked until all children are completed with objective evidence. When tool evidence invalidates a child, rewrite that child as blocked or split it into new children instead of overclaiming completion.",
298647
298680
  oldTodos: result.oldTodos,
298648
298681
  newTodos: result.newTodos,
298649
298682
  verificationNudgeNeeded
298650
298683
  };
298684
+ const hasNestedTodos = incoming.some((todo) => typeof todo.parentId === "string" && todo.parentId.trim().length > 0);
298685
+ if (incoming.length >= 3 && !hasNestedTodos) {
298686
+ payload["decompositionHint"] = "This checklist is still flat. If the current in_progress item expands into multiple actions, rewrite it as a parent with child todos using stable ids and parentId/children so the active branch stays visible.";
298687
+ }
298651
298688
  if (repairNotes.length > 0) {
298652
298689
  payload["inputRepair"] = Array.from(new Set(repairNotes));
298653
298690
  payload["canonicalShape"] = {
@@ -573482,7 +573519,7 @@ function recordContextWindowDump(input) {
573482
573519
  const payload = JSON.stringify(record, null, 2);
573483
573520
  writeFileSync48(filePath, payload, "utf-8");
573484
573521
  writeFileSync48(join111(dir, `latest-${record.agentType}.json`), payload, "utf-8");
573485
- if (record.agentType === "main") {
573522
+ if (record.agentType === "main" || record.agentType === "sub-agent" || record.agentType === "adversary") {
573486
573523
  writeFileSync48(join111(dir, "latest.json"), payload, "utf-8");
573487
573524
  }
573488
573525
  appendFileSync9(join111(dir, "index.jsonl"), JSON.stringify(toSummary(record, filePath)) + "\n", "utf-8");
@@ -574701,14 +574738,13 @@ function resolveFocusSupervisorMode(configured, envValue = process.env["OMNIUS_F
574701
574738
  function violatesDirective(directive, input) {
574702
574739
  if (input.toolName === "task_complete") {
574703
574740
  const completionStatus = normalizeCompletionStatus(input.completionStatus ?? input.args?.["status"] ?? input.args?.["completion_status"] ?? input.args?.["completionStatus"]);
574704
- const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input";
574705
- if (directive.requiredNextAction === "use_cached_evidence") {
574706
- return false;
574741
+ const summaryText = typeof input.args?.["summary"] === "string" ? input.args["summary"] : "";
574742
+ const summaryReportsDegraded = /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summaryText);
574743
+ const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || summaryReportsDegraded;
574744
+ if (directive.requiredNextAction === "report_blocked" || directive.requiredNextAction === "report_incomplete" || directive.state === "terminal_incomplete") {
574745
+ return !reportsDegradedCompletion;
574707
574746
  }
574708
- if (reportsDegradedCompletion && (directive.requiredNextAction === "read_authoritative_target" || directive.requiredNextAction === "report_blocked" || directive.requiredNextAction === "report_incomplete")) {
574709
- return false;
574710
- }
574711
- return directive.requiredNextAction !== "report_blocked" && directive.requiredNextAction !== "report_incomplete";
574747
+ return false;
574712
574748
  }
574713
574749
  if (input.toolName === "ask_user")
574714
574750
  return false;
@@ -574989,6 +575025,11 @@ var init_focusSupervisor = __esm({
574989
575025
  const family = actionFamily(input.toolName, input.args);
574990
575026
  const prior = this.directive;
574991
575027
  if (prior && violatesDirective(prior, input)) {
575028
+ if (prior.state === "warn") {
575029
+ this.lastDecision = "warn_not_enforced";
575030
+ this.lastReason = prior.reason;
575031
+ return this.pass();
575032
+ }
574992
575033
  const strict = this.shouldStrictlyIntervene(input.context);
574993
575034
  const sameTurnAsDirective = input.turn <= prior.createdTurn;
574994
575035
  const alreadyCountedThisTurn = this.lastIgnoredDirectiveTurn === input.turn;
@@ -575105,6 +575146,10 @@ var init_focusSupervisor = __esm({
575105
575146
  this.clearSatisfiedDirective("mutation landed", input.turn);
575106
575147
  return;
575107
575148
  }
575149
+ if (input.success && input.alreadyApplied) {
575150
+ this.clearSatisfiedDirective("requested edit already applied", input.turn);
575151
+ return;
575152
+ }
575108
575153
  if (input.runtimeAuthored) {
575109
575154
  this.lastDecision = "runtime_authored_not_satisfied";
575110
575155
  this.lastReason = "runtime-authored control result did not execute the requested tool";
@@ -577627,7 +577672,7 @@ function computeTodoReminder(input) {
577627
577672
  if (!input.todos || input.todos.length === 0) {
577628
577673
  if (input.elicitCreateOnEmptyTodos) {
577629
577674
  const content2 = `<system-reminder>
577630
- No todo plan exists yet. Consider using the todo_write tool to create a plan for the current task — break the goal into ordered steps, mark the first step in_progress, and keep the rest pending. This helps you stay organized and track progress. Make sure that you NEVER mention this reminder to the user.
577675
+ No todo plan exists yet. Consider using the todo_write tool to create a plan for the current task — break the goal into parent objectives and child leaf subtasks when the work has multiple actions, mark the active leaf in_progress, and keep the rest pending. This helps you stay organized and track progress. Make sure that you NEVER mention this reminder to the user.
577631
577676
  </system-reminder>`;
577632
577677
  return { shouldInject: true, content: content2, reason: "elicit_create" };
577633
577678
  }
@@ -577647,9 +577692,9 @@ No todo plan exists yet. Consider using the todo_write tool to create a plan for
577647
577692
  return { shouldInject: false, content: null, reason: "recent_reminder" };
577648
577693
  }
577649
577694
  }
577650
- const todoItems = input.todos.slice(0, 12).map((t2, i2) => `${i2 + 1}. [${t2.status}] ${t2.content}`).join("\n");
577695
+ const todoItems = formatTodoReminderItems(input.todos, 12);
577651
577696
  const content = `<system-reminder>
577652
- The todo_write tool hasn't been used recently. Review your todo list below. If you're working on a task, mark it in_progress. When you complete a task, mark it completed immediately — don't batch completions. If the list is stale, clean it up. Make sure that you NEVER mention this reminder to the user.
577697
+ The todo_write tool hasn't been used recently. Review your todo list below. If you're working on a task, mark the active leaf in_progress. When you complete a leaf task, mark it completed immediately — don't batch completions. If the current item has multiple remaining actions, decompose it with stable ids and parentId/children instead of flattening the list. If the list is stale, clean it up. Make sure that you NEVER mention this reminder to the user.
577653
577698
 
577654
577699
  Here are the existing contents of your todo list:
577655
577700
 
@@ -577657,6 +577702,40 @@ ${todoItems}
577657
577702
  </system-reminder>`;
577658
577703
  return { shouldInject: true, content, reason: "injected" };
577659
577704
  }
577705
+ function formatTodoReminderItems(todos, maxRows) {
577706
+ const byId = new Map(todos.filter((todo) => typeof todo.id === "string" && todo.id.trim().length > 0).map((todo) => [todo.id, todo]));
577707
+ const byParent = /* @__PURE__ */ new Map();
577708
+ const roots = [];
577709
+ for (const todo of todos) {
577710
+ if (todo.parentId && byId.has(todo.parentId)) {
577711
+ const siblings = byParent.get(todo.parentId) ?? [];
577712
+ siblings.push(todo);
577713
+ byParent.set(todo.parentId, siblings);
577714
+ } else {
577715
+ roots.push(todo);
577716
+ }
577717
+ }
577718
+ const rows = [];
577719
+ const seen = /* @__PURE__ */ new Set();
577720
+ const visit = (todo, depth) => {
577721
+ if (rows.length >= maxRows || seen.has(todo))
577722
+ return;
577723
+ seen.add(todo);
577724
+ const indent2 = " ".repeat(Math.min(depth, 4));
577725
+ rows.push(`${indent2}- [${todo.status}] ${todo.content}`);
577726
+ if (!todo.id)
577727
+ return;
577728
+ for (const child of byParent.get(todo.id) ?? [])
577729
+ visit(child, depth + 1);
577730
+ };
577731
+ for (const root of roots)
577732
+ visit(root, 0);
577733
+ for (const todo of todos)
577734
+ visit(todo, 0);
577735
+ if (todos.length > rows.length)
577736
+ rows.push(`... +${todos.length - rows.length} more`);
577737
+ return rows.join("\n");
577738
+ }
577660
577739
  function stripThinkBlocks(s2) {
577661
577740
  if (!s2)
577662
577741
  return s2;
@@ -577946,6 +578025,7 @@ var init_agenticRunner = __esm({
577946
578025
  init_context_compressor();
577947
578026
  init_reflectionBuffer();
577948
578027
  init_taskHandoff();
578028
+ init_artifactQuality();
577949
578029
  init_messageLog();
577950
578030
  init_contextTree();
577951
578031
  init_codeGraphLink();
@@ -578844,16 +578924,21 @@ ${parts.join("\n")}
578844
578924
  lines.push("implementation_state=mutation evidence is already recorded; do not treat discovery as the active phase");
578845
578925
  }
578846
578926
  }
578847
- const todos = this.readSessionTodos() ?? [];
578927
+ const todos = this._normalizeTodosForPrompt(this.readSessionTodos() ?? []);
578848
578928
  if (todos.length > 0) {
578849
- const inProgress = todos.filter((todo) => todo.status === "in_progress");
578850
- const pending2 = todos.filter((todo) => todo.status === "pending");
578851
- const blocked = todos.filter((todo) => todo.status === "blocked");
578852
- lines.push(`todo_counts=in_progress:${inProgress.length} pending:${pending2.length} blocked:${blocked.length}`);
578853
- if (inProgress[0]) {
578854
- lines.push(`todo_current=${inProgress[0].content.slice(0, 180)}`);
578929
+ const index = this._todoTreeIndex(todos);
578930
+ const leaves = this._todoLeaves(todos, index);
578931
+ const active = this._activeTodoForPrompt(todos, index);
578932
+ const inProgress = leaves.filter((todo) => todo.status === "in_progress");
578933
+ const pending2 = leaves.filter((todo) => todo.status === "pending");
578934
+ const blocked = leaves.filter((todo) => todo.status === "blocked");
578935
+ const completed = leaves.filter((todo) => todo.status === "completed");
578936
+ lines.push(`todo_leaf_counts=completed:${completed.length} in_progress:${inProgress.length} pending:${pending2.length} blocked:${blocked.length}`);
578937
+ if (active) {
578938
+ const branch = this._todoPath(active, index).map((todo) => todo.content.slice(0, 64)).join(" > ");
578939
+ lines.push(`todo_active_branch=${branch.slice(0, 220)}`);
578855
578940
  } else if (pending2.length > 0) {
578856
- lines.push("todo_contract=mark exactly one pending todo in_progress before using the checklist as progress");
578941
+ lines.push("todo_contract=mark one pending leaf todo in_progress before using the checklist as progress");
578857
578942
  }
578858
578943
  }
578859
578944
  if (lines.length === 2)
@@ -578863,7 +578948,7 @@ ${parts.join("\n")}
578863
578948
  }
578864
578949
  _workboardTargetCardId(snapshot, toolName, result, meta = {}) {
578865
578950
  const byId = new Map(snapshot.cards.map((card) => [card.id, card]));
578866
- if (this._isProjectEditTool(toolName) && this._isRealProjectMutation(toolName, result)) {
578951
+ if (this._isProjectEditTool(toolName) && (this._isRealProjectMutation(toolName, result) || result?.alreadyApplied === true)) {
578867
578952
  return byId.has("implement-repair") ? "implement-repair" : null;
578868
578953
  }
578869
578954
  if (toolName === "shell" && meta.shellFilesystemMutation === true) {
@@ -578889,9 +578974,9 @@ ${parts.join("\n")}
578889
578974
  const dir = this._workboardDir();
578890
578975
  let current = snapshot;
578891
578976
  const byId = () => new Map(current.cards.map((card) => [card.id, card]));
578892
- const editMutation = this._isProjectEditTool(toolName) && this._isRealProjectMutation(toolName, result) || toolName === "shell" && result?.success === true && meta.shellFilesystemMutation === true;
578977
+ const editEvidence = this._isProjectEditTool(toolName) && (this._isRealProjectMutation(toolName, result) || result?.alreadyApplied === true) || toolName === "shell" && result?.success === true && meta.shellFilesystemMutation === true;
578893
578978
  try {
578894
- if (editMutation) {
578979
+ if (editEvidence) {
578895
578980
  let cards = byId();
578896
578981
  const discovery = cards.get("discover-current-state");
578897
578982
  if (discovery && discovery.status !== "verified" && discovery.evidence.length > 0) {
@@ -578924,7 +579009,7 @@ ${parts.join("\n")}
578924
579009
  cardId: implementation2.id,
578925
579010
  actor,
578926
579011
  updates: { status: "in_progress", lane: "worker" },
578927
- decisionReason: "Real project mutation landed; implementation is now the active phase."
579012
+ decisionReason: result?.alreadyApplied === true ? "Requested implementation edit was already present on disk; implementation evidence is now active." : "Real project mutation landed; implementation is now the active phase."
578928
579013
  });
578929
579014
  }
578930
579015
  }
@@ -578961,7 +579046,7 @@ ${parts.join("\n")}
578961
579046
  return;
578962
579047
  if (meta.focusDecisionKind === "block_tool_call")
578963
579048
  return;
578964
- if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result)) {
579049
+ if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result) && result.alreadyApplied !== true) {
578965
579050
  return;
578966
579051
  }
578967
579052
  const dir = this._workboardDir();
@@ -580430,6 +580515,127 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
580430
580515
  return null;
580431
580516
  }
580432
580517
  }
580518
+ _normalizeTodosForPrompt(todos) {
580519
+ return todos.map((todo, index) => {
580520
+ const rawId = todo.id;
580521
+ const id2 = typeof rawId === "string" && rawId.trim().length > 0 ? rawId.trim() : `prompt-${index}`;
580522
+ const rawParentId = todo.parentId;
580523
+ const parentId = typeof rawParentId === "string" && rawParentId.trim().length > 0 ? rawParentId.trim() : void 0;
580524
+ const createdAt = typeof todo.createdAt === "number" ? todo.createdAt : 0;
580525
+ const updatedAt = typeof todo.updatedAt === "number" ? todo.updatedAt : createdAt;
580526
+ return {
580527
+ ...todo,
580528
+ id: id2,
580529
+ ...parentId ? { parentId } : {},
580530
+ createdAt,
580531
+ updatedAt
580532
+ };
580533
+ });
580534
+ }
580535
+ _todoTreeIndex(todos) {
580536
+ const byId = new Map(todos.map((todo) => [todo.id, todo]));
580537
+ const byParent = /* @__PURE__ */ new Map();
580538
+ const roots = [];
580539
+ for (const todo of todos) {
580540
+ if (todo.parentId && byId.has(todo.parentId)) {
580541
+ const siblings = byParent.get(todo.parentId) ?? [];
580542
+ siblings.push(todo);
580543
+ byParent.set(todo.parentId, siblings);
580544
+ } else {
580545
+ roots.push(todo);
580546
+ }
580547
+ }
580548
+ return { byId, byParent, roots };
580549
+ }
580550
+ _todoDisplayStatus(todo, index, seen = /* @__PURE__ */ new Set()) {
580551
+ if (seen.has(todo.id))
580552
+ return todo.status;
580553
+ seen.add(todo.id);
580554
+ const children2 = index.byParent.get(todo.id) ?? [];
580555
+ if (children2.length === 0)
580556
+ return todo.status;
580557
+ const childStatuses = children2.map((child) => this._todoDisplayStatus(child, index, new Set(seen)));
580558
+ if (childStatuses.some((status) => status === "blocked"))
580559
+ return "blocked";
580560
+ if (childStatuses.some((status) => status === "in_progress"))
580561
+ return "in_progress";
580562
+ if (childStatuses.every((status) => status === "completed"))
580563
+ return "completed";
580564
+ if (todo.status === "completed")
580565
+ return "in_progress";
580566
+ return todo.status;
580567
+ }
580568
+ _todoLeafStats(todo, index) {
580569
+ const children2 = index.byParent.get(todo.id) ?? [];
580570
+ if (children2.length === 0)
580571
+ return null;
580572
+ let completed = 0;
580573
+ let total = 0;
580574
+ const seen = /* @__PURE__ */ new Set();
580575
+ const visit = (node) => {
580576
+ if (seen.has(node.id))
580577
+ return;
580578
+ seen.add(node.id);
580579
+ const nested = index.byParent.get(node.id) ?? [];
580580
+ if (nested.length === 0) {
580581
+ total++;
580582
+ if (node.status === "completed")
580583
+ completed++;
580584
+ return;
580585
+ }
580586
+ for (const child of nested)
580587
+ visit(child);
580588
+ };
580589
+ for (const child of children2)
580590
+ visit(child);
580591
+ return { completed, total };
580592
+ }
580593
+ _todoLeaves(todos, index = this._todoTreeIndex(todos)) {
580594
+ const leaves = todos.filter((todo) => (index.byParent.get(todo.id) ?? []).length === 0);
580595
+ return leaves.length > 0 ? leaves : todos;
580596
+ }
580597
+ _todoPath(todo, index) {
580598
+ const path12 = [todo];
580599
+ const seen = /* @__PURE__ */ new Set([todo.id]);
580600
+ let cursor = todo;
580601
+ while (cursor?.parentId && index.byId.has(cursor.parentId)) {
580602
+ const parent = index.byId.get(cursor.parentId);
580603
+ if (!parent || seen.has(parent.id))
580604
+ break;
580605
+ path12.unshift(parent);
580606
+ seen.add(parent.id);
580607
+ cursor = parent;
580608
+ }
580609
+ return path12;
580610
+ }
580611
+ _activeTodoForPrompt(todos, index = this._todoTreeIndex(todos)) {
580612
+ const leaves = this._todoLeaves(todos, index);
580613
+ return leaves.find((todo) => todo.status === "in_progress") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "in_progress") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "blocked") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "pending") ?? null;
580614
+ }
580615
+ _orderedTodosForPrompt(todos) {
580616
+ const index = this._todoTreeIndex(todos);
580617
+ const out = [];
580618
+ const seen = /* @__PURE__ */ new Set();
580619
+ const visit = (todo, depth) => {
580620
+ if (seen.has(todo.id))
580621
+ return;
580622
+ seen.add(todo.id);
580623
+ out.push({
580624
+ todo,
580625
+ depth: Math.min(depth, 4),
580626
+ displayStatus: this._todoDisplayStatus(todo, index),
580627
+ childStats: this._todoLeafStats(todo, index)
580628
+ });
580629
+ for (const child of index.byParent.get(todo.id) ?? []) {
580630
+ visit(child, depth + 1);
580631
+ }
580632
+ };
580633
+ for (const root of index.roots)
580634
+ visit(root, 0);
580635
+ for (const todo of todos)
580636
+ visit(todo, 0);
580637
+ return out;
580638
+ }
580433
580639
  _normalizeCommandForValidationMatch(command) {
580434
580640
  return normalizeShellCommand(command);
580435
580641
  }
@@ -580584,7 +580790,9 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
580584
580790
  const realFileMutation = input.realFileMutation ?? this._isRealProjectMutation(input.toolName, input.result);
580585
580791
  const attemptedTargetPaths = this._extractToolTargetPaths(input.toolName, input.args, input.result);
580586
580792
  const realMutationPaths = input.realMutationPaths ?? (realFileMutation ? attemptedTargetPaths : []);
580587
- if (input.result.success === true && this._isProjectEditTool(input.toolName) && !realFileMutation) {
580793
+ const shellVerification = input.toolName === "shell" && input.result.runtimeAuthored !== true && this._shellResultLooksLikeVerification(input.args, input.result);
580794
+ const shellVerificationFamily = shellVerification ? this._shellVerificationFamily(input.args) : "";
580795
+ if (input.result.success === true && this._isProjectEditTool(input.toolName) && !realFileMutation && input.result.alreadyApplied !== true) {
580588
580796
  return;
580589
580797
  }
580590
580798
  if (this._shouldSuppressCompletionDiscoveryEvidence(input.toolName, input.result, attemptedTargetPaths)) {
@@ -580596,9 +580804,9 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
580596
580804
  outputPreview: (input.outputPreview ?? this._toolEvidencePreview(input.result, void 0, 1200, input.toolName)).toString().slice(0, input.toolName === "audio_analyze" || input.toolName === "video_understand" ? 1200 : 500),
580597
580805
  argsKey: input.argsKey.slice(0, 300),
580598
580806
  targetPaths: attemptedTargetPaths,
580599
- role: input.result.completionEvidenceRole ?? (realFileMutation ? "mutation" : input.result.runtimeAuthored === true ? "blocker" : void 0),
580600
- family: input.result.completionEvidenceFamily ?? (input.result.runtimeAuthored === true ? "runtime_control" : void 0),
580601
- verificationImpact: input.result.completionVerificationImpact ?? (realFileMutation ? "invalidates" : void 0),
580807
+ role: input.result.completionEvidenceRole ?? (input.result.alreadyApplied === true ? "mutation" : realFileMutation ? "mutation" : input.result.runtimeAuthored === true ? "blocker" : shellVerification ? "verification" : void 0),
580808
+ family: input.result.completionEvidenceFamily ?? (input.result.runtimeAuthored === true ? "runtime_control" : shellVerification ? shellVerificationFamily : void 0),
580809
+ verificationImpact: input.result.completionVerificationImpact ?? (input.result.alreadyApplied === true ? "neutral" : realFileMutation ? "invalidates" : void 0),
580602
580810
  metrics: input.result.completionEvidenceMetrics
580603
580811
  });
580604
580812
  if (realFileMutation && realMutationPaths.length > 0) {
@@ -580617,6 +580825,20 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
580617
580825
  }
580618
580826
  this._saveCompletionLedgerSafe();
580619
580827
  }
580828
+ _shellVerificationFamily(args) {
580829
+ const command = String(args?.["command"] ?? args?.["cmd"] ?? "").trim();
580830
+ const normalized = normalizeShellCommand(command);
580831
+ return `shell:${(normalized || command || "verification").slice(0, 160)}`;
580832
+ }
580833
+ _shellResultLooksLikeVerification(args, result) {
580834
+ const command = String(args?.["command"] ?? args?.["cmd"] ?? "");
580835
+ if (!command.trim())
580836
+ return false;
580837
+ const text2 = `${command}
580838
+ ${result.output ?? ""}
580839
+ ${result.error ?? ""}`;
580840
+ return /\b(test|tests|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|tsc|build|verify|verification|check)\b/i.test(text2);
580841
+ }
580620
580842
  _shouldSuppressCompletionDiscoveryEvidence(toolName, result, targetPaths) {
580621
580843
  if (result.success !== true)
580622
580844
  return false;
@@ -581097,6 +581319,9 @@ ${context2 ?? ""}`;
581097
581319
  const originalGoal = (this._taskState.originalGoal || this._taskState.goal || "").trim();
581098
581320
  if (!originalGoal)
581099
581321
  return { proceed: true };
581322
+ if (/^\s*(BLOCKED|INCOMPLETE|NEEDS_INPUT)\b\s*:?/i.test(proposedSummary)) {
581323
+ return { proceed: true };
581324
+ }
581100
581325
  const actionable = toolCallLog.some((e2) => e2.mutated || e2.name === "shell" || e2.name === "file_write" || e2.name === "file_edit");
581101
581326
  if (!actionable)
581102
581327
  return { proceed: true };
@@ -581319,6 +581544,9 @@ ${outTail || "(no output captured)"}`,
581319
581544
  const _verify = await this._runCompletionVerifyGate(turn);
581320
581545
  if (!_verify.proceed)
581321
581546
  return _verify;
581547
+ if (this.options.backwardPassReview === false || this.options.disableAdversaryCritic === true) {
581548
+ return { proceed: true };
581549
+ }
581322
581550
  const _resolution = await this._runResolutionGate(turn, proposedSummary, toolCallLog);
581323
581551
  if (!_resolution.proceed) {
581324
581552
  return {
@@ -581662,14 +581890,18 @@ ${_checks}`
581662
581890
  const parts = [];
581663
581891
  const todos = this.readSessionTodos();
581664
581892
  if (todos && todos.length > 0) {
581665
- const current = todos.find((t2) => t2.status === "in_progress");
581666
- const completedN = todos.filter((t2) => t2.status === "completed").length;
581893
+ const index = this._todoTreeIndex(todos);
581894
+ const leaves = this._todoLeaves(todos, index);
581895
+ const current = this._activeTodoForPrompt(todos, index);
581896
+ const completedN = leaves.filter((t2) => t2.status === "completed").length;
581897
+ const totalN = leaves.length;
581667
581898
  if (current) {
581668
- parts.push(`[plan: ${completedN}/${todos.length} complete · currently: ${current.content.slice(0, 80)}]`);
581899
+ const path12 = this._todoPath(current, index).map((todo) => todo.content.slice(0, 64)).join(" > ");
581900
+ parts.push(`[plan: ${completedN}/${totalN} leaf tasks complete · active: ${path12.slice(0, 160)}]`);
581669
581901
  } else {
581670
- const nextPending = todos.find((t2) => t2.status === "pending");
581902
+ const nextPending = leaves.find((t2) => t2.status === "pending");
581671
581903
  if (nextPending) {
581672
- parts.push(`[plan: ${completedN}/${todos.length} complete · next: ${nextPending.content.slice(0, 80)}]`);
581904
+ parts.push(`[plan: ${completedN}/${totalN} leaf tasks complete · next: ${nextPending.content.slice(0, 80)}]`);
581673
581905
  }
581674
581906
  }
581675
581907
  }
@@ -582641,36 +582873,73 @@ ${latest.output || ""}`.trim();
582641
582873
  */
582642
582874
  _renderTodoStateBlock(turn) {
582643
582875
  try {
582644
- const todos = this.readSessionTodos();
582876
+ const todos = this._normalizeTodosForPrompt(this.readSessionTodos() ?? []);
582645
582877
  if (!todos || todos.length === 0)
582646
582878
  return null;
582879
+ const index = this._todoTreeIndex(todos);
582880
+ const leaves = this._todoLeaves(todos, index);
582881
+ const completedLeaves = leaves.filter((t2) => t2.status === "completed");
582882
+ const active = this._activeTodoForPrompt(todos, index);
582883
+ const activePath = active ? this._todoPath(active, index).map((todo) => todo.content.slice(0, 80)).join(" > ") : "";
582647
582884
  const lines = [
582648
- "[CURRENT TODO PLAN — already in your state, no need to call todo_write to see it]"
582885
+ "[CURRENT TODO PLAN — already in your state, no need to call todo_write to see it]",
582886
+ `leaf_progress=${completedLeaves.length}/${leaves.length}`
582649
582887
  ];
582650
- const inProg = todos.filter((t2) => t2.status === "in_progress");
582651
- const pending2 = todos.filter((t2) => t2.status === "pending");
582652
- const done = todos.filter((t2) => t2.status === "completed");
582653
- const blocked = todos.filter((t2) => t2.status === "blocked");
582654
- if (inProg.length > 0) {
582655
- lines.push(`▶ in_progress (${inProg.length}):`);
582656
- for (const t2 of inProg)
582657
- lines.push(` • ${t2.content}`);
582658
- }
582659
- if (pending2.length > 0) {
582660
- lines.push(`◯ pending (${pending2.length}):`);
582661
- for (const t2 of pending2.slice(0, 8))
582662
- lines.push(` • ${t2.content}`);
582663
- if (pending2.length > 8)
582664
- lines.push(` … +${pending2.length - 8} more`);
582888
+ if (activePath)
582889
+ lines.push(`active_branch=${activePath.slice(0, 220)}`);
582890
+ const ordered = this._orderedTodosForPrompt(todos);
582891
+ const activeIndex = active ? ordered.findIndex((entry) => entry.todo.id === active.id) : -1;
582892
+ let visible = ordered;
582893
+ if (ordered.length > 14) {
582894
+ const selected = [];
582895
+ const seen = /* @__PURE__ */ new Set();
582896
+ const add3 = (entry) => {
582897
+ if (!entry || selected.length >= 13 || seen.has(entry.todo.id))
582898
+ return;
582899
+ selected.push(entry);
582900
+ seen.add(entry.todo.id);
582901
+ };
582902
+ if (active && activeIndex >= 0) {
582903
+ const pathIds = new Set(this._todoPath(active, index).map((t2) => t2.id));
582904
+ for (const entry of ordered) {
582905
+ if (pathIds.has(entry.todo.id) && entry.todo.id !== active.id)
582906
+ add3(entry);
582907
+ }
582908
+ const siblings = active.parentId ? index.byParent.get(active.parentId) ?? [] : index.roots;
582909
+ const siblingIndex = siblings.findIndex((t2) => t2.id === active.id);
582910
+ if (siblingIndex > 0) {
582911
+ add3(ordered.find((entry) => entry.todo.id === siblings[siblingIndex - 1]?.id));
582912
+ }
582913
+ add3(ordered[activeIndex]);
582914
+ for (let i2 = siblingIndex + 1; i2 < siblings.length && selected.length < 13; i2++) {
582915
+ add3(ordered.find((entry) => entry.todo.id === siblings[i2]?.id));
582916
+ }
582917
+ for (const child of index.byParent.get(active.id) ?? []) {
582918
+ add3(ordered.find((entry) => entry.todo.id === child.id));
582919
+ }
582920
+ for (let i2 = activeIndex + 1; i2 < ordered.length && selected.length < 13; i2++) {
582921
+ add3(ordered[i2]);
582922
+ }
582923
+ }
582924
+ visible = selected.length > 0 ? selected : ordered.slice(0, 13);
582665
582925
  }
582666
- if (blocked.length > 0) {
582667
- lines.push(`✕ blocked (${blocked.length}):`);
582668
- for (const t2 of blocked)
582669
- lines.push(` • ${t2.content}${t2.blocker ? ` — ${t2.blocker}` : ""}`);
582926
+ const statusMark = {
582927
+ completed: "OK",
582928
+ in_progress: ">>",
582929
+ pending: "--",
582930
+ blocked: "!!"
582931
+ };
582932
+ lines.push("tree:");
582933
+ for (const entry of visible) {
582934
+ const indent2 = " ".repeat(entry.depth);
582935
+ const childSuffix = entry.childStats && entry.childStats.total > 0 ? ` [${entry.childStats.completed}/${entry.childStats.total}]` : "";
582936
+ const blocker = entry.todo.blocker ? ` blocker=${entry.todo.blocker.slice(0, 120)}` : "";
582937
+ lines.push(`${indent2}${statusMark[entry.displayStatus]} ${entry.todo.content.slice(0, 140)}${childSuffix}${blocker}`);
582670
582938
  }
582671
- if (done.length > 0) {
582672
- lines.push(`✓ completed (${done.length}): ${done.map((t2) => t2.content.slice(0, 40)).join(" | ")}`);
582939
+ if (ordered.length > visible.length) {
582940
+ lines.push(`... +${ordered.length - visible.length} more todo node(s)`);
582673
582941
  }
582942
+ lines.push("decomposition_contract=Use stable ids plus parentId for subtasks. If the active item has multiple remaining actions, rewrite it as a parent with concrete child leaf todos; work leaf-first and do not complete a parent before all children are complete with evidence.");
582674
582943
  lines.push(`(turn ${turn} — call todo_write ONLY to update state, never to re-read this plan)`);
582675
582944
  return lines.join("\n");
582676
582945
  } catch {
@@ -583217,6 +583486,7 @@ ${chunk.content}`, {
583217
583486
  this._taskState.goal ? `Active task: (see user message above — the goal block defers to the user message as the authoritative source)` : null
583218
583487
  ].filter(Boolean).join("\n\n");
583219
583488
  const filesystemBlock = this._renderFilesystemStateBlock(turn);
583489
+ const artifactContractBlock = this._contractRegistry.format(15);
583220
583490
  const todoBlock = this._renderTodoStateBlock(turn);
583221
583491
  const failureBlock = this._renderRecentFailuresBlock(turn);
583222
583492
  const churnBlock = this._renderWriteChurnBlock(turn);
@@ -583259,6 +583529,13 @@ ${this._lastPprMemoryLines.slice(0, 5).join("\n")}` : null;
583259
583529
  createdTurn: turn,
583260
583530
  ttlTurns: 1
583261
583531
  }),
583532
+ signalFromBlock("known_files", "turn.artifact-contracts", artifactContractBlock, {
583533
+ id: "artifact-contracts",
583534
+ dedupeKey: "turn.artifact-contracts",
583535
+ priority: 86,
583536
+ createdTurn: turn,
583537
+ ttlTurns: 1
583538
+ }),
583262
583539
  signalFromBlock("action_contract", "turn.next-action-contract", actionContractBlock, {
583263
583540
  id: "next-action-contract",
583264
583541
  dedupeKey: "turn.next-action-contract",
@@ -584588,7 +584865,6 @@ Respond with your assessment, then take action.`;
584588
584865
  const hardDeadlineMs = Number.isFinite(taskTimeoutMs) && taskTimeoutMs > 0 ? start2 + taskTimeoutMs : Number.POSITIVE_INFINITY;
584589
584866
  let timedOut = false;
584590
584867
  let timeoutSummary = "";
584591
- let _timeoutAdvisoryInjected = false;
584592
584868
  const remainingTaskMs = () => Number.isFinite(hardDeadlineMs) ? Math.max(0, hardDeadlineMs - Date.now()) : Number.POSITIVE_INFINITY;
584593
584869
  const boundedRequestTimeoutMs = () => {
584594
584870
  const configured = Number.isFinite(this.options.requestTimeoutMs) && this.options.requestTimeoutMs > 0 ? this.options.requestTimeoutMs : 3e5;
@@ -584618,7 +584894,8 @@ Respond with your assessment, then take action.`;
584618
584894
  return false;
584619
584895
  if (Date.now() < hardDeadlineMs)
584620
584896
  return false;
584621
- return !_timeoutAdvisoryInjected;
584897
+ markTaskTimedOut(phase, turn);
584898
+ return true;
584622
584899
  };
584623
584900
  const selfEvalInterval = taskTimeoutMs;
584624
584901
  let nextSelfEval = start2 + selfEvalInterval;
@@ -586647,12 +586924,13 @@ ${_staleSamples.join("\n")}` : ``,
586647
586924
  content: `[TASK DECOMPOSITION — Multi-step task detected]
586648
586925
 
586649
586926
  MANDATORY FIRST ACTION: Call todo_write NOW with the complete plan.
586650
- Each todo item is { content: "what to do", status: "pending" | "in_progress" | "completed" | "blocked" }.
586651
- Mark item 1 as in_progress, the rest as pending.
586927
+ Each todo item is { id, content, status, parentId? } or a parent with children/subtasks. Use stable ids.
586928
+ Represent broad objectives as parent todos and concrete work as child leaf todos. Mark the active leaf in_progress, the rest pending.
586652
586929
  Only count substantive work phases. Do NOT count observing a tool result, reporting findings, or calling task_complete as todo phases.
586653
- Example: todo_write({todos: [{content: "read source files", status: "in_progress"}, {content: "make changes", status: "pending"}, {content: "run tests", status: "pending"}]})
586930
+ Example: todo_write({todos: [{id: "impl", content: "Implement requested change", status: "in_progress", children: [{id: "inspect", content: "Read relevant files", status: "in_progress"}, {id: "edit", content: "Make scoped edits", status: "pending"}, {id: "verify", content: "Run verification", status: "pending"}]}]})
586654
586931
 
586655
- After EACH phase finishes, call todo_write AGAIN with item N marked completed and item N+1 marked in_progress.
586932
+ After EACH leaf finishes, call todo_write AGAIN with that leaf completed and the next leaf in_progress.
586933
+ If new work is discovered, decompose the current leaf into children or add siblings under the same parent instead of flattening a long list.
586656
586934
  The user watches this checklist update live in the chat UI. Without it they cannot see your plan or track progress.
586657
586935
 
586658
586936
  Then complete these phases IN ORDER:
@@ -586971,10 +587249,6 @@ ${memoryLines.join("\n")}`
586971
587249
  this.proactivePrune(compacted, turn);
586972
587250
  this.microcompact(compacted, recentToolResults);
586973
587251
  this._insertContextFrame(compacted, await this._buildTurnContextFrame(turn, compacted, recentToolResults, environmentBlock));
586974
- const contractBlock = this._contractRegistry.format(15);
586975
- if (contractBlock) {
586976
- compacted.push({ role: "system", content: contractBlock });
586977
- }
586978
587252
  this.gcSystemMessages(compacted);
586979
587253
  let requestMessages = sanitizeHistoryThink(compacted);
586980
587254
  {
@@ -588639,7 +588913,7 @@ Respond with EXACTLY this structure before your next tool call:
588639
588913
  } else if (tc.name === "shell") {
588640
588914
  const cmd = String(tc.arguments?.["command"] ?? "");
588641
588915
  if (/\b(npm|pnpm|yarn)\s+test\b|\bjest\b|\bvitest\b/i.test(cmd)) {
588642
- const passed = /PASS|✓\s*all/i.test(result.output) && !/FAIL|✗/i.test(result.output);
588916
+ const passed = /\b(PASS|pass|passed|success|ok)\b|✓\s*all/i.test(result.output) && !/FAIL|✗/i.test(result.output);
588643
588917
  this._worldFacts.lastTest = {
588644
588918
  passed,
588645
588919
  summary: result.output.slice(0, 200),
@@ -589106,7 +589380,8 @@ Respond with EXACTLY this structure before your next tool call:
589106
589380
  const _shellCmd2 = String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? "");
589107
589381
  const _declaredVerify = this._matchedDeclaredVerifyCommand(_shellCmd2);
589108
589382
  const _lastVerifier = this._lastVerifierResult;
589109
- const _noTodoDirectValidation = !_declaredVerify && (this.readSessionTodos() || []).length === 0 && this._taskState.modifiedFiles.size > 0 && _lastVerifier?.outcomeClass === "verified";
589383
+ const _directShellValidation = result.success === true && this._shellResultLooksLikeVerification(tc.arguments, result);
589384
+ const _noTodoDirectValidation = !_declaredVerify && (this.readSessionTodos() || []).length === 0 && this._taskState.modifiedFiles.size > 0 && (_lastVerifier?.outcomeClass === "verified" || _directShellValidation);
589110
589385
  const _legacyGenericValidation = process.env["OMNIUS_ENABLE_GENERIC_COMPLETION_COMMAND_HEURISTIC"] === "1" && /\b(build|test|run\b|start\b|serve\b|verify|check)\b/i.test(_shellCmd2);
589111
589386
  if (_declaredVerify || _noTodoDirectValidation || _legacyGenericValidation) {
589112
589387
  this._lastBuildSuccessTurn = turn;
@@ -589281,7 +589556,7 @@ Respond with EXACTLY this structure before your next tool call:
589281
589556
  }
589282
589557
  const cacheableMemoryNoop = tc.name === "memory_write" && result.success && result.noop;
589283
589558
  const cacheableProjectWriteNoop = this._isProjectEditTool(tc.name) && result.success && !this._isRealProjectMutation(tc.name, result) && (result.noop === true || result.mutated === false);
589284
- if (isReadLike && result.success || tc.name === "shell" || cacheableMemoryNoop || cacheableProjectWriteNoop) {
589559
+ if (isReadLike && result.success && result.runtimeAuthored !== true || tc.name === "shell" && result.runtimeAuthored !== true || cacheableMemoryNoop || cacheableProjectWriteNoop) {
589285
589560
  recentToolResults.set(toolFingerprint, {
589286
589561
  result: tc.name === "shell" ? this._buildShellCacheResult(tc.arguments, result) : (
589287
589562
  // Read-like results are SERVED BACK to the model when the
@@ -589400,7 +589675,7 @@ Respond with EXACTLY this structure before your next tool call:
589400
589675
  }
589401
589676
  }
589402
589677
  }
589403
- if (!result.success) {
589678
+ if (!result.success && result.runtimeAuthored !== true) {
589404
589679
  this._recentFailures.push({
589405
589680
  tool: tc.name,
589406
589681
  fingerprint: toolFingerprint,
@@ -589638,7 +589913,7 @@ ${bookkeepingGuidance}` : bookkeepingGuidance;
589638
589913
  argsPreview: toolArgsPreview,
589639
589914
  targetPaths: toolTargetPaths,
589640
589915
  family: result.completionEvidenceFamily ?? (result.runtimeAuthored === true ? "runtime_control" : void 0),
589641
- role: result.completionEvidenceRole ?? (realFileMutation || shellFilesystemMutation ? "mutation" : result.runtimeAuthored === true ? "blocker" : void 0),
589916
+ role: result.completionEvidenceRole ?? (result.alreadyApplied === true ? "mutation" : realFileMutation || shellFilesystemMutation ? "mutation" : result.runtimeAuthored === true ? "blocker" : void 0),
589642
589917
  turn
589643
589918
  });
589644
589919
  if (this._toolEvents.length > 200)
@@ -589787,6 +590062,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
589787
590062
  mutated: realFileMutation || shellFilesystemMutation,
589788
590063
  isReadLike,
589789
590064
  noop: tc.name === "todo_write" ? this._isTodoWriteNoopResult(result) : result.noop,
590065
+ alreadyApplied: result.alreadyApplied === true,
589790
590066
  runtimeAuthored: result.runtimeAuthored === true
589791
590067
  });
589792
590068
  const focusSnapshotAfter = this._focusSupervisor?.snapshot();
@@ -590885,10 +591161,6 @@ ${this.options.maxTurns && this.options.maxTurns > 0 ? `You have ${this.options.
590885
591161
  }
590886
591162
  }
590887
591163
  this._insertContextFrame(compactedMsgs, await this._buildTurnContextFrame(turn, compactedMsgs, void 0, bfEnvironmentBlock));
590888
- const bfContractBlock = this._contractRegistry.format(15);
590889
- if (bfContractBlock) {
590890
- compactedMsgs.push({ role: "system", content: bfContractBlock });
590891
- }
590892
591164
  this.gcSystemMessages(compactedMsgs);
590893
591165
  const modelFacingMessages = sanitizeHistoryThink(compactedMsgs);
590894
591166
  const chatRequest = {
@@ -591431,7 +591703,7 @@ ${this._completionCaveat}` : this._completionCaveat;
591431
591703
  if (this._completionLedger) {
591432
591704
  this._completionLedger = finalizeCompletionLedgerTruth(this._completionLedger);
591433
591705
  this._saveCompletionLedgerSafe();
591434
- if (completed && this._completionLedger.status === "incomplete_verification") {
591706
+ if (completed && this._completionLedger.status === "incomplete_verification" && this.options.backwardPassReview !== false && !this._completionCaveat) {
591435
591707
  completed = false;
591436
591708
  const unresolvedSummary = this._completionLedger.unresolved.slice(-3).map((item) => item.text).join("; ");
591437
591709
  const caveat = unresolvedSummary ? `Incomplete verification: ${unresolvedSummary}` : "Incomplete verification: unresolved evidence remains in the completion ledger.";
@@ -592093,33 +592365,49 @@ ${caveat}` : caveat;
592093
592365
  this._saveWorkboard();
592094
592366
  await this._hookManager.runSessionHook("session_end", this._sessionId);
592095
592367
  try {
592096
- const fs11 = await import("node:fs");
592097
- const path12 = await import("node:path");
592098
- const trajDir = path12.join(this.omniusStateDir(), "trajectories");
592099
- fs11.mkdirSync(trajDir, { recursive: true });
592100
- const trajectory = {
592101
- id: this._sessionId,
592102
- // CLEAN task this file is the prerequisite for ALL future RL/RFT
592103
- // training. Storing the scaffolded version would teach future models
592104
- // to reproduce signpost text as part of their task understanding.
592105
- task: persistentTaskGoal.slice(0, 1e3),
592106
- outcome: completed ? "pass" : this.aborted ? "aborted" : runStatus === "incomplete_verification" ? "incomplete_verification" : "timeout",
592107
- model: this.backend.model ?? "unknown",
592108
- modelTier: this.options.modelTier ?? "large",
592109
- turns: messages2.filter((m2) => m2.role === "assistant").length,
592110
- toolCalls: toolCallLog.map((tc) => ({
592111
- name: tc.name,
592112
- argsFingerprint: tc.argsKey.slice(0, 100)
592113
- })),
592114
- totalToolCalls: toolCallCount,
592115
- totalTokens,
592116
- filesModified: [...this._taskState.modifiedFiles.keys()],
592117
- failedApproaches: this._taskState.failedApproaches,
592118
- completedSteps: this._taskState.completedSteps,
592119
- durationMs,
592120
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
592121
- };
592122
- fs11.appendFileSync(path12.join(trajDir, `trajectories.jsonl`), JSON.stringify(trajectory) + "\n", "utf-8");
592368
+ const trajectoryQuality = assessTaskArtifactQuality({
592369
+ artifactMode: this.options.artifactMode,
592370
+ goal: persistentTaskGoal,
592371
+ summary,
592372
+ toolsUsed: toolCallLog.map((tc) => tc.name),
592373
+ filesTouched: [...this._taskState.modifiedFiles.keys()],
592374
+ turns: messages2.filter((m2) => m2.role === "assistant").length
592375
+ });
592376
+ if (!trajectoryQuality.eligible) {
592377
+ this.emit({
592378
+ type: "status",
592379
+ content: `Trajectory log skipped: ${trajectoryQuality.reason ?? "not eligible"}`,
592380
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
592381
+ });
592382
+ } else {
592383
+ const fs11 = await import("node:fs");
592384
+ const path12 = await import("node:path");
592385
+ const trajDir = path12.join(this.omniusStateDir(), "trajectories");
592386
+ fs11.mkdirSync(trajDir, { recursive: true });
592387
+ const trajectory = {
592388
+ id: this._sessionId,
592389
+ // CLEAN task — this file is the prerequisite for ALL future RL/RFT
592390
+ // training. Storing the scaffolded version would teach future models
592391
+ // to reproduce signpost text as part of their task understanding.
592392
+ task: persistentTaskGoal.slice(0, 1e3),
592393
+ outcome: completed ? "pass" : this.aborted ? "aborted" : runStatus === "incomplete_verification" ? "incomplete_verification" : "timeout",
592394
+ model: this.backend.model ?? "unknown",
592395
+ modelTier: this.options.modelTier ?? "large",
592396
+ turns: messages2.filter((m2) => m2.role === "assistant").length,
592397
+ toolCalls: toolCallLog.map((tc) => ({
592398
+ name: tc.name,
592399
+ argsFingerprint: tc.argsKey.slice(0, 100)
592400
+ })),
592401
+ totalToolCalls: toolCallCount,
592402
+ totalTokens,
592403
+ filesModified: [...this._taskState.modifiedFiles.keys()],
592404
+ failedApproaches: this._taskState.failedApproaches,
592405
+ completedSteps: this._taskState.completedSteps,
592406
+ durationMs,
592407
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
592408
+ };
592409
+ fs11.appendFileSync(path12.join(trajDir, `trajectories.jsonl`), JSON.stringify(trajectory) + "\n", "utf-8");
592410
+ }
592123
592411
  } catch {
592124
592412
  }
592125
592413
  let filesEdited;
@@ -592615,7 +592903,7 @@ ${marker}` : marker);
592615
592903
  if (!target)
592616
592904
  return;
592617
592905
  const targetPathKey = this.staleEditPathKey(target.path);
592618
- if (this._isRealProjectMutation(toolName, result)) {
592906
+ if (this._isRealProjectMutation(toolName, result) || result.alreadyApplied === true) {
592619
592907
  for (const [key2, entry] of this._staleEditFamilies) {
592620
592908
  if (entry.pathKey === targetPathKey)
592621
592909
  this._staleEditFamilies.delete(key2);
@@ -629489,6 +629777,8 @@ var init_overlay_lock = __esm({
629489
629777
  // packages/cli/src/tui/tui-tasks-renderer.ts
629490
629778
  var tui_tasks_renderer_exports = {};
629491
629779
  __export(tui_tasks_renderer_exports, {
629780
+ __testOrderTodosForDisplay: () => __testOrderTodosForDisplay,
629781
+ __testSelectPanelTodoRows: () => __testSelectPanelTodoRows,
629492
629782
  getTuiTasksScope: () => getTuiTasksScope,
629493
629783
  onTuiTasksHeightChange: () => onTuiTasksHeightChange,
629494
629784
  refreshTuiTasks: () => refreshTuiTasks,
@@ -629646,7 +629936,7 @@ function computeTargetHeight() {
629646
629936
  if (!_lastTodos || _lastTodos.length === 0) return 0;
629647
629937
  const allDone = _lastTodos.every((t2) => t2 && t2.status === "completed");
629648
629938
  if (allDone) return 0;
629649
- const itemRows = Math.min(_lastTodos.length, MAX_VISIBLE_ROWS - 1);
629939
+ const itemRows = selectPanelTodoRows(_lastTodos, MAX_VISIBLE_ROWS - 1).rowCount;
629650
629940
  return 1 + itemRows;
629651
629941
  }
629652
629942
  function applyHeightChange(nextHeight) {
@@ -629724,9 +630014,9 @@ function buildTodoProgressBar(todos, maxWidth) {
629724
630014
  if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET3}`;
629725
630015
  return out;
629726
630016
  }
629727
- function orderTodosForDisplay(todos) {
630017
+ function buildTodoTreeIndex(todos) {
629728
630018
  const byParent = /* @__PURE__ */ new Map();
629729
- const byId = new Set(todos.map((t2) => t2.id));
630019
+ const byId = new Map(todos.map((t2) => [t2.id, t2]));
629730
630020
  const roots = [];
629731
630021
  for (const todo of todos) {
629732
630022
  if (todo.parentId && byId.has(todo.parentId)) {
@@ -629737,20 +630027,146 @@ function orderTodosForDisplay(todos) {
629737
630027
  roots.push(todo);
629738
630028
  }
629739
630029
  }
630030
+ return { byId, byParent, roots };
630031
+ }
630032
+ function childLeafStats(todo, index) {
630033
+ const children2 = index.byParent.get(todo.id) ?? [];
630034
+ if (children2.length === 0) return void 0;
630035
+ let completed = 0;
630036
+ let total = 0;
630037
+ const seen = /* @__PURE__ */ new Set();
630038
+ const visit = (node) => {
630039
+ if (seen.has(node.id)) return;
630040
+ seen.add(node.id);
630041
+ const nested = index.byParent.get(node.id) ?? [];
630042
+ if (nested.length === 0) {
630043
+ total++;
630044
+ if (node.status === "completed") completed++;
630045
+ return;
630046
+ }
630047
+ for (const child of nested) visit(child);
630048
+ };
630049
+ for (const child of children2) visit(child);
630050
+ return { completed, total };
630051
+ }
630052
+ function displayStatusFor(todo, index, seen = /* @__PURE__ */ new Set()) {
630053
+ if (seen.has(todo.id)) return todo.status;
630054
+ seen.add(todo.id);
630055
+ const children2 = index.byParent.get(todo.id) ?? [];
630056
+ if (children2.length === 0) return todo.status;
630057
+ const childStatuses = children2.map((child) => displayStatusFor(child, index, new Set(seen)));
630058
+ if (childStatuses.some((status) => status === "blocked")) return "blocked";
630059
+ if (childStatuses.some((status) => status === "in_progress")) return "in_progress";
630060
+ if (childStatuses.every((status) => status === "completed")) return "completed";
630061
+ if (todo.status === "completed") return "in_progress";
630062
+ return todo.status;
630063
+ }
630064
+ function toDisplayTodo(todo, depth, index) {
630065
+ return {
630066
+ ...todo,
630067
+ depth: Math.min(depth, 4),
630068
+ displayStatus: displayStatusFor(todo, index),
630069
+ childSummary: childLeafStats(todo, index)
630070
+ };
630071
+ }
630072
+ function orderTodosForDisplay(todos) {
630073
+ const index = buildTodoTreeIndex(todos);
629740
630074
  const out = [];
629741
630075
  const seen = /* @__PURE__ */ new Set();
629742
630076
  const visit = (todo, depth) => {
629743
630077
  if (seen.has(todo.id)) return;
629744
630078
  seen.add(todo.id);
629745
- out.push({ ...todo, depth: Math.min(depth, 4) });
629746
- for (const child of byParent.get(todo.id) ?? []) {
630079
+ out.push(toDisplayTodo(todo, depth, index));
630080
+ for (const child of index.byParent.get(todo.id) ?? []) {
629747
630081
  visit(child, depth + 1);
629748
630082
  }
629749
630083
  };
629750
- for (const root of roots) visit(root, 0);
630084
+ for (const root of index.roots) visit(root, 0);
629751
630085
  for (const todo of todos) visit(todo, 0);
629752
630086
  return out;
629753
630087
  }
630088
+ function leafTodosForProgress(todos) {
630089
+ const index = buildTodoTreeIndex(todos);
630090
+ const leaves = todos.filter((todo) => (index.byParent.get(todo.id) ?? []).length === 0);
630091
+ return leaves.length > 0 ? leaves : todos;
630092
+ }
630093
+ function activeDisplayIndex(displayTodos) {
630094
+ const inProgressLeaf = displayTodos.findIndex(
630095
+ (todo, index, all2) => todo.status === "in_progress" && !all2.some((candidate) => candidate.parentId === todo.id)
630096
+ );
630097
+ if (inProgressLeaf >= 0) return inProgressLeaf;
630098
+ const inProgress = displayTodos.findIndex((todo) => todo.displayStatus === "in_progress");
630099
+ if (inProgress >= 0) return inProgress;
630100
+ const blocked = displayTodos.findIndex((todo) => todo.displayStatus === "blocked");
630101
+ if (blocked >= 0) return blocked;
630102
+ return displayTodos.findIndex((todo) => todo.displayStatus === "pending");
630103
+ }
630104
+ function selectFocusedTodoItems(todos, maxItems) {
630105
+ const displayTodos = orderTodosForDisplay(todos);
630106
+ if (maxItems <= 0) return [];
630107
+ if (displayTodos.length <= maxItems) return displayTodos;
630108
+ const activeIndex = activeDisplayIndex(displayTodos);
630109
+ if (activeIndex < 0 || activeIndex < maxItems) return displayTodos.slice(0, maxItems);
630110
+ const rawIndex = buildTodoTreeIndex(todos);
630111
+ const displayById = new Map(displayTodos.map((todo) => [todo.id, todo]));
630112
+ const selected = [];
630113
+ const seen = /* @__PURE__ */ new Set();
630114
+ const add3 = (todo) => {
630115
+ if (!todo || selected.length >= maxItems || seen.has(todo.id)) return;
630116
+ selected.push(todo);
630117
+ seen.add(todo.id);
630118
+ };
630119
+ const active = displayTodos[activeIndex];
630120
+ const ancestors = [];
630121
+ let cursor = active;
630122
+ while (cursor?.parentId && rawIndex.byId.has(cursor.parentId)) {
630123
+ const parent = rawIndex.byId.get(cursor.parentId);
630124
+ if (!parent) break;
630125
+ const displayParent = displayById.get(parent.id);
630126
+ if (displayParent) ancestors.unshift(displayParent);
630127
+ cursor = parent;
630128
+ }
630129
+ for (const ancestor of ancestors) add3(ancestor);
630130
+ const siblings = active.parentId ? rawIndex.byParent.get(active.parentId) ?? [] : rawIndex.roots;
630131
+ const activeSiblingIndex = siblings.findIndex((todo) => todo.id === active.id);
630132
+ if (activeSiblingIndex > 0) add3(displayById.get(siblings[activeSiblingIndex - 1].id));
630133
+ add3(active);
630134
+ for (let i2 = activeSiblingIndex + 1; i2 < siblings.length && selected.length < maxItems; i2++) {
630135
+ add3(displayById.get(siblings[i2].id));
630136
+ }
630137
+ const activeChildren = rawIndex.byParent.get(active.id) ?? [];
630138
+ for (const child of activeChildren) add3(displayById.get(child.id));
630139
+ for (let i2 = activeIndex + 1; i2 < displayTodos.length && selected.length < maxItems; i2++) {
630140
+ add3(displayTodos[i2]);
630141
+ }
630142
+ for (let i2 = activeIndex - 1; i2 >= 0 && selected.length < maxItems; i2--) {
630143
+ add3(displayTodos[i2]);
630144
+ }
630145
+ return selected;
630146
+ }
630147
+ function selectPanelTodoRows(todos, maxRows) {
630148
+ const displayTodos = orderTodosForDisplay(todos);
630149
+ if (maxRows <= 0 || displayTodos.length === 0) {
630150
+ return { items: [], omitted: displayTodos.length, rowCount: 0 };
630151
+ }
630152
+ if (displayTodos.length <= maxRows) {
630153
+ return { items: displayTodos, omitted: 0, rowCount: displayTodos.length };
630154
+ }
630155
+ const itemBudget = Math.max(1, maxRows - 1);
630156
+ const items = selectFocusedTodoItems(todos, itemBudget);
630157
+ const omitted = Math.max(0, displayTodos.length - items.length);
630158
+ return {
630159
+ items,
630160
+ omitted,
630161
+ rowCount: Math.min(maxRows, items.length + (omitted > 0 ? 1 : 0))
630162
+ };
630163
+ }
630164
+ function __testOrderTodosForDisplay(todos) {
630165
+ return orderTodosForDisplay(todos);
630166
+ }
630167
+ function __testSelectPanelTodoRows(todos, maxRows) {
630168
+ return selectPanelTodoRows(todos, maxRows);
630169
+ }
629754
630170
  function render() {
629755
630171
  if (!_enabled) return;
629756
630172
  if (!panelEffectivelyVisible()) {
@@ -629766,29 +630182,30 @@ function render() {
629766
630182
  }
629767
630183
  const L = layout();
629768
630184
  const cols = Math.max(20, termCols());
629769
- const completed = _lastTodos.filter((t2) => t2.status === "completed").length;
629770
- const total = _lastTodos.length;
630185
+ const progressTodos = leafTodosForProgress(_lastTodos);
630186
+ const completed = progressTodos.filter((t2) => t2.status === "completed").length;
630187
+ const total = progressTodos.length;
629771
630188
  const headerColor = ACCENT;
629772
630189
  const lines = [];
629773
630190
  const headerPrefix = `tasks ${headerColor}${completed}/${total}${RESET3} `;
629774
630191
  const headerPrefixWidth = visualLen(headerPrefix);
629775
630192
  const maxBarWidth = Math.max(0, cols - 2 - headerPrefixWidth);
629776
- const progressBar = buildTodoProgressBar(_lastTodos, maxBarWidth);
630193
+ const progressBar = buildTodoProgressBar(progressTodos, maxBarWidth);
629777
630194
  const headerText = `${headerPrefix}${progressBar}`;
629778
630195
  lines.push(headerText);
629779
- const displayTodos = orderTodosForDisplay(_lastTodos);
629780
- const visible = displayTodos.slice(0, MAX_VISIBLE_ROWS - 1);
630196
+ const selection = selectPanelTodoRows(_lastTodos, MAX_VISIBLE_ROWS - 1);
630197
+ const visible = selection.items;
629781
630198
  for (const t2 of visible) {
629782
- const { mark, color } = statusToAnsi(t2.status);
630199
+ const { mark, color } = statusToAnsi(t2.displayStatus);
629783
630200
  const contentWidth = Math.max(4, cols - 8);
629784
- const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth - 1)}- ` : "";
629785
- const contentText = indent2 + t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
630201
+ const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth)}- ` : "";
630202
+ const childSummary = t2.childSummary && t2.childSummary.total > 0 ? ` [${t2.childSummary.completed}/${t2.childSummary.total}]` : "";
630203
+ const contentText = indent2 + t2.content + childSummary + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
629786
630204
  const truncated = truncate2(contentText, contentWidth);
629787
630205
  lines.push(`${color}${mark}${RESET3} ${color}${truncated}${RESET3}`);
629788
630206
  }
629789
- if (displayTodos.length > visible.length) {
629790
- const more = displayTodos.length - visible.length;
629791
- lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET3}`;
630207
+ if (selection.omitted > 0) {
630208
+ lines.push(`${DIM_LABEL}… +${selection.omitted} more${RESET3}`);
629792
630209
  }
629793
630210
  let out = HIDE + SAVE;
629794
630211
  const newTop = L.tasksTop;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.452",
3
+ "version": "1.0.453",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.452",
9
+ "version": "1.0.453",
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.452",
3
+ "version": "1.0.453",
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",