omnius 1.0.689 → 1.0.690

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
@@ -360167,6 +360167,7 @@ var init_todo_write = __esm({
360167
360167
  init_todo_store();
360168
360168
  _currentSessionId = "";
360169
360169
  TodoWriteTool = class {
360170
+ executionScope = null;
360170
360171
  name = "todo_write";
360171
360172
  description = `Update the session task checklist. To be used proactively and often to track progress and pending tasks. Make sure that at least one task is in_progress at all times.
360172
360173
 
@@ -360306,6 +360307,24 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
360306
360307
  }
360307
360308
  }
360308
360309
  };
360310
+ constructor(scope) {
360311
+ if (scope)
360312
+ this.bindExecutionScope(scope);
360313
+ }
360314
+ bindExecutionScope(scope) {
360315
+ const sessionId = String(scope.sessionId ?? "").trim();
360316
+ if (!sessionId)
360317
+ throw new TypeError("todo_write requires a non-empty host session scope");
360318
+ if (this.executionScope && this.executionScope.sessionId !== sessionId) {
360319
+ throw new Error(`todo_write is already bound to session '${this.executionScope.sessionId}'`);
360320
+ }
360321
+ this.executionScope = Object.freeze({ ...scope, sessionId });
360322
+ }
360323
+ resolveSessionId(args) {
360324
+ if (this.executionScope)
360325
+ return this.executionScope.sessionId;
360326
+ return typeof args["session_id"] === "string" && args["session_id"].trim() ? args["session_id"].trim() : typeof args["sessionId"] === "string" && args["sessionId"].trim() ? args["sessionId"].trim() : getTodoSessionId();
360327
+ }
360309
360328
  async execute(args) {
360310
360329
  const start2 = performance.now();
360311
360330
  try {
@@ -360406,7 +360425,7 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
360406
360425
  };
360407
360426
  }
360408
360427
  enforceActiveLeafTodo(incoming, repairNotes);
360409
- const sessionId = typeof args["session_id"] === "string" && args["session_id"].trim() ? args["session_id"].trim() : typeof args["sessionId"] === "string" && args["sessionId"].trim() ? args["sessionId"].trim() : getTodoSessionId();
360428
+ const sessionId = this.resolveSessionId(args);
360410
360429
  const oldTodos = readTodos(sessionId);
360411
360430
  const canonicalize4 = (todos) => JSON.stringify(todos.map((t2) => ({
360412
360431
  content: t2.content,
@@ -360517,6 +360536,7 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
360517
360536
  "acceptance"
360518
360537
  ]);
360519
360538
  TodoReadTool = class {
360539
+ executionScope = null;
360520
360540
  name = "todo_read";
360521
360541
  description = "Read the current task checklist for this session.";
360522
360542
  parameters = {
@@ -360528,10 +360548,28 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
360528
360548
  }
360529
360549
  }
360530
360550
  };
360551
+ constructor(scope) {
360552
+ if (scope)
360553
+ this.bindExecutionScope(scope);
360554
+ }
360555
+ bindExecutionScope(scope) {
360556
+ const sessionId = String(scope.sessionId ?? "").trim();
360557
+ if (!sessionId)
360558
+ throw new TypeError("todo_read requires a non-empty host session scope");
360559
+ if (this.executionScope && this.executionScope.sessionId !== sessionId) {
360560
+ throw new Error(`todo_read is already bound to session '${this.executionScope.sessionId}'`);
360561
+ }
360562
+ this.executionScope = Object.freeze({ ...scope, sessionId });
360563
+ }
360564
+ resolveSessionId(args) {
360565
+ if (this.executionScope)
360566
+ return this.executionScope.sessionId;
360567
+ return typeof args["session_id"] === "string" && args["session_id"].trim() ? args["session_id"].trim() : typeof args["sessionId"] === "string" && args["sessionId"].trim() ? args["sessionId"].trim() : getTodoSessionId();
360568
+ }
360531
360569
  async execute(args) {
360532
360570
  const start2 = performance.now();
360533
360571
  try {
360534
- const sessionId = typeof args["session_id"] === "string" && args["session_id"].trim() ? args["session_id"].trim() : typeof args["sessionId"] === "string" && args["sessionId"].trim() ? args["sessionId"].trim() : getTodoSessionId();
360572
+ const sessionId = this.resolveSessionId(args);
360535
360573
  const todos = readTodos(sessionId);
360536
360574
  return {
360537
360575
  success: true,
@@ -630245,6 +630283,58 @@ var init_contextBudget = __esm({
630245
630283
  }
630246
630284
  });
630247
630285
 
630286
+ // packages/orchestrator/dist/convergence-progress.js
630287
+ function classifyAuthoritativeProgress(input) {
630288
+ if (!input.success || input.noop || input.runtimeAuthored)
630289
+ return null;
630290
+ const kind = input.confirmedMutation ? "mutation" : input.todoTransition ? "todo_transition" : input.workboardTransition ? "workboard_transition" : input.assertiveVerifier ? "verifier_receipt" : input.deliveryReceipt ? "delivery_receipt" : null;
630291
+ return kind ? { kind, fingerprint: `${kind}:${input.fingerprint}` } : null;
630292
+ }
630293
+ var ConvergenceProgressTracker;
630294
+ var init_convergence_progress = __esm({
630295
+ "packages/orchestrator/dist/convergence-progress.js"() {
630296
+ "use strict";
630297
+ ConvergenceProgressTracker = class {
630298
+ fingerprints = /* @__PURE__ */ new Set();
630299
+ progressCount = 0;
630300
+ lastProgressTurn = 0;
630301
+ lastKind;
630302
+ reviewLatched = false;
630303
+ record(turn, signal) {
630304
+ if (!signal || this.fingerprints.has(signal.fingerprint))
630305
+ return false;
630306
+ this.fingerprints.add(signal.fingerprint);
630307
+ this.progressCount += 1;
630308
+ this.lastProgressTurn = Math.max(0, turn);
630309
+ this.lastKind = signal.kind;
630310
+ this.reviewLatched = false;
630311
+ return true;
630312
+ }
630313
+ shouldLatchReview(input) {
630314
+ if (this.reviewLatched || input.openLeafCount <= 0)
630315
+ return false;
630316
+ const threshold = Math.max(1, Math.floor(input.thresholdTurns));
630317
+ if (input.turn - this.lastProgressTurn < threshold)
630318
+ return false;
630319
+ this.reviewLatched = true;
630320
+ return true;
630321
+ }
630322
+ hasRecentProgress(turn, windowTurns) {
630323
+ return this.progressCount > 0 && turn - this.lastProgressTurn <= Math.max(1, Math.floor(windowTurns));
630324
+ }
630325
+ snapshot(turn) {
630326
+ return {
630327
+ count: this.progressCount,
630328
+ lastProgressTurn: this.lastProgressTurn,
630329
+ turnsSinceProgress: Math.max(0, turn - this.lastProgressTurn),
630330
+ reviewLatched: this.reviewLatched,
630331
+ ...this.lastKind ? { lastKind: this.lastKind } : {}
630332
+ };
630333
+ }
630334
+ };
630335
+ }
630336
+ });
630337
+
630248
630338
  // packages/orchestrator/dist/inference-pressure.js
630249
630339
  function getInferencePressureCoordinator() {
630250
630340
  processCoordinator ??= new InferencePressureCoordinator({
@@ -662510,6 +662600,7 @@ var init_agenticRunner = __esm({
662510
662600
  "packages/orchestrator/dist/agenticRunner.js"() {
662511
662601
  "use strict";
662512
662602
  init_contextBudget();
662603
+ init_convergence_progress();
662513
662604
  init_inference_pressure();
662514
662605
  init_interruption_lifecycle();
662515
662606
  init_tool_exposure_policy();
@@ -664559,18 +664650,20 @@ ${parts.join("\n")}
664559
664650
  recordWorkboardToolCall(toolName, args, success, output2, result, meta = {}) {
664560
664651
  const board = this._currentWorkboardSnapshotForFrame() ?? this.getOrCreateWorkboard();
664561
664652
  if (!board)
664562
- return;
664653
+ return false;
664563
664654
  if (toolName === "task_complete" || toolName === "workboard_create")
664564
- return;
664655
+ return false;
664565
664656
  if (meta.repeatShortCircuit || result?.runtimeAuthored)
664566
- return;
664657
+ return false;
664567
664658
  if (meta.focusDecisionKind === "block_tool_call")
664568
- return;
664659
+ return false;
664569
664660
  if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result) && result.alreadyApplied !== true) {
664570
- return;
664661
+ return false;
664571
664662
  }
664572
664663
  const dir = this._workboardDir();
664573
664664
  try {
664665
+ const statusFingerprint = (snapshot) => snapshot.cards.filter((card) => !card.supersededBy).map((card) => `${card.id}:${card.status}:${card.lane ?? ""}`).sort().join("|");
664666
+ const beforeStatus = statusFingerprint(board);
664574
664667
  const evidenceSummary = toolName === "shell" && meta.shellFilesystemMutation ? `shell filesystem mutation ${success ? "ok" : "fail"}` : `${toolName} ${success ? "ok" : "fail"}`;
664575
664668
  const rawEvidenceOutput = typeof output2 === "string" && output2.length > 0 ? output2 : result?.output || result?.error || "";
664576
664669
  const evidenceContent = rawEvidenceOutput.slice(0, 1e3);
@@ -664591,14 +664684,15 @@ ${parts.join("\n")}
664591
664684
  };
664592
664685
  const actor = this.options.subAgent ? "sub-agent" : "agent";
664593
664686
  const advanced = this._advanceWorkboardForTool(board, toolName, result, actor, meta);
664687
+ const workboardTransition = statusFingerprint(advanced) !== beforeStatus;
664594
664688
  const targetCardId = this._workboardTargetCardId(advanced, toolName, args, result, meta);
664595
664689
  if (!targetCardId)
664596
- return;
664690
+ return workboardTransition;
664597
664691
  const targetCard = advanced.cards.find((card) => card.id === targetCardId);
664598
664692
  if (!targetCard)
664599
- return;
664693
+ return workboardTransition;
664600
664694
  if (targetCard.assignee && targetCard.assignee !== actor && targetCard.assignee !== "any") {
664601
- return;
664695
+ return workboardTransition;
664602
664696
  }
664603
664697
  const evidenced = result?.executionReceipt ? attachTrustedWorkboardCommandEvidence(dir, {
664604
664698
  runId: this.currentArtifactRunId(),
@@ -664616,7 +664710,7 @@ ${parts.join("\n")}
664616
664710
  evidence
664617
664711
  });
664618
664712
  const persistedEvidence = evidenced.cards.find((card) => card.id === targetCard.id)?.evidence.find((item) => item.summary === evidence.summary && item.content === evidence.content);
664619
- const substantive = success === true && result?.runtimeAuthored !== true;
664713
+ const substantive = success === true && result?.runtimeAuthored !== true && (workboardTransition || this._isRealProjectMutation(toolName, result) || meta.shellFilesystemMutation === true || toolName === "shell" && result !== void 0 && this._shellResultLooksLikeVerification(args, result) || Boolean(result?.completionDeliveryReceipt));
664620
664714
  this._workboard = updateWorkboardMetadata(dir, {
664621
664715
  runId: this.currentArtifactRunId(),
664622
664716
  actor,
@@ -664629,7 +664723,9 @@ ${parts.join("\n")}
664629
664723
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
664630
664724
  } : void 0
664631
664725
  });
664726
+ return workboardTransition;
664632
664727
  } catch {
664728
+ return false;
664633
664729
  }
664634
664730
  }
664635
664731
  /** Persist the workboard snapshot before run teardown. */
@@ -665097,6 +665193,9 @@ runtime_module_sha256=${record.runtimeProvenance.module.sha256 ?? "unknown"}`
665097
665193
  workboardDir: options2?.workboardDir ?? "",
665098
665194
  skipCrossTaskHandoff: options2?.skipCrossTaskHandoff ?? false
665099
665195
  };
665196
+ const explicitSessionId = String(options2?.sessionId ?? "").trim();
665197
+ const inheritedSessionId = String(process.env["OMNIUS_SESSION_ID"] ?? "").trim();
665198
+ this._sessionId = explicitSessionId || !options2?.subAgent && inheritedSessionId || `session-${Date.now()}-${crypto.randomUUID()}`;
665100
665199
  this.setCanonicalToolExposureContracts(this.options.canonicalToolExposureContracts);
665101
665200
  this._adversaryMode = this.options.adversaryMode;
665102
665201
  this._streamingExecutor.setConcurrencyResolver((name10, args) => this.resolveToolConcurrencySafe(name10, args));
@@ -674042,6 +674141,11 @@ ${blob}
674042
674141
  }
674043
674142
  if (!this.isToolAllowedByProfile(tool.name, tool.aliases))
674044
674143
  return;
674144
+ tool.bindExecutionScope?.({
674145
+ sessionId: this._sessionId,
674146
+ taskEpoch: this._taskEpoch,
674147
+ ownerId: this._interruptionOwnerId
674148
+ });
674045
674149
  const registeredTool = withTaskCompleteCloseoutContract(tool);
674046
674150
  this.tools.set(registeredTool.name, registeredTool);
674047
674151
  if (registeredTool.name === "generate_image") {
@@ -674965,8 +675069,8 @@ ${notice}`;
674965
675069
  _fsWriteFileSync(_pathJoin(dir, `${this._activeRunId || this._sessionId}-epoch-${taskEpoch}.json`), JSON.stringify(archive, null, 2), "utf8");
674966
675070
  } catch {
674967
675071
  }
674968
- for (const todo of todos)
674969
- this._supersededTodoIds.add(todo.id);
675072
+ writeTodos(this._sessionId, []);
675073
+ this._supersededTodoIds.clear();
674970
675074
  this._workboard = null;
674971
675075
  }
674972
675076
  /**
@@ -674997,8 +675101,8 @@ ${notice}`;
674997
675101
  }, null, 2), "utf8");
674998
675102
  } catch {
674999
675103
  }
675000
- for (const todo of todos)
675001
- this._supersededTodoIds.add(todo.id);
675104
+ writeTodos(this._sessionId, []);
675105
+ this._supersededTodoIds.clear();
675002
675106
  this._workboard = null;
675003
675107
  this.emit({
675004
675108
  type: "status",
@@ -676829,7 +676933,6 @@ Respond with the assessment and take the selected evidence-backed action.`;
676829
676933
  this.aborted = false;
676830
676934
  this._abortController = new AbortController();
676831
676935
  this._turnAbortController = new AbortController();
676832
- this._sessionId = this.options.sessionId && String(this.options.sessionId) || process.env["OMNIUS_SESSION_ID"] && String(process.env["OMNIUS_SESSION_ID"]) || `session-${Date.now()}`;
676833
676936
  this._taskEpoch += 1;
676834
676937
  this._contextMemoryTaskRecordId = null;
676835
676938
  this._contextMemoryArtifactByPath.clear();
@@ -676861,10 +676964,8 @@ Respond with the assessment and take the selected evidence-backed action.`;
676861
676964
  }
676862
676965
  this._interruptionLifecycle = null;
676863
676966
  } else {
676864
- if (recovered.phase === "paused" && recovered.recovered) {
676865
- if (!this._resumeInterrupted) {
676866
- throw new Error(`Run ${recovered.runId} is paused after recovery. Resume that exact generation instead of starting new work.`);
676867
- }
676967
+ const recoveredPause = recovered.phase === "paused" && recovered.recovered;
676968
+ if (recoveredPause && this._resumeInterrupted) {
676868
676969
  this._taskEpoch = recovered.taskEpoch;
676869
676970
  this._activeRunId = recovered.runId;
676870
676971
  const resumed = lifecycle.requestResume(recovered.owner, "explicit restart recovery requested");
@@ -676874,6 +676975,11 @@ Respond with the assessment and take the selected evidence-backed action.`;
676874
676975
  if (!acknowledged.accepted)
676875
676976
  throw new Error(acknowledged.reason);
676876
676977
  } else {
676978
+ if (recoveredPause) {
676979
+ const superseded = lifecycle.requestStop(recovered.owner, `superseded by a new task submitted after recovery of run ${recovered.runId}`);
676980
+ if (!superseded.accepted)
676981
+ throw new Error(superseded.reason);
676982
+ }
676877
676983
  lifecycle.beginRun({
676878
676984
  sessionId: this._sessionId,
676879
676985
  runId: this._activeRunId,
@@ -677151,7 +677257,7 @@ Respond with the assessment and take the selected evidence-backed action.`;
677151
677257
  this._fileRegistry.clear();
677152
677258
  this._memexArchive.clear();
677153
677259
  this._todoOutputLedger.clear();
677154
- setTodoSessionId(this._sessionId);
677260
+ this._supersededTodoIds.clear();
677155
677261
  const continuesPriorTask = this._allowImplicitContinuation || this._resumeInterrupted;
677156
677262
  if (!continuesPriorTask) {
677157
677263
  this._hidePriorSessionTodosForFreshTask(persistentTaskGoal);
@@ -677658,7 +677764,7 @@ ${dynamicProjectContext}`
677658
677764
  let estimatedTokens = 0;
677659
677765
  let toolCallCount = 0;
677660
677766
  let substantiveProgressCount = 0;
677661
- const substantiveProgressFingerprints = /* @__PURE__ */ new Set();
677767
+ const convergenceProgress = new ConvergenceProgressTracker();
677662
677768
  let completed = false;
677663
677769
  let summary = "";
677664
677770
  let acceptedTaskCompleteStatus;
@@ -677727,6 +677833,7 @@ ${dynamicProjectContext}`
677727
677833
  let consecutiveTextOnly = 0;
677728
677834
  let consecutiveThinkOnly = 0;
677729
677835
  let loopInterventionCount = 0;
677836
+ let loopCircuitBreakerLatched = false;
677730
677837
  const MAX_CONSECUTIVE_TEXT_ONLY = 3;
677731
677838
  const MAX_CONSECUTIVE_THINK_ONLY = 6;
677732
677839
  let narratedToolCallCount = 0;
@@ -679447,34 +679554,32 @@ Respond with EXACTLY this structure before your next tool call:
679447
679554
  nextSelfEval = now2 + selfEvalInterval;
679448
679555
  }
679449
679556
  const turnsRemaining = this.options.maxTurns - turn;
679450
- if (this.options.allowTurnExtension && turnsRemaining <= 3 && turnsRemaining > 0 && this._adversaryToolOutcomes.length >= 2) {
679557
+ if (this.options.allowTurnExtension && turnsRemaining <= 3 && turnsRemaining > 0 && convergenceProgress.hasRecentProgress(turn, 6)) {
679558
+ const extension3 = 30;
679559
+ const progress = convergenceProgress.snapshot(turn);
679560
+ this.options.maxTurns += extension3;
679561
+ this.emit({
679562
+ type: "status",
679563
+ content: `Convergence triage: recent ${progress.lastKind ?? "task"} advancement (${progress.turnsSinceProgress} turn(s) ago) extended the limit by ${extension3} (now ${this.options.maxTurns})`,
679564
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
679565
+ });
679451
679566
  const recentOutcomes = this._adversaryToolOutcomes.slice(-6);
679452
679567
  const recentSuccesses = recentOutcomes.filter((o2) => o2.succeeded).length;
679453
679568
  const uniqueResults = new Set(recentOutcomes.map((o2) => o2.preview.slice(0, 40))).size;
679454
- const isActive = recentSuccesses >= 2 && uniqueResults >= 2;
679455
- if (isActive) {
679456
- const extension3 = 30;
679457
- this.options.maxTurns += extension3;
679458
- this.emit({
679459
- type: "status",
679460
- content: `Adversary triage: activity detected (${recentSuccesses} recent successes, ${uniqueResults} unique results) — extending turn limit by ${extension3} (now ${this.options.maxTurns})`,
679461
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
679462
- });
679463
- const detailsLines = recentOutcomes.map((o2) => `- ${o2.tool}: ${o2.succeeded ? "OK" : "ERR"} — ${o2.preview}`);
679464
- this.emit({
679465
- type: "debug_adversary",
679466
- turn,
679467
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
679468
- content: `Timeout triage: EXTENDED by ${extension3} turns (active session detected)`,
679469
- adversaryAction: {
679470
- detection: "none",
679471
- recentSuccesses,
679472
- recentFailures: recentOutcomes.length - recentSuccesses,
679473
- intervention: `Extended maxTurns to ${this.options.maxTurns}`,
679474
- details: [`Recent outcomes:`, ...detailsLines].join("\n")
679475
- }
679476
- });
679477
- }
679569
+ const detailsLines = recentOutcomes.map((o2) => `- ${o2.tool}: ${o2.succeeded ? "OK" : "ERR"} — ${o2.preview}`);
679570
+ this.emit({
679571
+ type: "debug_adversary",
679572
+ turn,
679573
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
679574
+ content: `Timeout triage: EXTENDED by ${extension3} turns after typed task advancement`,
679575
+ adversaryAction: {
679576
+ detection: "none",
679577
+ recentSuccesses,
679578
+ recentFailures: recentOutcomes.length - recentSuccesses,
679579
+ intervention: `Extended maxTurns to ${this.options.maxTurns}`,
679580
+ details: [`Recent outcomes:`, ...detailsLines].join("\n")
679581
+ }
679582
+ });
679478
679583
  }
679479
679584
  const steeringBoundary = await this._applySteeringAtSafeBoundary(messages2, turn);
679480
679585
  if (steeringBoundary !== "continue") {
@@ -680786,7 +680891,7 @@ Corrective action: try a different approach first: read relevant files, adjust a
680786
680891
  turn,
680787
680892
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
680788
680893
  });
680789
- const budgetMsg = `[BUDGET EXHAUSTED] You have used all ${toolBudgets[tc.name]} allowed ${tc.name} calls for the current phase. You ALREADY have enough information from previous calls. DO NOT try to call ${tc.name} again — it will be blocked. ` + (this._todoWriteAvailable() ? `If your todo list shows more phases pending: mark the current phase completed via todo_write so a new budget allowance kicks in. ` : `If more phases remain, continue with a different available tool instead of repeating the exhausted one. `) + `If all phases are done: call task_complete with your final summary.`;
680894
+ const budgetMsg = `[BUDGET EXHAUSTED] You have used all ${toolBudgets[tc.name]} allowed ${tc.name} calls for the current phase. You ALREADY have enough information from previous calls. DO NOT try to call ${tc.name} again — it will be blocked. ` + (this._todoWriteAvailable() ? `If the current phase is complete, update the todo truth and take an action from the next phase; the detected context-tree phase transition resets the allowance. ` : `If more phases remain, continue with a different available tool instead of repeating the exhausted one. `) + `If all phases are done: call task_complete with your final summary.`;
680790
680895
  this.emit({
680791
680896
  type: "tool_result",
680792
680897
  toolName: tc.name,
@@ -682997,22 +683102,6 @@ Evidence: ${evidencePreview}`.slice(0, 500);
682997
683102
  turn,
682998
683103
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
682999
683104
  });
683000
- const branchCacheHit = result.completionEvidenceMetrics?.["branchCacheHit"] === true;
683001
- const nonSubstantiveResult = result.noop === true || branchCacheHit || repeatShortCircuit !== null && result.runtimeAuthored === true;
683002
- if (!nonSubstantiveResult) {
683003
- const observationFingerprint = [
683004
- this._buildToolFingerprint(tc.name, tc.arguments ?? {}),
683005
- result.success ? "ok" : "error",
683006
- result.beforeHash ?? "",
683007
- result.afterHash ?? "",
683008
- realMutationPaths.join(","),
683009
- this.quickHash(String(result.output ?? result.error ?? result.llmContent ?? ""))
683010
- ].join("|");
683011
- if (!substantiveProgressFingerprints.has(observationFingerprint)) {
683012
- substantiveProgressFingerprints.add(observationFingerprint);
683013
- substantiveProgressCount++;
683014
- }
683015
- }
683016
683105
  this._taskState.toolCallCount++;
683017
683106
  if (realFileMutation) {
683018
683107
  this._lastFileWriteTurn = turn;
@@ -683367,13 +683456,36 @@ ${delegateDir}` : delegateDir;
683367
683456
  });
683368
683457
  } catch {
683369
683458
  }
683370
- this.recordWorkboardToolCall(tc.name, tc.arguments, result.success, result.output || result.error || output2, result, {
683459
+ const workboardTransition = this.recordWorkboardToolCall(tc.name, tc.arguments, result.success, result.output || result.error || output2, result, {
683371
683460
  repeatShortCircuit: Boolean(repeatShortCircuit),
683372
683461
  focusDecisionKind: focusDecision?.kind,
683373
683462
  shellFilesystemMutation,
683374
683463
  turn
683375
683464
  });
683376
- if (tc.name === "todo_write" && !this._isTodoWriteNoopResult(result)) {
683465
+ const progressSignal = classifyAuthoritativeProgress({
683466
+ toolName: tc.name,
683467
+ fingerprint: [
683468
+ toolFingerprint,
683469
+ result.afterHash ?? "",
683470
+ realMutationPaths.join(","),
683471
+ result.completionDeliveryReceipt?.receiptId ?? "",
683472
+ workboardTransition ? "workboard-transition" : ""
683473
+ ].join("|"),
683474
+ success: result.success,
683475
+ noop: result.noop === true,
683476
+ runtimeAuthored: result.runtimeAuthored === true,
683477
+ confirmedMutation: realFileMutation || shellFilesystemMutation || result.success === true && result.mutated === true,
683478
+ todoTransition: tc.name === "todo_write" && result.success === true && !this._isTodoWriteNoopResult(result),
683479
+ workboardTransition,
683480
+ assertiveVerifier: tc.name === "shell" && result.success === true && this._shellResultLooksLikeVerification(tc.arguments, result),
683481
+ deliveryReceipt: Boolean(result.completionDeliveryReceipt)
683482
+ });
683483
+ if (convergenceProgress.record(turn, progressSignal)) {
683484
+ substantiveProgressCount = convergenceProgress.snapshot(turn).count;
683485
+ loopInterventionCount = 0;
683486
+ loopCircuitBreakerLatched = false;
683487
+ }
683488
+ if (tc.name === "todo_write" && result.success === true && !this._isTodoWriteNoopResult(result)) {
683377
683489
  this._lastTodoWriteTurn = turn;
683378
683490
  }
683379
683491
  if (tc.name === "file_read" || tc.name === "list_directory" || tc.name === "find_files" || tc.name === "grep_search") {
@@ -683919,21 +684031,19 @@ ${sr.result.output}`;
683919
684031
  freqMap.set(key2, (freqMap.get(key2) ?? 0) + 1);
683920
684032
  }
683921
684033
  const topRepeated = [...freqMap.entries()].sort((a2, b) => b[1] - a2[1]).slice(0, 2).map(([k, v]) => `${k} (${v}x)`).join(", ");
683922
- loopInterventionCount++;
683923
684034
  const loopTier2 = this.options.modelTier ?? "large";
683924
684035
  const maxInterventions = loopTier2 === "small" ? 3 : loopTier2 === "medium" ? 5 : 8;
684036
+ loopInterventionCount = Math.min(maxInterventions, loopInterventionCount + 1);
683925
684037
  let loopCircuitBreakerFired = false;
683926
684038
  const loopInterventionOrdinal = loopInterventionCount;
683927
- if (loopInterventionCount >= maxInterventions) {
684039
+ if (loopInterventionCount >= maxInterventions && !loopCircuitBreakerLatched) {
683928
684040
  loopCircuitBreakerFired = true;
684041
+ loopCircuitBreakerLatched = true;
683929
684042
  this.emit({
683930
684043
  type: "status",
683931
- content: `Loop circuit breaker: ${loopInterventionCount} interventions requesting user guidance`,
684044
+ content: `Loop circuit breaker latched after ${loopInterventionCount} interventions; awaiting authoritative task advancement or an agent-selected blocker/clarification decision`,
683932
684045
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
683933
684046
  });
683934
- const partialResults = this._taskState.completedSteps.length > 0 ? this._taskState.completedSteps.join(". ") : `Progress made before loop: ${topRepeated}`;
683935
- void partialResults;
683936
- loopInterventionCount = 0;
683937
684047
  }
683938
684048
  const findings = [];
683939
684049
  for (const [key2, entry] of recentToolResults) {
@@ -684277,7 +684387,9 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684277
684387
  }
684278
684388
  }
684279
684389
  const todosNow = this.readSessionTodos() || [];
684280
- const completedNow = todosNow.filter((t2) => t2.status === "completed").length;
684390
+ const parentIds = new Set(todosNow.map((todo) => todo.parentId).filter((id2) => Boolean(id2)));
684391
+ const leavesNow = todosNow.filter((todo) => !parentIds.has(todo.id));
684392
+ const completedNow = leavesNow.filter((t2) => t2.status === "completed").length;
684281
684393
  stagnationWindow.push({
684282
684394
  turn,
684283
684395
  ts: Date.now(),
@@ -684288,6 +684400,40 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684288
684400
  });
684289
684401
  } catch {
684290
684402
  }
684403
+ try {
684404
+ const todosNow = this.readSessionTodos() || [];
684405
+ const parentIds = new Set(todosNow.map((todo) => todo.parentId).filter((id2) => Boolean(id2)));
684406
+ const leaves = todosNow.filter((todo) => !parentIds.has(todo.id));
684407
+ const openLeaves = leaves.filter((todo) => todo.status !== "completed");
684408
+ const configuredThreshold = Number.parseInt(process.env["OMNIUS_CONVERGENCE_REVIEW_TURNS"] ?? "", 10);
684409
+ const defaultThreshold = this.options.modelTier === "small" ? 6 : this.options.modelTier === "medium" ? 8 : 10;
684410
+ const thresholdTurns = Number.isFinite(configuredThreshold) && configuredThreshold > 0 ? configuredThreshold : defaultThreshold;
684411
+ if (convergenceProgress.shouldLatchReview({
684412
+ turn,
684413
+ openLeafCount: openLeaves.length,
684414
+ thresholdTurns
684415
+ })) {
684416
+ const progress = convergenceProgress.snapshot(turn);
684417
+ const activeLeaf = openLeaves.find((todo) => todo.status === "in_progress") ?? openLeaves[0];
684418
+ const review = [
684419
+ "[CONVERGENCE REVIEW REQUIRED]",
684420
+ `The task leaf frontier has not advanced for ${progress.turnsSinceProgress} turns.`,
684421
+ `Leaf progress: ${leaves.length - openLeaves.length}/${leaves.length}.`,
684422
+ activeLeaf ? `Current leaf: ${activeLeaf.id} (${activeLeaf.status}) ${activeLeaf.content}` : "Current leaf: none.",
684423
+ "Reads, searches, and changed error text are evidence, not task advancement.",
684424
+ "Choose the next action from current evidence. You may continue with one specific action and its expected evidence, update or block the leaf truthfully, ask for missing input, or call task_complete only if completion is already proven.",
684425
+ "This review is advisory. The host does not infer the decision for you."
684426
+ ].join("\n");
684427
+ messages2.push({ role: "system", content: review });
684428
+ this.emit({
684429
+ type: "status",
684430
+ content: `Convergence review latched at leaf progress ${leaves.length - openLeaves.length}/${leaves.length} after ${progress.turnsSinceProgress} turns without authoritative advancement`,
684431
+ turn,
684432
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
684433
+ });
684434
+ }
684435
+ } catch {
684436
+ }
684291
684437
  }
684292
684438
  let prevCycleSubstantiveProgress = substantiveProgressCount;
684293
684439
  while (!completed && !this.aborted && !this._completionIncompleteVerification && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles) {
@@ -684296,7 +684442,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684296
684442
  if (bruteForceCycle > 1 && substantiveProgressCount === prevCycleSubstantiveProgress) {
684297
684443
  this.emit({
684298
684444
  type: "status",
684299
- content: `Stopping re-engagement — cycle ${bruteForceCycle - 1} produced no new executed fingerprint, mutation, todo transition, or verifier evidence (${toolCallCount} attempted tool calls; ${substantiveProgressCount} substantive observations).`,
684445
+ content: `Stopping re-engagement — cycle ${bruteForceCycle - 1} produced no authoritative mutation, todo/workboard transition, verifier receipt, or delivery receipt (${toolCallCount} attempted tool calls; ${substantiveProgressCount} authoritative advancements).`,
684300
684446
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
684301
684447
  });
684302
684448
  break;
@@ -684306,7 +684452,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684306
684452
  consecutiveThinkOnly = 0;
684307
684453
  this.emit({
684308
684454
  type: "status",
684309
- content: `Re-engaging — cycle ${bruteForceCycle} (${totalTurns} turns, ${toolCallCount} attempted calls / ${substantiveProgressCount} substantive observations)`,
684455
+ content: `Re-engaging — cycle ${bruteForceCycle} (${totalTurns} turns, ${toolCallCount} attempted calls / ${substantiveProgressCount} authoritative advancements)`,
684310
684456
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
684311
684457
  });
684312
684458
  this._reg61CooldownUntilTurn = -1;
@@ -702479,6 +702625,7 @@ __export(dist_exports3, {
702479
702625
  CoherenceGate: () => CoherenceGate,
702480
702626
  ContentAddressedArtifactStore: () => ContentAddressedArtifactStore,
702481
702627
  ConvergenceBreaker: () => ConvergenceBreaker,
702628
+ ConvergenceProgressTracker: () => ConvergenceProgressTracker,
702482
702629
  CoordinatorManager: () => CoordinatorManager,
702483
702630
  CostTracker: () => CostTracker,
702484
702631
  DEFAULT_ADAPTIVE_POLICY_CONFIG: () => DEFAULT_ADAPTIVE_POLICY_CONFIG,
@@ -702603,6 +702750,7 @@ __export(dist_exports3, {
702603
702750
  checkMilestoneComplete: () => checkMilestoneComplete,
702604
702751
  chooseCheapModelRoute: () => chooseCheapModelRoute,
702605
702752
  claimAssertion: () => claimAssertion,
702753
+ classifyAuthoritativeProgress: () => classifyAuthoritativeProgress,
702606
702754
  classifyBreadth: () => classifyBreadth,
702607
702755
  classifyClaimKind: () => classifyClaimKind,
702608
702756
  classifyCompletionClaim: () => classifyCompletionClaim,
@@ -702961,6 +703109,7 @@ var init_dist8 = __esm({
702961
703109
  init_mergeRunner();
702962
703110
  init_retryController();
702963
703111
  init_agenticRunner();
703112
+ init_convergence_progress();
702964
703113
  init_inference_pressure();
702965
703114
  init_canonical_context_projection();
702966
703115
  init_runtime_rollout();
@@ -715459,6 +715608,9 @@ function adaptExecutionTool(tool, options2 = {}) {
715459
715608
  return mapExecutionToolResult(result);
715460
715609
  }
715461
715610
  };
715611
+ if (typeof tool.bindExecutionScope === "function") {
715612
+ adapted.bindExecutionScope = (scope) => tool.bindExecutionScope(scope);
715613
+ }
715462
715614
  if (typeof tool.cancel === "function") {
715463
715615
  adapted.cancel = () => tool.cancel();
715464
715616
  }
@@ -803468,13 +803620,12 @@ function normalizeTelegramCallbackQuery(update2) {
803468
803620
  };
803469
803621
  }
803470
803622
  function adaptTool5(tool, todoSessionId, progress) {
803623
+ if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803624
+ tool.bindExecutionScope?.({ sessionId: todoSessionId });
803625
+ }
803471
803626
  return adaptExecutionTool(tool, {
803472
803627
  onProgress: (toolName, event) => progress?.onProgress(toolName, event),
803473
- execute: async (_tool, args, invoke) => {
803474
- const previousTodoSession = todoSessionId ? getTodoSessionId() : "";
803475
- if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803476
- setTodoSessionId(todoSessionId);
803477
- }
803628
+ execute: async (_tool, _args, invoke) => {
803478
803629
  try {
803479
803630
  const result = await invoke();
803480
803631
  progress?.complete(tool.name, result);
@@ -803486,10 +803637,6 @@ function adaptTool5(tool, todoSessionId, progress) {
803486
803637
  error: err instanceof Error ? err.message : String(err)
803487
803638
  });
803488
803639
  throw err;
803489
- } finally {
803490
- if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803491
- setTodoSessionId(previousTodoSession);
803492
- }
803493
803640
  }
803494
803641
  }
803495
803642
  });
@@ -33824,6 +33824,46 @@
33824
33824
  }
33825
33825
  ]
33826
33826
  },
33827
+ {
33828
+ "id": "guide.work-orders-runtime-health-remediation-wo-21-task-convergence-and-todo-scope-uppercase",
33829
+ "kind": "guide",
33830
+ "title": "WO-21: Task convergence and todo scope",
33831
+ "summary": "On 2026-09-03, a user reported that Omnius remained at task 2/8 for about 40 turns. The report did not include a screenshot or run artifact, so the exact label remains unverified. Omnius has two similar counters:",
33832
+ "keywords": [
33833
+ "work",
33834
+ "orders",
33835
+ "runtime",
33836
+ "health",
33837
+ "remediation",
33838
+ "WO",
33839
+ "21",
33840
+ "task",
33841
+ "convergence",
33842
+ "and",
33843
+ "todo",
33844
+ "scope",
33845
+ "md"
33846
+ ],
33847
+ "maturity": "internal",
33848
+ "audiences": [
33849
+ "maintainer",
33850
+ "large-context-agent"
33851
+ ],
33852
+ "layer": "documentation",
33853
+ "interfaces": [
33854
+ {
33855
+ "type": "file",
33856
+ "target": "docs/work-orders/runtime-health-remediation/WO-21-task-convergence-and-todo-scope.md"
33857
+ }
33858
+ ],
33859
+ "references": [
33860
+ {
33861
+ "type": "documentation",
33862
+ "target": "docs/work-orders/runtime-health-remediation/WO-21-task-convergence-and-todo-scope.md",
33863
+ "relation": "canonical-artifact"
33864
+ }
33865
+ ]
33866
+ },
33827
33867
  {
33828
33868
  "id": "guide.work-orders-telegram-dropbear-context-rca-workorder",
33829
33869
  "kind": "guide",
package/docs/DISCOVERY.md CHANGED
@@ -559,6 +559,7 @@ Daemon equivalents are `GET /v1/discovery/bootstrap`, `GET /v1/discovery?q=<inte
559
559
  | `guide.work-orders-runtime-health-remediation-wo-19-clean-build-evidence-uppercase` | WO-19 Clean Build Evidence | WO-19 is implemented in commit 5f253c05. |
560
560
  | `guide.work-orders-runtime-health-remediation-wo-19-clean-build-reproducibility-uppercase` | WO-19: Clean Build and Optional-Dependency Reproducibility | Status: deterministic acceptance complete Risk: medium Depends on: WO-00 |
561
561
  | `guide.work-orders-runtime-health-remediation-wo-20-exact-session-continuation-uppercase` | WO-20: Exact session continuation across exit and restart | On 2026-09-03, the telegramtest TUI displayed the latest pentest task during startup, but the first restored model context combined a completed breach.py handoff with an unrelated, older page.tsx todo tree. The user then entered continue, and Omnius continued the stale tree instead of the task that was active before /quit. |
562
+ | `guide.work-orders-runtime-health-remediation-wo-21-task-convergence-and-todo-scope-uppercase` | WO-21: Task convergence and todo scope | On 2026-09-03, a user reported that Omnius remained at task 2/8 for about 40 turns. The report did not include a screenshot or run artifact, so the exact label remains unverified. Omnius has two similar counters: |
562
563
  | `guide.work-orders-telegram-dropbear-context-rca-workorder` | Telegram Dropbear Context Engineering RCA Work Order | Observed run: /home/roko/Documents/Projects/Adjacent/telegramtest/.omnius, run id 1782873796963-i5r7mv. |
563
564
  | `guide.work-orders-wo-am-gaps-uppercase` | Associative Memory Gap Work Orders | Generated: 2026-04-13 Source: Deep audit of multimodal associative memory systems Status: READY FOR IMPLEMENTATION |
564
565
  | `guide.work-orders-world-class-memory-compiler-readme-uppercase` | World-Class Memory Compiler Program | Status: active implementation program Owner: Omnius orchestration and memory packages Last updated: 2026-07-13 |
@@ -311,3 +311,15 @@ deterministically repaired P0 or P1 defect.
311
311
  pass cancellation, restart, ownership, external-effect, and isolation tests.
312
312
  - [x] WO-20 exit and restart preserve one exact typed task continuation. Generic
313
313
  context restoration cannot mix or activate unrelated historical artifacts.
314
+ - [x] WO-21 task convergence and todo scope prevent stale `tasks N/M` displays,
315
+ cross-runner todo leakage, and activity-only re-engagement.
316
+ - [x] Bind todo tools to immutable runner-owned sessions.
317
+ - [x] Clear the active persisted checklist at a fresh task boundary after
318
+ archival.
319
+ - [x] Count only typed authoritative advancement as progress.
320
+ - [x] Latch an advisory convergence review when the leaf frontier is static.
321
+ - [x] Make loop, reminder, budget, and workboard progress semantics truthful.
322
+ - [x] Pass deterministic race, stasis, display, typecheck, and build tests.
323
+ - [x] A recovered pause no longer strands its session. Submitting a new task
324
+ retires the superseded generation instead of refusing every later prompt,
325
+ while unreconciled external effects still fence admission.
@@ -0,0 +1,115 @@
1
+ # WO-21: Task convergence and todo scope
2
+
3
+ ## Incident
4
+
5
+ On 2026-09-03, a user reported that Omnius remained at `task 2/8` for about
6
+ 40 turns. The report did not include a screenshot or run artifact, so the
7
+ exact label remains unverified. Omnius has two similar counters:
8
+
9
+ - `tasks 2/8` is the completed-leaf count in the pinned TUI checklist.
10
+ - `Loop intervention 2/8` is the second repetition intervention in a
11
+ model-tier-specific series.
12
+
13
+ The source audit found defects in both paths. These defects are sufficient to
14
+ produce the reported symptom even though they do not prove which counter the
15
+ reporter saw.
16
+
17
+ ## Root causes
18
+
19
+ - A TUI process reuses one todo session across tasks. A fresh runner hides old
20
+ todo IDs in its private view but leaves the persisted checklist unchanged.
21
+ The TUI reads that unchanged file and can display a stale `tasks 2/8` row.
22
+ - Todo tools fall back to one mutable process-global session ID. Concurrent
23
+ parent, Telegram, background, and child runners can redirect one another's
24
+ unscoped reads and writes.
25
+ - Any unique non-noop tool result increments the counter used to permit turn
26
+ extension and brute-force re-engagement. Varying reads, searches, and error
27
+ text therefore masquerade as task advancement.
28
+ - The repeated-loop counter resets after its maximum intervention even though
29
+ no task state changed. Its status says that user guidance was requested, but
30
+ it does not create a user-input boundary.
31
+ - A failed `todo_write` advances the reminder clock. The visible checklist can
32
+ remain unchanged for ten more turns without a reminder.
33
+ - Small-model budget text says that a todo update resets the phase budget, but
34
+ the implementation resets only after a context-tree phase transition.
35
+ - Workboard `lastSubstantiveProgress` treats successful discovery reads as
36
+ progress. This conflicts with the task and completion ledgers, where a read
37
+ is evidence but not advancement.
38
+
39
+ ## Required invariants
40
+
41
+ - [x] Every runner binds todo tools to its own immutable session scope.
42
+ - [x] Model-supplied todo session IDs cannot override the host scope.
43
+ - [x] Concurrent runners cannot redirect one another's todo reads or writes.
44
+ - [x] A fresh task archives the prior checklist and clears the active persisted
45
+ projection so the TUI, runner, REST, and Telegram views agree.
46
+ - [x] Only a confirmed mutation, todo transition, workboard transition,
47
+ assertive verifier receipt, or delivery receipt counts as authoritative task
48
+ progress.
49
+ - [x] Reads, searches, runtime-authored blocks, no-ops, and changing errors do
50
+ not extend the run or re-arm brute-force execution. Entry into the first
51
+ re-engagement cycle is deliberately not gated on prior authoritative
52
+ progress: that cycle is the rescue attempt for a run that has only read.
53
+ If it also advances nothing, re-engagement stops at cycle 2.
54
+ - [x] Repeated stasis creates one latched, model-visible convergence review.
55
+ - [x] The convergence review remains advisory. It does not infer completion,
56
+ manufacture a blocker, or force a user question.
57
+ - [x] The loop intervention maximum does not silently reset without
58
+ authoritative progress.
59
+ - [x] Todo reminders advance only after a successful state-changing write.
60
+ - [x] Budget exhaustion text describes the actual reset condition.
61
+ - [x] Workboard progress metadata uses the same authoritative distinction.
62
+ - [x] Todo stagnation compares completed leaves, matching the TUI counter.
63
+
64
+ ## Status
65
+
66
+ Implemented and verified on 2026-09-03.
67
+
68
+ | Invariant | Implementation | Test |
69
+ | --- | --- | --- |
70
+ | Immutable runner-owned todo scope | `packages/execution/src/tools/todo-write.ts` `bindExecutionScope` | `todo-store.test.ts` "binds todo tools to a host session that model arguments cannot replace" |
71
+ | Concurrent runner isolation | `AgenticRunner` constructor resolves one immutable `_sessionId`; `registerTool` binds it | `todo-store.test.ts` "keeps concurrent runner-scoped todo writes isolated" |
72
+ | Fresh task clears the active projection | `_hidePriorSessionTodosForFreshTask` writes an empty active checklist after archival | `fresh-task-todo-boundary.test.ts` |
73
+ | Typed authoritative progress only | `packages/orchestrator/src/convergence-progress.ts` | `task-convergence-progress.test.ts` (8 cases) |
74
+ | Latched advisory convergence review | `agenticRunner.ts` convergence review block | `task-convergence-progress.test.ts` "latches one advisory review and clears it only after typed progress" |
75
+ | Loop intervention maximum stays latched | `Math.min(maxInterventions, loopInterventionCount + 1)` | `agenticRunner-context-behavior.test.ts` "latches the loop-intervention maximum instead of cycling it back to one" |
76
+ | Workboard progress uses the same distinction | `recordWorkboardToolCall` returns a status-fingerprint transition | `workboard-run-continuity.test.ts` "does not record a successful discovery read as task advancement" |
77
+
78
+ Full suites run green: `@omnius/execution` 1507 passed, `@omnius/orchestrator`
79
+ 2303 passed (200 files, 2 skipped), `omnius` CLI 2428 passed (258 files).
80
+ Typecheck passes for all three packages and `pnpm -r build` completes.
81
+
82
+ The reported counter was never attributed to a specific field run. The evidence
83
+ request below stays open.
84
+
85
+ One correction was made during verification. An earlier draft of this work also
86
+ refused to enter brute-force cycle 1 whenever the primary run recorded no
87
+ authoritative advancement. That inverted the purpose of re-engagement and broke
88
+ eight existing brute-force tests, because a run that has only read is exactly
89
+ the run that still needs one push to act. The cycle-2 gate already bounds the
90
+ thrash this work order set out to stop, so the cycle-1 refusal was removed.
91
+
92
+ ## Deterministic verification
93
+
94
+ - [x] Scoped todo tools ignore conflicting model-supplied session IDs.
95
+ - [x] Concurrent scoped todo tools write isolated session files.
96
+ - [x] A same-session fresh task clears the old visible todo projection after
97
+ archiving it.
98
+ - [x] Forty distinct reads produce zero authoritative progress.
99
+ - [x] Changing failures produce zero authoritative progress.
100
+ - [x] Mutations, todo transitions, workboard transitions, assertive verifiers,
101
+ and delivery receipts produce typed progress.
102
+ - [x] A convergence review latches after the configured unchanged frontier and
103
+ clears only after typed progress.
104
+ - [x] The loop intervention maximum stays latched instead of cycling to one.
105
+ - [x] Focused execution, orchestrator, and CLI tests pass.
106
+ - [x] Affected package typechecks and builds pass.
107
+
108
+ ## Evidence request for exact field attribution
109
+
110
+ If the original run is available, collect only a redacted structural slice:
111
+ the exact counter line or screenshot, Omnius version, model/backend, launch
112
+ surface, incident time and timezone, session/run IDs, five turns before the
113
+ stall through ten turns after it, todo IDs/statuses/revisions, workboard card
114
+ statuses, and completion-ledger statuses. Do not copy a complete `.omnius`
115
+ directory because debug previews can contain user text and source fragments.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.689",
3
+ "version": "1.0.690",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.689",
9
+ "version": "1.0.690",
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.689",
3
+ "version": "1.0.690",
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/library.js",