omnius 1.0.369 → 1.0.371

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
@@ -295152,7 +295152,13 @@ var init_todo_write = __esm({
295152
295152
  - completed: fully done (tests pass, code works, goal met)
295153
295153
  - blocked: stuck on a dependency (include blocker text)
295154
295154
 
295155
- 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":[{"content":"Inspect files","status":"in_progress"},{"content":"Make changes","status":"pending"},{"content":"Verify results","status":"pending"}]})`;
295155
+ ## Nested decomposition
295156
+ - Use stable ids plus parentId to create subtasks under a parent objective.
295157
+ - Work on leaf subtasks; a parent is completed only after every child is completed with evidence.
295158
+ - 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.
295159
+ - 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.
295160
+
295161
+ 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"}]})`;
295156
295162
  parameters = {
295157
295163
  type: "object",
295158
295164
  required: ["todos"],
@@ -295170,7 +295176,7 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
295170
295176
  type: "string",
295171
295177
  enum: ["pending", "in_progress", "completed", "blocked"]
295172
295178
  },
295173
- parentId: { type: "string", description: "Parent todo id for sub-tasks" },
295179
+ parentId: { type: "string", description: "Parent todo id for nested sub-tasks. Parents summarize objectives; children carry concrete implementation/verification work. Never complete a parent until every child is completed with evidence." },
295174
295180
  blocker: { type: "string", description: "Reason this is blocked (status=blocked only)" },
295175
295181
  verifyCommand: {
295176
295182
  type: "string",
@@ -295287,6 +295293,7 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
295287
295293
  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.";
295288
295294
  const payload = {
295289
295295
  reminder,
295296
+ 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.",
295290
295297
  oldTodos: result.oldTodos,
295291
295298
  newTodos: result.newTodos,
295292
295299
  verificationNudgeNeeded
@@ -295295,9 +295302,10 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
295295
295302
  payload["inputRepair"] = Array.from(new Set(repairNotes));
295296
295303
  payload["canonicalShape"] = {
295297
295304
  todos: [
295298
- { content: "Inspect files", status: "in_progress" },
295299
- { content: "Make changes", status: "pending" },
295300
- { content: "Verify results", status: "pending" }
295305
+ { id: "p1", content: "Implement the requested change", status: "in_progress" },
295306
+ { id: "c1", parentId: "p1", content: "Inspect files", status: "in_progress" },
295307
+ { id: "c2", parentId: "p1", content: "Make changes", status: "pending" },
295308
+ { id: "c3", parentId: "p1", content: "Verify results", status: "pending" }
295301
295309
  ]
295302
295310
  };
295303
295311
  }
@@ -546587,6 +546595,32 @@ ${content.slice(0, 500)}`,
546587
546595
  }
546588
546596
  });
546589
546597
 
546598
+ // packages/execution/dist/tools/clip-feature-python.js
546599
+ var CLIP_FEATURE_HELPERS_PY;
546600
+ var init_clip_feature_python = __esm({
546601
+ "packages/execution/dist/tools/clip-feature-python.js"() {
546602
+ "use strict";
546603
+ CLIP_FEATURE_HELPERS_PY = `
546604
+ def _omnius_feature_tensor(features):
546605
+ if hasattr(features, "image_embeds"):
546606
+ return features.image_embeds
546607
+ if hasattr(features, "text_embeds"):
546608
+ return features.text_embeds
546609
+ if hasattr(features, "pooler_output"):
546610
+ return features.pooler_output
546611
+ if hasattr(features, "last_hidden_state"):
546612
+ return features.last_hidden_state[:, 0]
546613
+ if isinstance(features, (tuple, list)):
546614
+ return features[0]
546615
+ return features
546616
+
546617
+ def _omnius_normalized_features(features):
546618
+ tensor = _omnius_feature_tensor(features)
546619
+ return tensor / tensor.norm(dim=-1, keepdim=True)
546620
+ `;
546621
+ }
546622
+ });
546623
+
546590
546624
  // packages/execution/dist/tools/visual-memory.js
546591
546625
  import { execSync as execSync39 } from "node:child_process";
546592
546626
  import { existsSync as existsSync66, mkdirSync as mkdirSync39, writeFileSync as writeFileSync31, readFileSync as readFileSync48 } from "node:fs";
@@ -546639,19 +546673,38 @@ function normalizeVisualMemoryAction(args) {
546639
546673
  }
546640
546674
  function summarizeProcessFailure(stdout, stderr) {
546641
546675
  const normalize2 = (text2) => text2.replace(/\r/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean);
546642
- const lines = [
546643
- ...normalize2(stderr).map((line) => `stderr: ${line}`),
546644
- ...normalize2(stdout).map((line) => `stdout: ${line}`)
546676
+ const stderrLines = normalize2(stderr);
546677
+ const stdoutLines = normalize2(stdout);
546678
+ const tagged = [
546679
+ ...stderrLines.map((line) => `stderr: ${line}`),
546680
+ ...stdoutLines.map((line) => `stdout: ${line}`)
546645
546681
  ];
546646
- if (lines.length === 0)
546682
+ if (tagged.length === 0)
546647
546683
  return "Vision ML script failed";
546648
- return lines.slice(-24).join("\n").slice(-1800);
546684
+ const exceptionRe = /(?:^|\s)(?:[A-Za-z_][\w.]*Error|[A-Za-z_][\w.]*Exception|AssertionError|KeyboardInterrupt|SystemExit):\s+.+$/;
546685
+ const tracebackIdx = tagged.findIndex((line) => /Traceback \(most recent call last\):/.test(line));
546686
+ const exceptionLine = [...stderrLines, ...stdoutLines].slice().reverse().find((line) => exceptionRe.test(line) && !/\bwarning:/i.test(line));
546687
+ const parts = [];
546688
+ if (exceptionLine) {
546689
+ parts.push(`Root cause: ${exceptionLine}`);
546690
+ }
546691
+ if (tracebackIdx >= 0) {
546692
+ parts.push("Traceback:");
546693
+ parts.push(...tagged.slice(tracebackIdx).slice(0, 18));
546694
+ }
546695
+ const tail = tagged.slice(-24);
546696
+ for (const line of tail) {
546697
+ if (!parts.includes(line))
546698
+ parts.push(line);
546699
+ }
546700
+ return parts.join("\n").slice(0, 2200);
546649
546701
  }
546650
546702
  var VMEM_DIR, VENV_DIR2, VENV_PY, VENV_PIP2, VISUAL_MEMORY_ACTIONS, VisualMemoryTool;
546651
546703
  var init_visual_memory = __esm({
546652
546704
  "packages/execution/dist/tools/visual-memory.js"() {
546653
546705
  "use strict";
546654
546706
  init_cuda_device_filter();
546707
+ init_clip_feature_python();
546655
546708
  VMEM_DIR = join80(homedir20(), ".omnius", "visual-memory");
546656
546709
  VENV_DIR2 = join80(homedir20(), ".omnius", "vision-ml-venv");
546657
546710
  VENV_PY = join80(VENV_DIR2, "bin", "python3");
@@ -546994,6 +547047,8 @@ import torch
546994
547047
  from PIL import Image
546995
547048
  from transformers import CLIPProcessor, CLIPModel
546996
547049
 
547050
+ ${CLIP_FEATURE_HELPERS_PY}
547051
+
546997
547052
  image_path = ${pyLiteral(image)}
546998
547053
  label = ${pyLiteral(label)}
546999
547054
  aliases = ${JSON.stringify(aliases)}
@@ -547007,16 +547062,14 @@ processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
547007
547062
  img = Image.open(image_path).convert("RGB")
547008
547063
  inputs = processor(images=img, return_tensors="pt")
547009
547064
  with torch.no_grad():
547010
- image_features = model.get_image_features(**inputs)
547011
- image_features = image_features / image_features.norm(dim=-1, keepdim=True)
547065
+ image_features = _omnius_normalized_features(model.get_image_features(**inputs))
547012
547066
 
547013
547067
  embedding = image_features[0].cpu().numpy().tolist()
547014
547068
 
547015
547069
  # Also embed the text label and aliases for cross-modal retrieval
547016
547070
  text_inputs = processor(text=aliases, return_tensors="pt", padding=True)
547017
547071
  with torch.no_grad():
547018
- text_features = model.get_text_features(**text_inputs)
547019
- text_features = text_features / text_features.norm(dim=-1, keepdim=True)
547072
+ text_features = _omnius_normalized_features(model.get_text_features(**text_inputs))
547020
547073
 
547021
547074
  text_embeddings = {}
547022
547075
  for i, alias in enumerate(aliases):
@@ -547089,6 +547142,8 @@ import torch
547089
547142
  from PIL import Image
547090
547143
  from transformers import CLIPProcessor, CLIPModel
547091
547144
 
547145
+ ${CLIP_FEATURE_HELPERS_PY}
547146
+
547092
547147
  model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
547093
547148
  processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
547094
547149
 
@@ -547097,8 +547152,7 @@ img = Image.open(${pyLiteral(image)}).convert("RGB")
547097
547152
  # Get image embedding
547098
547153
  inputs = processor(images=img, return_tensors="pt")
547099
547154
  with torch.no_grad():
547100
- image_features = model.get_image_features(**inputs)
547101
- image_features = image_features / image_features.norm(dim=-1, keepdim=True)
547155
+ image_features = _omnius_normalized_features(model.get_image_features(**inputs))
547102
547156
 
547103
547157
  query = image_features[0].cpu().numpy()
547104
547158
 
@@ -547160,8 +547214,7 @@ extra_labels = ${JSON.stringify(extraLabels)}
547160
547214
  if extra_labels:
547161
547215
  text_inputs = processor(text=extra_labels, return_tensors="pt", padding=True)
547162
547216
  with torch.no_grad():
547163
- text_features = model.get_text_features(**text_inputs)
547164
- text_features = text_features / text_features.norm(dim=-1, keepdim=True)
547217
+ text_features = _omnius_normalized_features(model.get_text_features(**text_inputs))
547165
547218
 
547166
547219
  for i, label in enumerate(extra_labels):
547167
547220
  sim = float(np.dot(query, text_features[i].cpu().numpy()))
@@ -547320,6 +547373,7 @@ var MM_DIR, MM_INDEX, MultimodalMemoryTool;
547320
547373
  var init_multimodal_memory = __esm({
547321
547374
  "packages/execution/dist/tools/multimodal-memory.js"() {
547322
547375
  "use strict";
547376
+ init_clip_feature_python();
547323
547377
  MM_DIR = join81(homedir21(), ".omnius", "multimodal-episodes");
547324
547378
  MM_INDEX = join81(MM_DIR, "index.json");
547325
547379
  MultimodalMemoryTool = class {
@@ -547413,13 +547467,13 @@ var init_multimodal_memory = __esm({
547413
547467
  import json, torch
547414
547468
  from PIL import Image
547415
547469
  from transformers import CLIPProcessor, CLIPModel
547470
+ ${CLIP_FEATURE_HELPERS_PY}
547416
547471
  model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
547417
547472
  processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
547418
547473
  img = Image.open("${imagePath}").convert("RGB")
547419
547474
  inputs = processor(images=img, return_tensors="pt")
547420
547475
  with torch.no_grad():
547421
- features = model.get_image_features(**inputs)
547422
- features = features / features.norm(dim=-1, keepdim=True)
547476
+ features = _omnius_normalized_features(model.get_image_features(**inputs))
547423
547477
  print(json.dumps(features[0].cpu().numpy().tolist()))
547424
547478
  `;
547425
547479
  const scriptFile = join81(tmpdir17(), `mm-clip-${Date.now()}.py`);
@@ -547639,12 +547693,12 @@ Recall later: multimodal_memory action=recall query="${personName}"`,
547639
547693
  const clipTextScript = `
547640
547694
  import json, torch
547641
547695
  from transformers import CLIPProcessor, CLIPModel
547696
+ ${CLIP_FEATURE_HELPERS_PY}
547642
547697
  model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
547643
547698
  processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
547644
547699
  inputs = processor(text=["${query.replace(/"/g, '\\"').replace(/\n/g, " ")}"], return_tensors="pt", padding=True)
547645
547700
  with torch.no_grad():
547646
- features = model.get_text_features(**inputs)
547647
- features = features / features.norm(dim=-1, keepdim=True)
547701
+ features = _omnius_normalized_features(model.get_text_features(**inputs))
547648
547702
  print(json.dumps(features[0].cpu().numpy().tolist()))
547649
547703
  `;
547650
547704
  const scriptFile = join81(tmpdir17(), `mm-clipq-${Date.now()}.py`);
@@ -567219,7 +567273,6 @@ function normalizeFailurePatterns(patterns) {
567219
567273
  });
567220
567274
  }
567221
567275
  function buildFailureModeHandoff(input) {
567222
- const patterns = normalizeFailurePatterns(input.errorPatterns).slice(0, input.maxPatterns ?? 10);
567223
567276
  const toolCalls = input.toolCallLog ?? [];
567224
567277
  const maxRecentCalls = input.maxRecentCalls ?? 8;
567225
567278
  const recentCalls = maxRecentCalls > 0 ? toolCalls.slice(-maxRecentCalls) : [];
@@ -567231,6 +567284,7 @@ function buildFailureModeHandoff(input) {
567231
567284
  const currentStep = cleanInline(input.taskState?.currentStep, 180);
567232
567285
  const nextAction = cleanInline(input.taskState?.nextAction, 180);
567233
567286
  const goal = cleanInline(input.taskGoal || input.taskState?.goal || input.taskState?.originalGoal || "", 260);
567287
+ const patterns = normalizeFailurePatterns(input.errorPatterns).slice(0, input.maxPatterns ?? 10);
567234
567288
  if (patterns.length === 0 && recentCalls.length === 0 && modified.length === 0 && failedApproaches.length === 0 && !goal) {
567235
567289
  return null;
567236
567290
  }
@@ -568200,6 +568254,10 @@ function resolutionSystemPrompt() {
568200
568254
  " - exit code 0 on an unrelated command does not resolve the request.",
568201
568255
  " - Doing PART of the request, or adjacent work, is NOT resolution.",
568202
568256
  " - If the request had multiple parts, EVERY part must be addressed.",
568257
+ " - If the original request explicitly allows a degraded fallback such as",
568258
+ " documenting/reporting a tool failure, that fallback can resolve the task",
568259
+ " only when the failure is clearly disclosed and the requested fallback",
568260
+ " artifact/report/verifier is evidenced.",
568203
568261
  "",
568204
568262
  "Respond with ONLY a JSON object, no prose, no code fences:",
568205
568263
  '{"resolved": true|false,',
@@ -568211,6 +568269,56 @@ function resolutionSystemPrompt() {
568211
568269
  "original request. When in doubt, resolved=false and name what is missing."
568212
568270
  ].join("\n");
568213
568271
  }
568272
+ function buildDegradedCompletionPrompt(i2) {
568273
+ return [
568274
+ "Decide whether this task_complete claim should be accepted as an explicitly permitted degraded/failure-report completion.",
568275
+ "",
568276
+ "Accept only if all three are true:",
568277
+ "1. The original request explicitly permits a fallback where a failed/blocked tool or subtask is documented/reported instead of fully fixed.",
568278
+ "2. The completion claim clearly discloses the failed/blocked/degraded part.",
568279
+ "3. The actions/evidence show the requested fallback artifact/report/verification was actually produced.",
568280
+ "",
568281
+ "Reject if the request merely asks for the main work to be fixed, or if the failure disclosure/fallback evidence is missing.",
568282
+ "",
568283
+ "ORIGINAL REQUEST:",
568284
+ i2.originalGoal.slice(0, 2e3) || "(empty)",
568285
+ "",
568286
+ "ACTIONS:",
568287
+ i2.actionsDigest.slice(0, 3e3) || "(none recorded)",
568288
+ "",
568289
+ "EVIDENCE:",
568290
+ i2.evidenceDigest.slice(0, 2e3) || "(none recorded)",
568291
+ "",
568292
+ "COMPLETION CLAIM:",
568293
+ i2.proposedSummary.slice(0, 1500) || "(empty)",
568294
+ "",
568295
+ 'Return only JSON: {"accepted":true|false,"confidence":0.0-1.0,"reason":"one sentence"}'
568296
+ ].join("\n");
568297
+ }
568298
+ function parseDegradedCompletionVerdict(raw) {
568299
+ if (!raw)
568300
+ return null;
568301
+ const text2 = raw.trim().replace(/^```(?:json)?/i, "").replace(/```$/i, "").trim();
568302
+ const start2 = text2.indexOf("{");
568303
+ const end = text2.lastIndexOf("}");
568304
+ if (start2 < 0 || end <= start2)
568305
+ return null;
568306
+ let obj;
568307
+ try {
568308
+ obj = JSON.parse(text2.slice(start2, end + 1));
568309
+ } catch {
568310
+ return null;
568311
+ }
568312
+ let confidence2 = Number(obj["confidence"]);
568313
+ if (!Number.isFinite(confidence2))
568314
+ confidence2 = 0.5;
568315
+ confidence2 = Math.min(1, Math.max(0, confidence2));
568316
+ return {
568317
+ accepted: obj["accepted"] === true,
568318
+ confidence: confidence2,
568319
+ reason: String(obj["reason"] ?? "").slice(0, 500)
568320
+ };
568321
+ }
568214
568322
  function buildResolutionPrompt(i2) {
568215
568323
  return [
568216
568324
  "ORIGINAL REQUEST (what the user actually asked for):",
@@ -568261,6 +568369,134 @@ var init_completion_resolution_verifier = __esm({
568261
568369
  }
568262
568370
  });
568263
568371
 
568372
+ // packages/orchestrator/dist/todoTruth.js
568373
+ function normalizeText2(value2) {
568374
+ return value2.toLowerCase().replace(/[_-]+/g, " ").replace(/[^a-z0-9./ ]+/g, " ").replace(/\s+/g, " ").trim();
568375
+ }
568376
+ function extractArg(argsKey, key) {
568377
+ if (!argsKey)
568378
+ return "";
568379
+ const re = new RegExp(`(?:^|,)${key}=([^,]+)`);
568380
+ return argsKey.match(re)?.[1]?.trim() ?? "";
568381
+ }
568382
+ function toolAliases(toolName) {
568383
+ const normalized = normalizeText2(toolName);
568384
+ const spaceAlias = normalizeText2(toolName.replace(/_/g, " "));
568385
+ const compactAlias = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "");
568386
+ return [...new Set([normalized, spaceAlias, compactAlias].filter(Boolean))];
568387
+ }
568388
+ function todoMentionsFailedCall(todo, call) {
568389
+ if (NON_WORK_TOOLS.has(call.name))
568390
+ return false;
568391
+ const content = normalizeText2(todo.content);
568392
+ const compactContent2 = todo.content.toLowerCase().replace(/[^a-z0-9]+/g, "");
568393
+ const mentionsTool = toolAliases(call.name).some((alias) => {
568394
+ if (alias.length <= 2)
568395
+ return false;
568396
+ if (/^[a-z0-9]+$/.test(alias) && alias === call.name.toLowerCase().replace(/[^a-z0-9]+/g, "")) {
568397
+ return compactContent2.includes(alias);
568398
+ }
568399
+ return content.includes(alias);
568400
+ });
568401
+ if (!mentionsTool)
568402
+ return false;
568403
+ const action = normalizeText2(extractArg(call.argsKey, "action"));
568404
+ if (!action)
568405
+ return true;
568406
+ return content.includes(action) || action.length <= 2;
568407
+ }
568408
+ function evidenceLine(call) {
568409
+ const preview = String(call.outputPreview ?? "").split("\n").map((line) => line.trim()).find(Boolean);
568410
+ const action = extractArg(call.argsKey, "action");
568411
+ const detail = preview || "tool returned failure";
568412
+ return `${call.name}${action ? ` ${action}` : ""}: ${detail}`.slice(0, 260);
568413
+ }
568414
+ function hasLaterSuccessForSameFamily(calls, failedIndex) {
568415
+ const failed = calls[failedIndex];
568416
+ if (!failed)
568417
+ return false;
568418
+ const failedAction = normalizeText2(extractArg(failed.argsKey, "action"));
568419
+ for (let i2 = failedIndex + 1; i2 < calls.length; i2++) {
568420
+ const next = calls[i2];
568421
+ if (!next || next.success !== true || next.name !== failed.name)
568422
+ continue;
568423
+ const nextAction = normalizeText2(extractArg(next.argsKey, "action"));
568424
+ if (!failedAction || !nextAction || failedAction === nextAction)
568425
+ return true;
568426
+ }
568427
+ return false;
568428
+ }
568429
+ function firstUnresolvedChild(todo, childrenByParent) {
568430
+ const id = todo.id;
568431
+ if (!id)
568432
+ return null;
568433
+ const children2 = childrenByParent.get(id) ?? [];
568434
+ return children2.find((child) => child.status === "blocked") ?? children2.find((child) => child.status === "in_progress") ?? children2.find((child) => child.status === "pending") ?? null;
568435
+ }
568436
+ function reconcileCompletedTodosWithEvidence(input) {
568437
+ const nextTodos = input.todos.map((todo) => ({ ...todo }));
568438
+ const downgrades = [];
568439
+ const childrenByParent = /* @__PURE__ */ new Map();
568440
+ for (const todo of nextTodos) {
568441
+ if (!todo.parentId)
568442
+ continue;
568443
+ const arr = childrenByParent.get(todo.parentId) ?? [];
568444
+ arr.push(todo);
568445
+ childrenByParent.set(todo.parentId, arr);
568446
+ }
568447
+ for (const todo of nextTodos) {
568448
+ if (todo.status !== "completed")
568449
+ continue;
568450
+ const unresolvedChild = firstUnresolvedChild(todo, childrenByParent);
568451
+ if (unresolvedChild) {
568452
+ const childStatus = unresolvedChild.status;
568453
+ const targetStatus = childStatus === "blocked" ? "blocked" : "in_progress";
568454
+ const blocker2 = childStatus === "blocked" ? `Child todo blocked: ${unresolvedChild.content}${unresolvedChild.blocker ? ` (${unresolvedChild.blocker})` : ""}` : `Child todo not complete: [${childStatus}] ${unresolvedChild.content}`;
568455
+ downgrades.push({
568456
+ id: todo.id,
568457
+ content: todo.content,
568458
+ from: "completed",
568459
+ to: targetStatus,
568460
+ blocker: blocker2,
568461
+ evidence: blocker2
568462
+ });
568463
+ todo.status = targetStatus;
568464
+ todo.blocker = blocker2;
568465
+ continue;
568466
+ }
568467
+ const startTurn = todo.id && input.todoStartTurnById?.has(todo.id) ? input.todoStartTurnById.get(todo.id) : 0;
568468
+ const relevantCalls = input.toolCallLog.filter((call) => (call.turn ?? 0) >= startTurn);
568469
+ const failedIndex = relevantCalls.findIndex((call) => call.success === false && todoMentionsFailedCall(todo, call) && !hasLaterSuccessForSameFamily(relevantCalls, relevantCalls.indexOf(call)));
568470
+ if (failedIndex < 0)
568471
+ continue;
568472
+ const failed = relevantCalls[failedIndex];
568473
+ const evidence = evidenceLine(failed);
568474
+ const blocker = `Completion claim contradicted by failed ${failed.name} evidence. Reconfigure this subtask: verify whether the goal is already satisfied, choose a different target/tool, or leave the todo blocked with the root cause.`;
568475
+ downgrades.push({
568476
+ id: todo.id,
568477
+ content: todo.content,
568478
+ from: "completed",
568479
+ to: "blocked",
568480
+ blocker,
568481
+ evidence
568482
+ });
568483
+ todo.status = "blocked";
568484
+ todo.blocker = `${blocker} Evidence: ${evidence}`;
568485
+ }
568486
+ return {
568487
+ todos: nextTodos,
568488
+ changed: downgrades.length > 0,
568489
+ downgrades
568490
+ };
568491
+ }
568492
+ var NON_WORK_TOOLS;
568493
+ var init_todoTruth = __esm({
568494
+ "packages/orchestrator/dist/todoTruth.js"() {
568495
+ "use strict";
568496
+ NON_WORK_TOOLS = /* @__PURE__ */ new Set(["todo_write", "todo_read", "task_complete"]);
568497
+ }
568498
+ });
568499
+
568264
568500
  // packages/orchestrator/dist/evidenceBranch.js
568265
568501
  function buildStructuralPreview2(lines, path12, query) {
568266
568502
  const n2 = lines.length;
@@ -570467,6 +570703,7 @@ var init_agenticRunner = __esm({
570467
570703
  init_evidenceLedger();
570468
570704
  init_adversaryStream();
570469
570705
  init_completion_resolution_verifier();
570706
+ init_todoTruth();
570470
570707
  init_evidenceBranch();
570471
570708
  init_resolution_memory();
570472
570709
  init_contextEngine();
@@ -570642,6 +570879,7 @@ var init_agenticRunner = __esm({
570642
570879
  // Research: Kumaran et al. (2016) — complementary learning systems
570643
570880
  // Fast learning from errors → immediate behavioral change
570644
570881
  _errorPatterns = /* @__PURE__ */ new Map();
570882
+ _taskRelevantErrorPatterns = /* @__PURE__ */ new Map();
570645
570883
  _errorGuidanceInjected = /* @__PURE__ */ new Set();
570646
570884
  // prevent duplicate injection per turn
570647
570885
  // REG-26 (Patch C): Reflexion-style structured failure memory. Indexed by
@@ -571219,6 +571457,145 @@ ${parts.join("\n")}
571219
571457
  writesUserTaskArtifacts() {
571220
571458
  return this.options.artifactMode === "user-task" && !this.options.subAgent;
571221
571459
  }
571460
+ _backendModelLabel(backend = this.backend) {
571461
+ const b = backend;
571462
+ const direct = b["model"] ?? b["resolvedModel"] ?? b["modelName"];
571463
+ if (typeof direct === "string" && direct.trim())
571464
+ return direct.trim();
571465
+ const nested = b["config"];
571466
+ if (nested && typeof nested === "object") {
571467
+ const model = nested["model"];
571468
+ if (typeof model === "string" && model.trim())
571469
+ return model.trim();
571470
+ }
571471
+ return "unknown";
571472
+ }
571473
+ _emitModelResolutionTelemetry(purpose, turn) {
571474
+ try {
571475
+ const backendName = this.backend.constructor?.name ?? "unknown";
571476
+ const resolved = this._backendModelLabel(this.backend);
571477
+ this.emit({
571478
+ type: "status",
571479
+ content: `Model resolution: purpose=${purpose} resolved=${resolved} backend=${backendName}`,
571480
+ turn,
571481
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
571482
+ });
571483
+ } catch {
571484
+ }
571485
+ }
571486
+ _cosineSimilarity(a2, b) {
571487
+ if (!a2 || !b || a2.length !== b.length || a2.length === 0)
571488
+ return 0;
571489
+ let dot = 0;
571490
+ let na = 0;
571491
+ let nb = 0;
571492
+ for (let i2 = 0; i2 < a2.length; i2++) {
571493
+ const av = a2[i2] ?? 0;
571494
+ const bv = b[i2] ?? 0;
571495
+ dot += av * bv;
571496
+ na += av * av;
571497
+ nb += bv * bv;
571498
+ }
571499
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
571500
+ return denom > 0 ? dot / denom : 0;
571501
+ }
571502
+ _embeddingBaseUrl() {
571503
+ const raw = this.backend["baseUrl"] || "http://localhost:11434";
571504
+ return raw.replace(/\/v1\/?$/, "");
571505
+ }
571506
+ _failurePatternText(pattern) {
571507
+ return [
571508
+ `signature: ${pattern.signature}`,
571509
+ pattern.tool ? `tool: ${pattern.tool}` : "",
571510
+ pattern.errorType ? `errorType: ${pattern.errorType}` : "",
571511
+ pattern.guidance ? `guidance: ${pattern.guidance}` : ""
571512
+ ].filter(Boolean).join("\n");
571513
+ }
571514
+ async _inferRelevantFailurePatternSignatures(taskGoal, candidates, maxPatterns) {
571515
+ if (candidates.length === 0)
571516
+ return /* @__PURE__ */ new Set();
571517
+ try {
571518
+ this._emitModelResolutionTelemetry("failure_pattern_relevance");
571519
+ const backend = this._auxInferenceBackend();
571520
+ const resp = await backend.chatCompletion({
571521
+ messages: [
571522
+ {
571523
+ role: "system",
571524
+ content: "You select prior failure patterns that are semantically relevant to the active task. Return only JSON."
571525
+ },
571526
+ {
571527
+ role: "user",
571528
+ content: JSON.stringify({
571529
+ taskGoal,
571530
+ max: maxPatterns,
571531
+ candidates: candidates.slice(0, 30).map((pattern) => ({
571532
+ signature: pattern.signature,
571533
+ tool: pattern.tool,
571534
+ errorType: pattern.errorType,
571535
+ guidance: pattern.guidance
571536
+ })),
571537
+ outputSchema: { relevant: ["signature"] }
571538
+ })
571539
+ }
571540
+ ],
571541
+ tools: [],
571542
+ temperature: 0,
571543
+ maxTokens: 500,
571544
+ timeoutMs: 2e4
571545
+ });
571546
+ const raw = resp.choices?.[0]?.message?.content ?? "";
571547
+ const start2 = raw.indexOf("{");
571548
+ const end = raw.lastIndexOf("}");
571549
+ if (start2 < 0 || end <= start2)
571550
+ return /* @__PURE__ */ new Set();
571551
+ const parsed = JSON.parse(raw.slice(start2, end + 1));
571552
+ return new Set((parsed.relevant ?? []).map((item) => String(item)).filter((signature) => candidates.some((pattern) => pattern.signature === signature)).slice(0, maxPatterns));
571553
+ } catch {
571554
+ return /* @__PURE__ */ new Set();
571555
+ }
571556
+ }
571557
+ async _selectTaskRelevantErrorPatterns(taskGoal, maxPatterns) {
571558
+ const candidates = normalizeFailurePatterns(this._errorPatterns).slice(0, 40);
571559
+ if (!taskGoal.trim() || candidates.length === 0)
571560
+ return /* @__PURE__ */ new Map();
571561
+ const selected = /* @__PURE__ */ new Map();
571562
+ let selectedBy = "none";
571563
+ try {
571564
+ const embeddings = await generateEmbeddingBatch([taskGoal, ...candidates.map((pattern) => this._failurePatternText(pattern))], { baseUrl: this._embeddingBaseUrl(), timeoutMs: 12e3 });
571565
+ const query = embeddings[0]?.vector;
571566
+ const minScore = Number.parseFloat(process.env["OMNIUS_FAILURE_PATTERN_SIM_MIN"] ?? "0.58");
571567
+ if (query) {
571568
+ const scored = candidates.map((pattern, index) => ({
571569
+ pattern,
571570
+ score: embeddings[index + 1]?.vector ? this._cosineSimilarity(query, embeddings[index + 1].vector) : 0
571571
+ })).filter((item) => item.score >= minScore).sort((a2, b) => b.score - a2.score).slice(0, maxPatterns);
571572
+ for (const item of scored) {
571573
+ const raw = this._errorPatterns.get(item.pattern.signature);
571574
+ if (raw)
571575
+ selected.set(item.pattern.signature, raw);
571576
+ }
571577
+ if (selected.size > 0)
571578
+ selectedBy = "vector";
571579
+ }
571580
+ } catch {
571581
+ }
571582
+ if (selected.size === 0) {
571583
+ const relevant = await this._inferRelevantFailurePatternSignatures(taskGoal, candidates, maxPatterns);
571584
+ for (const signature of relevant) {
571585
+ const raw = this._errorPatterns.get(signature);
571586
+ if (raw)
571587
+ selected.set(signature, raw);
571588
+ }
571589
+ if (selected.size > 0)
571590
+ selectedBy = "inference";
571591
+ }
571592
+ this.emit({
571593
+ type: "status",
571594
+ content: `Failure handoff semantic selection: ${selected.size}/${candidates.length} persisted pattern(s) selected by ${selectedBy}`,
571595
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
571596
+ });
571597
+ return selected;
571598
+ }
571222
571599
  _persistCompletionContract(contract) {
571223
571600
  if (!this.writesUserTaskArtifacts())
571224
571601
  return;
@@ -572584,7 +572961,7 @@ ${context2 ?? ""}`;
572584
572961
  `Task affect: uncertainty=${affect.uncertainty.toFixed(2)} frustration=${affect.frustration.toFixed(2)} confidence=${affect.confidence.toFixed(2)} momentum=${affect.momentum.toFixed(2)}`
572585
572962
  ].join("\n");
572586
572963
  }
572587
- _buildPreflightTaskMemoryRecall(taskGoal) {
572964
+ async _buildPreflightTaskMemoryRecall(taskGoal) {
572588
572965
  if (process.env["OMNIUS_DISABLE_PREFLIGHT_MEMORY_RECALL"] === "1")
572589
572966
  return "";
572590
572967
  if (this.options.stateDir || this.options.subAgent)
@@ -572593,17 +572970,31 @@ ${context2 ?? ""}`;
572593
572970
  return "";
572594
572971
  try {
572595
572972
  const query = taskGoal.slice(0, 1e3);
572596
- const results = this._episodeStore.search({ query, limit: 6 });
572597
- const useful = results.filter((entry) => typeof entry.content === "string" && entry.content.trim().length >= 30).slice(0, 4);
572973
+ const embedding = await generateEmbedding(query, {
572974
+ baseUrl: this._embeddingBaseUrl(),
572975
+ timeoutMs: 1e4
572976
+ });
572977
+ if (!embedding?.vector)
572978
+ return "";
572979
+ const results = this._episodeStore.search({ query, limit: 12 }, {
572980
+ queryEmbedding: embedding.vector,
572981
+ lexicalWeight: 0,
572982
+ embeddingWeight: 1
572983
+ });
572984
+ const minScore = Number.parseFloat(process.env["OMNIUS_PREFLIGHT_MEMORY_SIM_MIN"] ?? "0.58");
572985
+ const useful = results.map((entry) => ({
572986
+ entry,
572987
+ score: entry.embedding ? this._cosineSimilarity(embedding.vector, entry.embedding) : 0
572988
+ })).filter(({ entry, score }) => typeof entry.content === "string" && entry.content.trim().length >= 30 && score >= minScore).sort((a2, b) => b.score - a2.score).slice(0, 3);
572598
572989
  if (useful.length === 0)
572599
572990
  return "";
572600
- const lines = useful.map((entry, index) => {
572601
- const tool = typeof entry.metadata?.["toolName"] === "string" ? ` tool=${entry.metadata["toolName"]}` : "";
572602
- return `${index + 1}. ${entry.content.replace(/\s+/g, " ").slice(0, 260)}${tool}`;
572991
+ const lines = useful.map(({ entry, score }, index) => {
572992
+ const tool = typeof entry.toolName === "string" && entry.toolName ? ` tool=${entry.toolName}` : "";
572993
+ return `${index + 1}. sim=${score.toFixed(3)} ${entry.content.replace(/\s+/g, " ").slice(0, 260)}${tool}`;
572603
572994
  });
572604
572995
  return [
572605
572996
  `[PREFLIGHT MEMORY RECALL]`,
572606
- `Retrieved task-relevant prior episodes before the first action. Use these as hypotheses, not truth; verify against current files/UI.`,
572997
+ `Retrieved vector-similar prior episodes before the first action. Use these as hypotheses, not truth; verify against current files/UI.`,
572607
572998
  ...lines,
572608
572999
  `If the current task resembles one of these, prefer the remembered working verification path and avoid the remembered failure pattern.`
572609
573000
  ].join("\n");
@@ -572647,6 +573038,43 @@ ${input.answerText ?? ""}`.toLowerCase().trim();
572647
573038
  ].join("\n");
572648
573039
  return { proceed: false, feedback, reason };
572649
573040
  }
573041
+ async _inferExplicitDegradedCompletion(input) {
573042
+ try {
573043
+ this._emitModelResolutionTelemetry("explicit_degraded_completion", input.turn);
573044
+ const backend = this._auxInferenceBackend();
573045
+ const resp = await backend.chatCompletion({
573046
+ messages: [
573047
+ {
573048
+ role: "system",
573049
+ content: "You are a strict completion-contract classifier. Return only JSON."
573050
+ },
573051
+ {
573052
+ role: "user",
573053
+ content: buildDegradedCompletionPrompt({
573054
+ originalGoal: input.originalGoal,
573055
+ actionsDigest: input.actionsDigest,
573056
+ evidenceDigest: input.evidenceDigest,
573057
+ proposedSummary: input.proposedSummary
573058
+ })
573059
+ }
573060
+ ],
573061
+ tools: [],
573062
+ temperature: 0,
573063
+ maxTokens: 400,
573064
+ timeoutMs: 2e4
573065
+ });
573066
+ const verdict = parseDegradedCompletionVerdict(resp.choices?.[0]?.message?.content ?? "");
573067
+ if (verdict?.accepted && verdict.confidence >= 0.6) {
573068
+ return {
573069
+ accepted: true,
573070
+ reason: verdict.reason || "inference classified this as an explicitly permitted degraded completion"
573071
+ };
573072
+ }
573073
+ return null;
573074
+ } catch {
573075
+ return null;
573076
+ }
573077
+ }
572650
573078
  /**
572651
573079
  * REG-47: post-implementation backward-pass review.
572652
573080
  *
@@ -572716,9 +573144,27 @@ ${shellLines.join("\n")}` : "Commands run: none"
572716
573144
  const failCount = toolCallLog.filter((e2) => e2.success === false).length;
572717
573145
  evidenceParts.push(`Failed tool calls this run: ${failCount}`);
572718
573146
  const evidenceDigest = evidenceParts.join("\n");
573147
+ const degraded = failCount > 0 ? await this._inferExplicitDegradedCompletion({
573148
+ turn,
573149
+ originalGoal,
573150
+ actionsDigest,
573151
+ evidenceDigest,
573152
+ proposedSummary
573153
+ }) : null;
573154
+ if (degraded) {
573155
+ this._resolutionGateRejections = 0;
573156
+ this.emit({
573157
+ type: "status",
573158
+ content: `Resolution gate accepted explicit degraded completion: ${degraded.reason}`,
573159
+ turn,
573160
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
573161
+ });
573162
+ return { proceed: true };
573163
+ }
572719
573164
  let verdict = null;
572720
573165
  try {
572721
573166
  const backend = this._auxInferenceBackend();
573167
+ this._emitModelResolutionTelemetry("completion_resolution", turn);
572722
573168
  for (let attempt = 0; attempt < 2 && !verdict; attempt++) {
572723
573169
  const resp = await backend.chatCompletion({
572724
573170
  messages: [
@@ -576151,6 +576597,7 @@ Respond with your assessment, then take action.`;
576151
576597
  this.pendingUserMessages.length = 0;
576152
576598
  const persistentTaskGoal = cleanForStorage(actualUserGoal || "") || cleanedTask;
576153
576599
  const userGoal = persistentTaskGoal.slice(0, 500);
576600
+ this._taskRelevantErrorPatterns = process.env["OMNIUS_DISABLE_FAILURE_HANDOFF"] === "1" ? /* @__PURE__ */ new Map() : await this._selectTaskRelevantErrorPatterns(persistentTaskGoal, 10);
576154
576601
  this._taskState = {
576155
576602
  goal: userGoal,
576156
576603
  originalGoal: userGoal,
@@ -576195,6 +576642,7 @@ Respond with your assessment, then take action.`;
576195
576642
  contextWindowSize: this.options.contextWindowSize ?? 0,
576196
576643
  verbose: false
576197
576644
  });
576645
+ this._emitModelResolutionTelemetry("main");
576198
576646
  this._hookManager.runSessionHook("session_start", this._sessionId);
576199
576647
  if (this.writesUserTaskArtifacts()) {
576200
576648
  this._initializeCompletionContract(task, context2, actualUserGoal);
@@ -576425,7 +576873,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
576425
576873
  ...missionCompletionContract ? [{ role: "system", content: missionCompletionContract }] : [],
576426
576874
  { role: "user", content: userContent }
576427
576875
  ];
576428
- const preflightMemoryRecall = this._buildPreflightTaskMemoryRecall(persistentTaskGoal);
576876
+ const preflightMemoryRecall = await this._buildPreflightTaskMemoryRecall(persistentTaskGoal);
576429
576877
  if (preflightMemoryRecall) {
576430
576878
  messages2.splice(messages2.length - 1, 0, {
576431
576879
  role: "system",
@@ -576545,7 +576993,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
576545
576993
  try {
576546
576994
  const failureHandoff = buildFailureModeHandoff({
576547
576995
  taskGoal: persistentTaskGoal,
576548
- errorPatterns: this._errorPatterns,
576996
+ errorPatterns: this._taskRelevantErrorPatterns,
576549
576997
  toolCallLog,
576550
576998
  taskState: this._taskState,
576551
576999
  maxPatterns: 10,
@@ -580022,13 +580470,15 @@ Respond with EXACTLY this structure before your next tool call:
580022
580470
  default:
580023
580471
  guidance = `This tool failed previously with a similar error. Review the error message carefully and adjust your approach before retrying.`;
580024
580472
  }
580025
- this._errorPatterns.set(sig, {
580473
+ const learnedPattern = {
580026
580474
  count,
580027
580475
  guidance,
580028
580476
  lastSeen: Date.now(),
580029
580477
  tool: tc.name,
580030
580478
  errorType
580031
- });
580479
+ };
580480
+ this._errorPatterns.set(sig, learnedPattern);
580481
+ this._taskRelevantErrorPatterns.set(sig, learnedPattern);
580032
580482
  if (this._failureStore) {
580033
580483
  try {
580034
580484
  this._failureStore.insert({
@@ -580078,13 +580528,48 @@ Respond with EXACTLY this structure before your next tool call:
580078
580528
  }
580079
580529
  if (tc.name === "todo_write") {
580080
580530
  try {
580081
- const _todosNow = this.readSessionTodos() || [];
580531
+ let _todosNow = this.readSessionTodos() || [];
580082
580532
  for (const _tp of _todosNow) {
580083
580533
  const _tpId = _tp.id;
580084
580534
  if (_tp.status === "in_progress" && _tpId && !this._todoInProgressTurn.has(_tpId)) {
580085
580535
  this._todoInProgressTurn.set(_tpId, turn);
580086
580536
  }
580087
580537
  }
580538
+ const truth = reconcileCompletedTodosWithEvidence({
580539
+ todos: _todosNow,
580540
+ toolCallLog,
580541
+ todoStartTurnById: this._todoInProgressTurn
580542
+ });
580543
+ if (truth.changed) {
580544
+ const sid = this._sessionId || process.env["OMNIUS_SESSION_ID"] || "default";
580545
+ writeTodos(sid, truth.todos.map((t2) => ({
580546
+ id: t2.id,
580547
+ content: t2.content,
580548
+ status: t2.status,
580549
+ parentId: t2.parentId,
580550
+ blocker: t2.blocker,
580551
+ verifyCommand: t2.verifyCommand,
580552
+ declaredArtifacts: t2.declaredArtifacts
580553
+ })));
580554
+ _todosNow = this.readSessionTodos() || truth.todos;
580555
+ const downgradeLines = truth.downgrades.slice(0, 6).map((d2) => `- ${d2.content}: ${d2.from} -> ${d2.to}; ${d2.evidence}`).join("\n");
580556
+ messages2.push({
580557
+ role: "system",
580558
+ content: [
580559
+ `[TODO TRUTH RECONCILIATION]`,
580560
+ `One or more completed todo claims contradicted the evidence or nested child status and were rewritten in the active todo list.`,
580561
+ downgradeLines,
580562
+ ``,
580563
+ `Reconfigure the affected subtask tree now: read/verify the current state, choose a different tool or target if the prior route failed, or leave the child todo blocked with the root cause. Do not mark a parent completed until every child is completed with evidence.`
580564
+ ].join("\n")
580565
+ });
580566
+ this.emit({
580567
+ type: "status",
580568
+ content: `Todo truth reconciled ${truth.downgrades.length} completed claim(s) from evidence/nested child state`,
580569
+ turn,
580570
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
580571
+ });
580572
+ }
580088
580573
  if (!this._newFieldNudgeFired) {
580089
580574
  this._todoWritesObservedForNudge++;
580090
580575
  const _anyFieldUsed = _todosNow.some((t2) => typeof t2.verifyCommand === "string" || Array.isArray(t2.declaredArtifacts));
@@ -580824,7 +581309,7 @@ Then use file_read on individual FILES inside it.`);
580824
581309
  if (process.env["OMNIUS_DISABLE_FAILURE_HANDOFF"] !== "1" && !result.success && turn - lastFailureHandoffTurn >= 4) {
580825
581310
  const runtimeHandoff = buildFailureModeHandoff({
580826
581311
  taskGoal: persistentTaskGoal,
580827
- errorPatterns: this._errorPatterns,
581312
+ errorPatterns: this._taskRelevantErrorPatterns,
580828
581313
  toolCallLog,
580829
581314
  taskState: this._taskState,
580830
581315
  maxPatterns: 5,
@@ -584348,7 +584833,7 @@ ${content.slice(0, 8e3)}
584348
584833
  try {
584349
584834
  const compactFailureHandoff = buildFailureModeHandoff({
584350
584835
  taskGoal: this._taskState.goal,
584351
- errorPatterns: this._errorPatterns,
584836
+ errorPatterns: this._taskRelevantErrorPatterns,
584352
584837
  taskState: this._taskState,
584353
584838
  maxPatterns: 6,
584354
584839
  maxRecentCalls: 0
@@ -613124,6 +613609,33 @@ function buildTodoProgressBar(todos, maxWidth) {
613124
613609
  if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET3}`;
613125
613610
  return out;
613126
613611
  }
613612
+ function orderTodosForDisplay(todos) {
613613
+ const byParent = /* @__PURE__ */ new Map();
613614
+ const byId = new Set(todos.map((t2) => t2.id));
613615
+ const roots = [];
613616
+ for (const todo of todos) {
613617
+ if (todo.parentId && byId.has(todo.parentId)) {
613618
+ const arr = byParent.get(todo.parentId) ?? [];
613619
+ arr.push(todo);
613620
+ byParent.set(todo.parentId, arr);
613621
+ } else {
613622
+ roots.push(todo);
613623
+ }
613624
+ }
613625
+ const out = [];
613626
+ const seen = /* @__PURE__ */ new Set();
613627
+ const visit = (todo, depth) => {
613628
+ if (seen.has(todo.id)) return;
613629
+ seen.add(todo.id);
613630
+ out.push({ ...todo, depth: Math.min(depth, 4) });
613631
+ for (const child of byParent.get(todo.id) ?? []) {
613632
+ visit(child, depth + 1);
613633
+ }
613634
+ };
613635
+ for (const root of roots) visit(root, 0);
613636
+ for (const todo of todos) visit(todo, 0);
613637
+ return out;
613638
+ }
613127
613639
  function render() {
613128
613640
  if (!_enabled) return;
613129
613641
  if (!panelEffectivelyVisible()) {
@@ -613149,16 +613661,18 @@ function render() {
613149
613661
  const progressBar = buildTodoProgressBar(_lastTodos, maxBarWidth);
613150
613662
  const headerText = `${headerPrefix}${progressBar}`;
613151
613663
  lines.push(headerText);
613152
- const visible = _lastTodos.slice(0, MAX_VISIBLE_ROWS - 1);
613664
+ const displayTodos = orderTodosForDisplay(_lastTodos);
613665
+ const visible = displayTodos.slice(0, MAX_VISIBLE_ROWS - 1);
613153
613666
  for (const t2 of visible) {
613154
613667
  const { mark, color } = statusToAnsi(t2.status);
613155
613668
  const contentWidth = Math.max(4, cols - 8);
613156
- const contentText = t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
613669
+ const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth - 1)}- ` : "";
613670
+ const contentText = indent2 + t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
613157
613671
  const truncated = truncate2(contentText, contentWidth);
613158
613672
  lines.push(`${color}${mark}${RESET3} ${color}${truncated}${RESET3}`);
613159
613673
  }
613160
- if (_lastTodos.length > visible.length) {
613161
- const more = _lastTodos.length - visible.length;
613674
+ if (displayTodos.length > visible.length) {
613675
+ const more = displayTodos.length - visible.length;
613162
613676
  lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET3}`;
613163
613677
  }
613164
613678
  let out = HIDE + SAVE;
@@ -717470,6 +717984,7 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
717470
717984
  },
717471
717985
  async () => {
717472
717986
  const result = await runner.run(effectiveTask, systemContext);
717987
+ _apiCallbacks?.onRunResult?.(result);
717473
717988
  const tokens = {
717474
717989
  total: result.totalTokens,
717475
717990
  estimated: result.estimatedTokens
@@ -723752,6 +724267,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723752
724267
  }
723753
724268
  }
723754
724269
  _apiCallbacks = callbacks ?? null;
724270
+ const headlessMode = Boolean(callbacks);
723755
724271
  await bootstrapMcpAndPlugins(repoRoot);
723756
724272
  renderCompactHeader(config.model);
723757
724273
  renderUserMessage(task);
@@ -723762,6 +724278,10 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723762
724278
  try {
723763
724279
  const handle2 = startTask(task, config, repoRoot);
723764
724280
  await handle2.promise;
724281
+ if (headlessMode) {
724282
+ _apiCallbacks = null;
724283
+ return;
724284
+ }
723765
724285
  try {
723766
724286
  const ikDir = join174(repoRoot, ".omnius", "identity");
723767
724287
  const ikFile = join174(ikDir, "self-state.json");
@@ -724045,6 +724565,10 @@ Rules:
724045
724565
  } catch {
724046
724566
  }
724047
724567
  } catch (err) {
724568
+ if (headlessMode) {
724569
+ _apiCallbacks = null;
724570
+ throw err;
724571
+ }
724048
724572
  try {
724049
724573
  const ikFile = join174(repoRoot, ".omnius", "identity", "self-state.json");
724050
724574
  if (existsSync161(ikFile)) {
@@ -724271,6 +724795,7 @@ async function runJson(task, config, repoPath2) {
724271
724795
  let result;
724272
724796
  const assistantTexts = [];
724273
724797
  const toolCallLog = [];
724798
+ let runnerResult = null;
724274
724799
  try {
724275
724800
  await runWithTUI(task, config, repoPath2, {
724276
724801
  onAssistantText: (text2) => {
@@ -724294,13 +724819,29 @@ async function runJson(task, config, repoPath2) {
724294
724819
  },
724295
724820
  onStatus: (content) => {
724296
724821
  origWrite(JSON.stringify({ type: "status", content }) + "\n");
724822
+ },
724823
+ onRunResult: (runResult) => {
724824
+ runnerResult = {
724825
+ status: runResult.status,
724826
+ completed: runResult.completed,
724827
+ summary: runResult.summary,
724828
+ turns: runResult.turns,
724829
+ toolCalls: runResult.toolCalls,
724830
+ filesEdited: runResult.filesEdited,
724831
+ testsRun: runResult.testsRun
724832
+ };
724297
724833
  }
724298
724834
  });
724835
+ const rr = runnerResult;
724299
724836
  result = {
724300
- status: "completed",
724301
- summary: extractSummary(captured),
724837
+ status: rr?.completed ? "completed" : rr?.status ?? "completed",
724838
+ summary: rr?.summary || extractSummary(captured),
724839
+ turns: rr?.turns,
724840
+ toolCalls: rr?.toolCalls,
724841
+ filesModified: rr?.filesEdited,
724842
+ testsRun: rr?.testsRun,
724302
724843
  durationMs: Date.now() - startTime,
724303
- exitCode: 0
724844
+ exitCode: rr && !rr.completed ? 2 : 0
724304
724845
  };
724305
724846
  } catch (err) {
724306
724847
  result = {
@@ -724324,7 +724865,14 @@ async function runJson(task, config, repoPath2) {
724324
724865
  result.tool_calls = toolCallLog;
724325
724866
  }
724326
724867
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
724327
- if (result.exitCode !== 0) process.exit(1);
724868
+ if (result.exitCode !== 0) process.exit(result.exitCode);
724869
+ if (shouldForceJsonExit()) process.exit(0);
724870
+ }
724871
+ function shouldForceJsonExit() {
724872
+ if (process.env["OMNIUS_JSON_NO_FORCE_EXIT"] === "1") return false;
724873
+ if (process.env["VITEST"] === "true" || process.env["NODE_ENV"] === "test")
724874
+ return false;
724875
+ return true;
724328
724876
  }
724329
724877
  function extractSummary(captured) {
724330
724878
  const all2 = captured.join("");
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.369",
3
+ "version": "1.0.371",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.369",
9
+ "version": "1.0.371",
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.369",
3
+ "version": "1.0.371",
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",