omnius 1.0.369 → 1.0.370

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,25 @@ function resolutionSystemPrompt() {
568211
568269
  "original request. When in doubt, resolved=false and name what is missing."
568212
568270
  ].join("\n");
568213
568271
  }
568272
+ function detectExplicitDegradedCompletion(i2) {
568273
+ const original = i2.originalGoal.toLowerCase();
568274
+ const summary = i2.proposedSummary.toLowerCase();
568275
+ const evidence = `${i2.actionsDigest}
568276
+ ${i2.evidenceDigest}`.toLowerCase();
568277
+ const permitsFallback = /\bif\b[\s\S]{0,160}\b(?:fails?|failed|failure|blocked|unavailable|cannot|can't|unable)\b[\s\S]{0,180}\b(?:document|report|note|record|summari[sz]e|explain)\b/.test(original) || /\b(?:document|report|note|record|summari[sz]e|explain)\b[\s\S]{0,180}\b(?:fails?|failed|failure|blocked|unavailable|cannot|can't|unable)\b/.test(original) || /\b(?:fallback|degraded|best effort|best-effort|honestly document|document honestly)\b/.test(original);
568278
+ if (!permitsFallback)
568279
+ return null;
568280
+ const disclosesFailure = /\b(?:fail(?:ed|ure)?|blocked|unable|could not|couldn't|cannot|can't|degraded|partial|not available|unavailable)\b/.test(summary);
568281
+ if (!disclosesFailure)
568282
+ return null;
568283
+ const hasFallbackEvidence = /\b(?:passed|success|succeeded|verified|wrote|created|saved|report|artifact|file changed|files changed|last test outcome: passed|exit code 0)\b/.test(evidence) || /\b(?:verifier|verification|report)\b/.test(summary);
568284
+ if (!hasFallbackEvidence)
568285
+ return null;
568286
+ return {
568287
+ accepted: true,
568288
+ reason: "original request explicitly allowed a degraded/failure-report fallback and the completion disclosed the failure with fallback evidence"
568289
+ };
568290
+ }
568214
568291
  function buildResolutionPrompt(i2) {
568215
568292
  return [
568216
568293
  "ORIGINAL REQUEST (what the user actually asked for):",
@@ -568261,6 +568338,134 @@ var init_completion_resolution_verifier = __esm({
568261
568338
  }
568262
568339
  });
568263
568340
 
568341
+ // packages/orchestrator/dist/todoTruth.js
568342
+ function normalizeText2(value2) {
568343
+ return value2.toLowerCase().replace(/[_-]+/g, " ").replace(/[^a-z0-9./ ]+/g, " ").replace(/\s+/g, " ").trim();
568344
+ }
568345
+ function extractArg(argsKey, key) {
568346
+ if (!argsKey)
568347
+ return "";
568348
+ const re = new RegExp(`(?:^|,)${key}=([^,]+)`);
568349
+ return argsKey.match(re)?.[1]?.trim() ?? "";
568350
+ }
568351
+ function toolAliases(toolName) {
568352
+ const normalized = normalizeText2(toolName);
568353
+ const spaceAlias = normalizeText2(toolName.replace(/_/g, " "));
568354
+ const compactAlias = toolName.toLowerCase().replace(/[^a-z0-9]+/g, "");
568355
+ return [...new Set([normalized, spaceAlias, compactAlias].filter(Boolean))];
568356
+ }
568357
+ function todoMentionsFailedCall(todo, call) {
568358
+ if (NON_WORK_TOOLS.has(call.name))
568359
+ return false;
568360
+ const content = normalizeText2(todo.content);
568361
+ const compactContent2 = todo.content.toLowerCase().replace(/[^a-z0-9]+/g, "");
568362
+ const mentionsTool = toolAliases(call.name).some((alias) => {
568363
+ if (alias.length <= 2)
568364
+ return false;
568365
+ if (/^[a-z0-9]+$/.test(alias) && alias === call.name.toLowerCase().replace(/[^a-z0-9]+/g, "")) {
568366
+ return compactContent2.includes(alias);
568367
+ }
568368
+ return content.includes(alias);
568369
+ });
568370
+ if (!mentionsTool)
568371
+ return false;
568372
+ const action = normalizeText2(extractArg(call.argsKey, "action"));
568373
+ if (!action)
568374
+ return true;
568375
+ return content.includes(action) || action.length <= 2;
568376
+ }
568377
+ function evidenceLine(call) {
568378
+ const preview = String(call.outputPreview ?? "").split("\n").map((line) => line.trim()).find(Boolean);
568379
+ const action = extractArg(call.argsKey, "action");
568380
+ const detail = preview || "tool returned failure";
568381
+ return `${call.name}${action ? ` ${action}` : ""}: ${detail}`.slice(0, 260);
568382
+ }
568383
+ function hasLaterSuccessForSameFamily(calls, failedIndex) {
568384
+ const failed = calls[failedIndex];
568385
+ if (!failed)
568386
+ return false;
568387
+ const failedAction = normalizeText2(extractArg(failed.argsKey, "action"));
568388
+ for (let i2 = failedIndex + 1; i2 < calls.length; i2++) {
568389
+ const next = calls[i2];
568390
+ if (!next || next.success !== true || next.name !== failed.name)
568391
+ continue;
568392
+ const nextAction = normalizeText2(extractArg(next.argsKey, "action"));
568393
+ if (!failedAction || !nextAction || failedAction === nextAction)
568394
+ return true;
568395
+ }
568396
+ return false;
568397
+ }
568398
+ function firstUnresolvedChild(todo, childrenByParent) {
568399
+ const id = todo.id;
568400
+ if (!id)
568401
+ return null;
568402
+ const children2 = childrenByParent.get(id) ?? [];
568403
+ return children2.find((child) => child.status === "blocked") ?? children2.find((child) => child.status === "in_progress") ?? children2.find((child) => child.status === "pending") ?? null;
568404
+ }
568405
+ function reconcileCompletedTodosWithEvidence(input) {
568406
+ const nextTodos = input.todos.map((todo) => ({ ...todo }));
568407
+ const downgrades = [];
568408
+ const childrenByParent = /* @__PURE__ */ new Map();
568409
+ for (const todo of nextTodos) {
568410
+ if (!todo.parentId)
568411
+ continue;
568412
+ const arr = childrenByParent.get(todo.parentId) ?? [];
568413
+ arr.push(todo);
568414
+ childrenByParent.set(todo.parentId, arr);
568415
+ }
568416
+ for (const todo of nextTodos) {
568417
+ if (todo.status !== "completed")
568418
+ continue;
568419
+ const unresolvedChild = firstUnresolvedChild(todo, childrenByParent);
568420
+ if (unresolvedChild) {
568421
+ const childStatus = unresolvedChild.status;
568422
+ const targetStatus = childStatus === "blocked" ? "blocked" : "in_progress";
568423
+ const blocker2 = childStatus === "blocked" ? `Child todo blocked: ${unresolvedChild.content}${unresolvedChild.blocker ? ` (${unresolvedChild.blocker})` : ""}` : `Child todo not complete: [${childStatus}] ${unresolvedChild.content}`;
568424
+ downgrades.push({
568425
+ id: todo.id,
568426
+ content: todo.content,
568427
+ from: "completed",
568428
+ to: targetStatus,
568429
+ blocker: blocker2,
568430
+ evidence: blocker2
568431
+ });
568432
+ todo.status = targetStatus;
568433
+ todo.blocker = blocker2;
568434
+ continue;
568435
+ }
568436
+ const startTurn = todo.id && input.todoStartTurnById?.has(todo.id) ? input.todoStartTurnById.get(todo.id) : 0;
568437
+ const relevantCalls = input.toolCallLog.filter((call) => (call.turn ?? 0) >= startTurn);
568438
+ const failedIndex = relevantCalls.findIndex((call) => call.success === false && todoMentionsFailedCall(todo, call) && !hasLaterSuccessForSameFamily(relevantCalls, relevantCalls.indexOf(call)));
568439
+ if (failedIndex < 0)
568440
+ continue;
568441
+ const failed = relevantCalls[failedIndex];
568442
+ const evidence = evidenceLine(failed);
568443
+ 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.`;
568444
+ downgrades.push({
568445
+ id: todo.id,
568446
+ content: todo.content,
568447
+ from: "completed",
568448
+ to: "blocked",
568449
+ blocker,
568450
+ evidence
568451
+ });
568452
+ todo.status = "blocked";
568453
+ todo.blocker = `${blocker} Evidence: ${evidence}`;
568454
+ }
568455
+ return {
568456
+ todos: nextTodos,
568457
+ changed: downgrades.length > 0,
568458
+ downgrades
568459
+ };
568460
+ }
568461
+ var NON_WORK_TOOLS;
568462
+ var init_todoTruth = __esm({
568463
+ "packages/orchestrator/dist/todoTruth.js"() {
568464
+ "use strict";
568465
+ NON_WORK_TOOLS = /* @__PURE__ */ new Set(["todo_write", "todo_read", "task_complete"]);
568466
+ }
568467
+ });
568468
+
568264
568469
  // packages/orchestrator/dist/evidenceBranch.js
568265
568470
  function buildStructuralPreview2(lines, path12, query) {
568266
568471
  const n2 = lines.length;
@@ -570467,6 +570672,7 @@ var init_agenticRunner = __esm({
570467
570672
  init_evidenceLedger();
570468
570673
  init_adversaryStream();
570469
570674
  init_completion_resolution_verifier();
570675
+ init_todoTruth();
570470
570676
  init_evidenceBranch();
570471
570677
  init_resolution_memory();
570472
570678
  init_contextEngine();
@@ -570642,6 +570848,7 @@ var init_agenticRunner = __esm({
570642
570848
  // Research: Kumaran et al. (2016) — complementary learning systems
570643
570849
  // Fast learning from errors → immediate behavioral change
570644
570850
  _errorPatterns = /* @__PURE__ */ new Map();
570851
+ _taskRelevantErrorPatterns = /* @__PURE__ */ new Map();
570645
570852
  _errorGuidanceInjected = /* @__PURE__ */ new Set();
570646
570853
  // prevent duplicate injection per turn
570647
570854
  // REG-26 (Patch C): Reflexion-style structured failure memory. Indexed by
@@ -571219,6 +571426,145 @@ ${parts.join("\n")}
571219
571426
  writesUserTaskArtifacts() {
571220
571427
  return this.options.artifactMode === "user-task" && !this.options.subAgent;
571221
571428
  }
571429
+ _backendModelLabel(backend = this.backend) {
571430
+ const b = backend;
571431
+ const direct = b["model"] ?? b["resolvedModel"] ?? b["modelName"];
571432
+ if (typeof direct === "string" && direct.trim())
571433
+ return direct.trim();
571434
+ const nested = b["config"];
571435
+ if (nested && typeof nested === "object") {
571436
+ const model = nested["model"];
571437
+ if (typeof model === "string" && model.trim())
571438
+ return model.trim();
571439
+ }
571440
+ return "unknown";
571441
+ }
571442
+ _emitModelResolutionTelemetry(purpose, turn) {
571443
+ try {
571444
+ const backendName = this.backend.constructor?.name ?? "unknown";
571445
+ const resolved = this._backendModelLabel(this.backend);
571446
+ this.emit({
571447
+ type: "status",
571448
+ content: `Model resolution: purpose=${purpose} resolved=${resolved} backend=${backendName}`,
571449
+ turn,
571450
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
571451
+ });
571452
+ } catch {
571453
+ }
571454
+ }
571455
+ _cosineSimilarity(a2, b) {
571456
+ if (!a2 || !b || a2.length !== b.length || a2.length === 0)
571457
+ return 0;
571458
+ let dot = 0;
571459
+ let na = 0;
571460
+ let nb = 0;
571461
+ for (let i2 = 0; i2 < a2.length; i2++) {
571462
+ const av = a2[i2] ?? 0;
571463
+ const bv = b[i2] ?? 0;
571464
+ dot += av * bv;
571465
+ na += av * av;
571466
+ nb += bv * bv;
571467
+ }
571468
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
571469
+ return denom > 0 ? dot / denom : 0;
571470
+ }
571471
+ _embeddingBaseUrl() {
571472
+ const raw = this.backend["baseUrl"] || "http://localhost:11434";
571473
+ return raw.replace(/\/v1\/?$/, "");
571474
+ }
571475
+ _failurePatternText(pattern) {
571476
+ return [
571477
+ `signature: ${pattern.signature}`,
571478
+ pattern.tool ? `tool: ${pattern.tool}` : "",
571479
+ pattern.errorType ? `errorType: ${pattern.errorType}` : "",
571480
+ pattern.guidance ? `guidance: ${pattern.guidance}` : ""
571481
+ ].filter(Boolean).join("\n");
571482
+ }
571483
+ async _inferRelevantFailurePatternSignatures(taskGoal, candidates, maxPatterns) {
571484
+ if (candidates.length === 0)
571485
+ return /* @__PURE__ */ new Set();
571486
+ try {
571487
+ this._emitModelResolutionTelemetry("failure_pattern_relevance");
571488
+ const backend = this._auxInferenceBackend();
571489
+ const resp = await backend.chatCompletion({
571490
+ messages: [
571491
+ {
571492
+ role: "system",
571493
+ content: "You select prior failure patterns that are semantically relevant to the active task. Return only JSON."
571494
+ },
571495
+ {
571496
+ role: "user",
571497
+ content: JSON.stringify({
571498
+ taskGoal,
571499
+ max: maxPatterns,
571500
+ candidates: candidates.slice(0, 30).map((pattern) => ({
571501
+ signature: pattern.signature,
571502
+ tool: pattern.tool,
571503
+ errorType: pattern.errorType,
571504
+ guidance: pattern.guidance
571505
+ })),
571506
+ outputSchema: { relevant: ["signature"] }
571507
+ })
571508
+ }
571509
+ ],
571510
+ tools: [],
571511
+ temperature: 0,
571512
+ maxTokens: 500,
571513
+ timeoutMs: 2e4
571514
+ });
571515
+ const raw = resp.choices?.[0]?.message?.content ?? "";
571516
+ const start2 = raw.indexOf("{");
571517
+ const end = raw.lastIndexOf("}");
571518
+ if (start2 < 0 || end <= start2)
571519
+ return /* @__PURE__ */ new Set();
571520
+ const parsed = JSON.parse(raw.slice(start2, end + 1));
571521
+ return new Set((parsed.relevant ?? []).map((item) => String(item)).filter((signature) => candidates.some((pattern) => pattern.signature === signature)).slice(0, maxPatterns));
571522
+ } catch {
571523
+ return /* @__PURE__ */ new Set();
571524
+ }
571525
+ }
571526
+ async _selectTaskRelevantErrorPatterns(taskGoal, maxPatterns) {
571527
+ const candidates = normalizeFailurePatterns(this._errorPatterns).slice(0, 40);
571528
+ if (!taskGoal.trim() || candidates.length === 0)
571529
+ return /* @__PURE__ */ new Map();
571530
+ const selected = /* @__PURE__ */ new Map();
571531
+ let selectedBy = "none";
571532
+ try {
571533
+ const embeddings = await generateEmbeddingBatch([taskGoal, ...candidates.map((pattern) => this._failurePatternText(pattern))], { baseUrl: this._embeddingBaseUrl(), timeoutMs: 12e3 });
571534
+ const query = embeddings[0]?.vector;
571535
+ const minScore = Number.parseFloat(process.env["OMNIUS_FAILURE_PATTERN_SIM_MIN"] ?? "0.58");
571536
+ if (query) {
571537
+ const scored = candidates.map((pattern, index) => ({
571538
+ pattern,
571539
+ score: embeddings[index + 1]?.vector ? this._cosineSimilarity(query, embeddings[index + 1].vector) : 0
571540
+ })).filter((item) => item.score >= minScore).sort((a2, b) => b.score - a2.score).slice(0, maxPatterns);
571541
+ for (const item of scored) {
571542
+ const raw = this._errorPatterns.get(item.pattern.signature);
571543
+ if (raw)
571544
+ selected.set(item.pattern.signature, raw);
571545
+ }
571546
+ if (selected.size > 0)
571547
+ selectedBy = "vector";
571548
+ }
571549
+ } catch {
571550
+ }
571551
+ if (selected.size === 0) {
571552
+ const relevant = await this._inferRelevantFailurePatternSignatures(taskGoal, candidates, maxPatterns);
571553
+ for (const signature of relevant) {
571554
+ const raw = this._errorPatterns.get(signature);
571555
+ if (raw)
571556
+ selected.set(signature, raw);
571557
+ }
571558
+ if (selected.size > 0)
571559
+ selectedBy = "inference";
571560
+ }
571561
+ this.emit({
571562
+ type: "status",
571563
+ content: `Failure handoff semantic selection: ${selected.size}/${candidates.length} persisted pattern(s) selected by ${selectedBy}`,
571564
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
571565
+ });
571566
+ return selected;
571567
+ }
571222
571568
  _persistCompletionContract(contract) {
571223
571569
  if (!this.writesUserTaskArtifacts())
571224
571570
  return;
@@ -572584,7 +572930,7 @@ ${context2 ?? ""}`;
572584
572930
  `Task affect: uncertainty=${affect.uncertainty.toFixed(2)} frustration=${affect.frustration.toFixed(2)} confidence=${affect.confidence.toFixed(2)} momentum=${affect.momentum.toFixed(2)}`
572585
572931
  ].join("\n");
572586
572932
  }
572587
- _buildPreflightTaskMemoryRecall(taskGoal) {
572933
+ async _buildPreflightTaskMemoryRecall(taskGoal) {
572588
572934
  if (process.env["OMNIUS_DISABLE_PREFLIGHT_MEMORY_RECALL"] === "1")
572589
572935
  return "";
572590
572936
  if (this.options.stateDir || this.options.subAgent)
@@ -572593,17 +572939,31 @@ ${context2 ?? ""}`;
572593
572939
  return "";
572594
572940
  try {
572595
572941
  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);
572942
+ const embedding = await generateEmbedding(query, {
572943
+ baseUrl: this._embeddingBaseUrl(),
572944
+ timeoutMs: 1e4
572945
+ });
572946
+ if (!embedding?.vector)
572947
+ return "";
572948
+ const results = this._episodeStore.search({ query, limit: 12 }, {
572949
+ queryEmbedding: embedding.vector,
572950
+ lexicalWeight: 0,
572951
+ embeddingWeight: 1
572952
+ });
572953
+ const minScore = Number.parseFloat(process.env["OMNIUS_PREFLIGHT_MEMORY_SIM_MIN"] ?? "0.58");
572954
+ const useful = results.map((entry) => ({
572955
+ entry,
572956
+ score: entry.embedding ? this._cosineSimilarity(embedding.vector, entry.embedding) : 0
572957
+ })).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
572958
  if (useful.length === 0)
572599
572959
  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}`;
572960
+ const lines = useful.map(({ entry, score }, index) => {
572961
+ const tool = typeof entry.toolName === "string" && entry.toolName ? ` tool=${entry.toolName}` : "";
572962
+ return `${index + 1}. sim=${score.toFixed(3)} ${entry.content.replace(/\s+/g, " ").slice(0, 260)}${tool}`;
572603
572963
  });
572604
572964
  return [
572605
572965
  `[PREFLIGHT MEMORY RECALL]`,
572606
- `Retrieved task-relevant prior episodes before the first action. Use these as hypotheses, not truth; verify against current files/UI.`,
572966
+ `Retrieved vector-similar prior episodes before the first action. Use these as hypotheses, not truth; verify against current files/UI.`,
572607
572967
  ...lines,
572608
572968
  `If the current task resembles one of these, prefer the remembered working verification path and avoid the remembered failure pattern.`
572609
572969
  ].join("\n");
@@ -572716,9 +573076,26 @@ ${shellLines.join("\n")}` : "Commands run: none"
572716
573076
  const failCount = toolCallLog.filter((e2) => e2.success === false).length;
572717
573077
  evidenceParts.push(`Failed tool calls this run: ${failCount}`);
572718
573078
  const evidenceDigest = evidenceParts.join("\n");
573079
+ const degraded = detectExplicitDegradedCompletion({
573080
+ originalGoal,
573081
+ actionsDigest,
573082
+ evidenceDigest,
573083
+ proposedSummary
573084
+ });
573085
+ if (degraded) {
573086
+ this._resolutionGateRejections = 0;
573087
+ this.emit({
573088
+ type: "status",
573089
+ content: `Resolution gate accepted explicit degraded completion: ${degraded.reason}`,
573090
+ turn,
573091
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
573092
+ });
573093
+ return { proceed: true };
573094
+ }
572719
573095
  let verdict = null;
572720
573096
  try {
572721
573097
  const backend = this._auxInferenceBackend();
573098
+ this._emitModelResolutionTelemetry("completion_resolution", turn);
572722
573099
  for (let attempt = 0; attempt < 2 && !verdict; attempt++) {
572723
573100
  const resp = await backend.chatCompletion({
572724
573101
  messages: [
@@ -576151,6 +576528,7 @@ Respond with your assessment, then take action.`;
576151
576528
  this.pendingUserMessages.length = 0;
576152
576529
  const persistentTaskGoal = cleanForStorage(actualUserGoal || "") || cleanedTask;
576153
576530
  const userGoal = persistentTaskGoal.slice(0, 500);
576531
+ this._taskRelevantErrorPatterns = process.env["OMNIUS_DISABLE_FAILURE_HANDOFF"] === "1" ? /* @__PURE__ */ new Map() : await this._selectTaskRelevantErrorPatterns(persistentTaskGoal, 10);
576154
576532
  this._taskState = {
576155
576533
  goal: userGoal,
576156
576534
  originalGoal: userGoal,
@@ -576195,6 +576573,7 @@ Respond with your assessment, then take action.`;
576195
576573
  contextWindowSize: this.options.contextWindowSize ?? 0,
576196
576574
  verbose: false
576197
576575
  });
576576
+ this._emitModelResolutionTelemetry("main");
576198
576577
  this._hookManager.runSessionHook("session_start", this._sessionId);
576199
576578
  if (this.writesUserTaskArtifacts()) {
576200
576579
  this._initializeCompletionContract(task, context2, actualUserGoal);
@@ -576425,7 +576804,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
576425
576804
  ...missionCompletionContract ? [{ role: "system", content: missionCompletionContract }] : [],
576426
576805
  { role: "user", content: userContent }
576427
576806
  ];
576428
- const preflightMemoryRecall = this._buildPreflightTaskMemoryRecall(persistentTaskGoal);
576807
+ const preflightMemoryRecall = await this._buildPreflightTaskMemoryRecall(persistentTaskGoal);
576429
576808
  if (preflightMemoryRecall) {
576430
576809
  messages2.splice(messages2.length - 1, 0, {
576431
576810
  role: "system",
@@ -576545,7 +576924,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
576545
576924
  try {
576546
576925
  const failureHandoff = buildFailureModeHandoff({
576547
576926
  taskGoal: persistentTaskGoal,
576548
- errorPatterns: this._errorPatterns,
576927
+ errorPatterns: this._taskRelevantErrorPatterns,
576549
576928
  toolCallLog,
576550
576929
  taskState: this._taskState,
576551
576930
  maxPatterns: 10,
@@ -580022,13 +580401,15 @@ Respond with EXACTLY this structure before your next tool call:
580022
580401
  default:
580023
580402
  guidance = `This tool failed previously with a similar error. Review the error message carefully and adjust your approach before retrying.`;
580024
580403
  }
580025
- this._errorPatterns.set(sig, {
580404
+ const learnedPattern = {
580026
580405
  count,
580027
580406
  guidance,
580028
580407
  lastSeen: Date.now(),
580029
580408
  tool: tc.name,
580030
580409
  errorType
580031
- });
580410
+ };
580411
+ this._errorPatterns.set(sig, learnedPattern);
580412
+ this._taskRelevantErrorPatterns.set(sig, learnedPattern);
580032
580413
  if (this._failureStore) {
580033
580414
  try {
580034
580415
  this._failureStore.insert({
@@ -580078,13 +580459,48 @@ Respond with EXACTLY this structure before your next tool call:
580078
580459
  }
580079
580460
  if (tc.name === "todo_write") {
580080
580461
  try {
580081
- const _todosNow = this.readSessionTodos() || [];
580462
+ let _todosNow = this.readSessionTodos() || [];
580082
580463
  for (const _tp of _todosNow) {
580083
580464
  const _tpId = _tp.id;
580084
580465
  if (_tp.status === "in_progress" && _tpId && !this._todoInProgressTurn.has(_tpId)) {
580085
580466
  this._todoInProgressTurn.set(_tpId, turn);
580086
580467
  }
580087
580468
  }
580469
+ const truth = reconcileCompletedTodosWithEvidence({
580470
+ todos: _todosNow,
580471
+ toolCallLog,
580472
+ todoStartTurnById: this._todoInProgressTurn
580473
+ });
580474
+ if (truth.changed) {
580475
+ const sid = this._sessionId || process.env["OMNIUS_SESSION_ID"] || "default";
580476
+ writeTodos(sid, truth.todos.map((t2) => ({
580477
+ id: t2.id,
580478
+ content: t2.content,
580479
+ status: t2.status,
580480
+ parentId: t2.parentId,
580481
+ blocker: t2.blocker,
580482
+ verifyCommand: t2.verifyCommand,
580483
+ declaredArtifacts: t2.declaredArtifacts
580484
+ })));
580485
+ _todosNow = this.readSessionTodos() || truth.todos;
580486
+ const downgradeLines = truth.downgrades.slice(0, 6).map((d2) => `- ${d2.content}: ${d2.from} -> ${d2.to}; ${d2.evidence}`).join("\n");
580487
+ messages2.push({
580488
+ role: "system",
580489
+ content: [
580490
+ `[TODO TRUTH RECONCILIATION]`,
580491
+ `One or more completed todo claims contradicted the evidence or nested child status and were rewritten in the active todo list.`,
580492
+ downgradeLines,
580493
+ ``,
580494
+ `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.`
580495
+ ].join("\n")
580496
+ });
580497
+ this.emit({
580498
+ type: "status",
580499
+ content: `Todo truth reconciled ${truth.downgrades.length} completed claim(s) from evidence/nested child state`,
580500
+ turn,
580501
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
580502
+ });
580503
+ }
580088
580504
  if (!this._newFieldNudgeFired) {
580089
580505
  this._todoWritesObservedForNudge++;
580090
580506
  const _anyFieldUsed = _todosNow.some((t2) => typeof t2.verifyCommand === "string" || Array.isArray(t2.declaredArtifacts));
@@ -580824,7 +581240,7 @@ Then use file_read on individual FILES inside it.`);
580824
581240
  if (process.env["OMNIUS_DISABLE_FAILURE_HANDOFF"] !== "1" && !result.success && turn - lastFailureHandoffTurn >= 4) {
580825
581241
  const runtimeHandoff = buildFailureModeHandoff({
580826
581242
  taskGoal: persistentTaskGoal,
580827
- errorPatterns: this._errorPatterns,
581243
+ errorPatterns: this._taskRelevantErrorPatterns,
580828
581244
  toolCallLog,
580829
581245
  taskState: this._taskState,
580830
581246
  maxPatterns: 5,
@@ -584348,7 +584764,7 @@ ${content.slice(0, 8e3)}
584348
584764
  try {
584349
584765
  const compactFailureHandoff = buildFailureModeHandoff({
584350
584766
  taskGoal: this._taskState.goal,
584351
- errorPatterns: this._errorPatterns,
584767
+ errorPatterns: this._taskRelevantErrorPatterns,
584352
584768
  taskState: this._taskState,
584353
584769
  maxPatterns: 6,
584354
584770
  maxRecentCalls: 0
@@ -613124,6 +613540,33 @@ function buildTodoProgressBar(todos, maxWidth) {
613124
613540
  if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET3}`;
613125
613541
  return out;
613126
613542
  }
613543
+ function orderTodosForDisplay(todos) {
613544
+ const byParent = /* @__PURE__ */ new Map();
613545
+ const byId = new Set(todos.map((t2) => t2.id));
613546
+ const roots = [];
613547
+ for (const todo of todos) {
613548
+ if (todo.parentId && byId.has(todo.parentId)) {
613549
+ const arr = byParent.get(todo.parentId) ?? [];
613550
+ arr.push(todo);
613551
+ byParent.set(todo.parentId, arr);
613552
+ } else {
613553
+ roots.push(todo);
613554
+ }
613555
+ }
613556
+ const out = [];
613557
+ const seen = /* @__PURE__ */ new Set();
613558
+ const visit = (todo, depth) => {
613559
+ if (seen.has(todo.id)) return;
613560
+ seen.add(todo.id);
613561
+ out.push({ ...todo, depth: Math.min(depth, 4) });
613562
+ for (const child of byParent.get(todo.id) ?? []) {
613563
+ visit(child, depth + 1);
613564
+ }
613565
+ };
613566
+ for (const root of roots) visit(root, 0);
613567
+ for (const todo of todos) visit(todo, 0);
613568
+ return out;
613569
+ }
613127
613570
  function render() {
613128
613571
  if (!_enabled) return;
613129
613572
  if (!panelEffectivelyVisible()) {
@@ -613149,16 +613592,18 @@ function render() {
613149
613592
  const progressBar = buildTodoProgressBar(_lastTodos, maxBarWidth);
613150
613593
  const headerText = `${headerPrefix}${progressBar}`;
613151
613594
  lines.push(headerText);
613152
- const visible = _lastTodos.slice(0, MAX_VISIBLE_ROWS - 1);
613595
+ const displayTodos = orderTodosForDisplay(_lastTodos);
613596
+ const visible = displayTodos.slice(0, MAX_VISIBLE_ROWS - 1);
613153
613597
  for (const t2 of visible) {
613154
613598
  const { mark, color } = statusToAnsi(t2.status);
613155
613599
  const contentWidth = Math.max(4, cols - 8);
613156
- const contentText = t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
613600
+ const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth - 1)}- ` : "";
613601
+ const contentText = indent2 + t2.content + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
613157
613602
  const truncated = truncate2(contentText, contentWidth);
613158
613603
  lines.push(`${color}${mark}${RESET3} ${color}${truncated}${RESET3}`);
613159
613604
  }
613160
- if (_lastTodos.length > visible.length) {
613161
- const more = _lastTodos.length - visible.length;
613605
+ if (displayTodos.length > visible.length) {
613606
+ const more = displayTodos.length - visible.length;
613162
613607
  lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET3}`;
613163
613608
  }
613164
613609
  let out = HIDE + SAVE;
@@ -717470,6 +717915,7 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
717470
717915
  },
717471
717916
  async () => {
717472
717917
  const result = await runner.run(effectiveTask, systemContext);
717918
+ _apiCallbacks?.onRunResult?.(result);
717473
717919
  const tokens = {
717474
717920
  total: result.totalTokens,
717475
717921
  estimated: result.estimatedTokens
@@ -723752,6 +724198,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723752
724198
  }
723753
724199
  }
723754
724200
  _apiCallbacks = callbacks ?? null;
724201
+ const headlessMode = Boolean(callbacks);
723755
724202
  await bootstrapMcpAndPlugins(repoRoot);
723756
724203
  renderCompactHeader(config.model);
723757
724204
  renderUserMessage(task);
@@ -723762,6 +724209,10 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723762
724209
  try {
723763
724210
  const handle2 = startTask(task, config, repoRoot);
723764
724211
  await handle2.promise;
724212
+ if (headlessMode) {
724213
+ _apiCallbacks = null;
724214
+ return;
724215
+ }
723765
724216
  try {
723766
724217
  const ikDir = join174(repoRoot, ".omnius", "identity");
723767
724218
  const ikFile = join174(ikDir, "self-state.json");
@@ -724045,6 +724496,10 @@ Rules:
724045
724496
  } catch {
724046
724497
  }
724047
724498
  } catch (err) {
724499
+ if (headlessMode) {
724500
+ _apiCallbacks = null;
724501
+ throw err;
724502
+ }
724048
724503
  try {
724049
724504
  const ikFile = join174(repoRoot, ".omnius", "identity", "self-state.json");
724050
724505
  if (existsSync161(ikFile)) {
@@ -724271,6 +724726,7 @@ async function runJson(task, config, repoPath2) {
724271
724726
  let result;
724272
724727
  const assistantTexts = [];
724273
724728
  const toolCallLog = [];
724729
+ let runnerResult = null;
724274
724730
  try {
724275
724731
  await runWithTUI(task, config, repoPath2, {
724276
724732
  onAssistantText: (text2) => {
@@ -724294,13 +724750,29 @@ async function runJson(task, config, repoPath2) {
724294
724750
  },
724295
724751
  onStatus: (content) => {
724296
724752
  origWrite(JSON.stringify({ type: "status", content }) + "\n");
724753
+ },
724754
+ onRunResult: (runResult) => {
724755
+ runnerResult = {
724756
+ status: runResult.status,
724757
+ completed: runResult.completed,
724758
+ summary: runResult.summary,
724759
+ turns: runResult.turns,
724760
+ toolCalls: runResult.toolCalls,
724761
+ filesEdited: runResult.filesEdited,
724762
+ testsRun: runResult.testsRun
724763
+ };
724297
724764
  }
724298
724765
  });
724766
+ const rr = runnerResult;
724299
724767
  result = {
724300
- status: "completed",
724301
- summary: extractSummary(captured),
724768
+ status: rr?.completed ? "completed" : rr?.status ?? "completed",
724769
+ summary: rr?.summary || extractSummary(captured),
724770
+ turns: rr?.turns,
724771
+ toolCalls: rr?.toolCalls,
724772
+ filesModified: rr?.filesEdited,
724773
+ testsRun: rr?.testsRun,
724302
724774
  durationMs: Date.now() - startTime,
724303
- exitCode: 0
724775
+ exitCode: rr && !rr.completed ? 2 : 0
724304
724776
  };
724305
724777
  } catch (err) {
724306
724778
  result = {
@@ -724324,7 +724796,14 @@ async function runJson(task, config, repoPath2) {
724324
724796
  result.tool_calls = toolCallLog;
724325
724797
  }
724326
724798
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
724327
- if (result.exitCode !== 0) process.exit(1);
724799
+ if (result.exitCode !== 0) process.exit(result.exitCode);
724800
+ if (shouldForceJsonExit()) process.exit(0);
724801
+ }
724802
+ function shouldForceJsonExit() {
724803
+ if (process.env["OMNIUS_JSON_NO_FORCE_EXIT"] === "1") return false;
724804
+ if (process.env["VITEST"] === "true" || process.env["NODE_ENV"] === "test")
724805
+ return false;
724806
+ return true;
724328
724807
  }
724329
724808
  function extractSummary(captured) {
724330
724809
  const all2 = captured.join("");
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.369",
3
+ "version": "1.0.370",
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.370",
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.370",
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",