omnius 1.0.688 → 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();
@@ -663967,7 +664058,7 @@ ${loadPrompt("agentic/system-small.md")}`;
663967
664058
  if (this.options.subAgent || this.options.recursionDepth > 0)
663968
664059
  return [];
663969
664060
  const normalizedGoal = goal.replace(/\s+/g, " ").trim();
663970
- if (normalizedGoal.length < 120 && !this._isContinuationResumeGoal(normalizedGoal))
664061
+ if (normalizedGoal.length < 120 && !this._resumeInterrupted)
663971
664062
  return [];
663972
664063
  return [
663973
664064
  {
@@ -664035,44 +664126,6 @@ ${loadPrompt("agentic/system-small.md")}`;
664035
664126
  }
664036
664127
  ];
664037
664128
  }
664038
- _isContinuationResumeGoal(goal) {
664039
- const tokens3 = goal.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter(Boolean);
664040
- if (tokens3.length === 0 || tokens3.length > 14)
664041
- return false;
664042
- const continuationTerms = /* @__PURE__ */ new Set([
664043
- "again",
664044
- "and",
664045
- "back",
664046
- "carry",
664047
- "complete",
664048
- "continue",
664049
- "finish",
664050
- "from",
664051
- "last",
664052
- "latest",
664053
- "left",
664054
- "my",
664055
- "off",
664056
- "please",
664057
- "previous",
664058
- "prior",
664059
- "proceed",
664060
- "request",
664061
- "resume",
664062
- "task",
664063
- "that",
664064
- "the",
664065
- "this",
664066
- "to",
664067
- "try",
664068
- "work"
664069
- ]);
664070
- const hasResumeVerb = tokens3.some((token) => token === "continue" || token === "resume" || token === "proceed" || token === "complete" || token === "finish");
664071
- if (!hasResumeVerb)
664072
- return false;
664073
- const continuationTokenCount = tokens3.filter((token) => continuationTerms.has(token)).length;
664074
- return continuationTokenCount / tokens3.length >= 0.7;
664075
- }
664076
664129
  /**
664077
664130
  * Build a compact workboard context string for injection into the
664078
664131
  * system prompt. Returns null when no active board exists or when
@@ -664597,18 +664650,20 @@ ${parts.join("\n")}
664597
664650
  recordWorkboardToolCall(toolName, args, success, output2, result, meta = {}) {
664598
664651
  const board = this._currentWorkboardSnapshotForFrame() ?? this.getOrCreateWorkboard();
664599
664652
  if (!board)
664600
- return;
664653
+ return false;
664601
664654
  if (toolName === "task_complete" || toolName === "workboard_create")
664602
- return;
664655
+ return false;
664603
664656
  if (meta.repeatShortCircuit || result?.runtimeAuthored)
664604
- return;
664657
+ return false;
664605
664658
  if (meta.focusDecisionKind === "block_tool_call")
664606
- return;
664659
+ return false;
664607
664660
  if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result) && result.alreadyApplied !== true) {
664608
- return;
664661
+ return false;
664609
664662
  }
664610
664663
  const dir = this._workboardDir();
664611
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);
664612
664667
  const evidenceSummary = toolName === "shell" && meta.shellFilesystemMutation ? `shell filesystem mutation ${success ? "ok" : "fail"}` : `${toolName} ${success ? "ok" : "fail"}`;
664613
664668
  const rawEvidenceOutput = typeof output2 === "string" && output2.length > 0 ? output2 : result?.output || result?.error || "";
664614
664669
  const evidenceContent = rawEvidenceOutput.slice(0, 1e3);
@@ -664629,14 +664684,15 @@ ${parts.join("\n")}
664629
664684
  };
664630
664685
  const actor = this.options.subAgent ? "sub-agent" : "agent";
664631
664686
  const advanced = this._advanceWorkboardForTool(board, toolName, result, actor, meta);
664687
+ const workboardTransition = statusFingerprint(advanced) !== beforeStatus;
664632
664688
  const targetCardId = this._workboardTargetCardId(advanced, toolName, args, result, meta);
664633
664689
  if (!targetCardId)
664634
- return;
664690
+ return workboardTransition;
664635
664691
  const targetCard = advanced.cards.find((card) => card.id === targetCardId);
664636
664692
  if (!targetCard)
664637
- return;
664693
+ return workboardTransition;
664638
664694
  if (targetCard.assignee && targetCard.assignee !== actor && targetCard.assignee !== "any") {
664639
- return;
664695
+ return workboardTransition;
664640
664696
  }
664641
664697
  const evidenced = result?.executionReceipt ? attachTrustedWorkboardCommandEvidence(dir, {
664642
664698
  runId: this.currentArtifactRunId(),
@@ -664654,7 +664710,7 @@ ${parts.join("\n")}
664654
664710
  evidence
664655
664711
  });
664656
664712
  const persistedEvidence = evidenced.cards.find((card) => card.id === targetCard.id)?.evidence.find((item) => item.summary === evidence.summary && item.content === evidence.content);
664657
- 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));
664658
664714
  this._workboard = updateWorkboardMetadata(dir, {
664659
664715
  runId: this.currentArtifactRunId(),
664660
664716
  actor,
@@ -664667,7 +664723,9 @@ ${parts.join("\n")}
664667
664723
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
664668
664724
  } : void 0
664669
664725
  });
664726
+ return workboardTransition;
664670
664727
  } catch {
664728
+ return false;
664671
664729
  }
664672
664730
  }
664673
664731
  /** Persist the workboard snapshot before run teardown. */
@@ -665135,6 +665193,9 @@ runtime_module_sha256=${record.runtimeProvenance.module.sha256 ?? "unknown"}`
665135
665193
  workboardDir: options2?.workboardDir ?? "",
665136
665194
  skipCrossTaskHandoff: options2?.skipCrossTaskHandoff ?? false
665137
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()}`;
665138
665199
  this.setCanonicalToolExposureContracts(this.options.canonicalToolExposureContracts);
665139
665200
  this._adversaryMode = this.options.adversaryMode;
665140
665201
  this._streamingExecutor.setConcurrencyResolver((name10, args) => this.resolveToolConcurrencySafe(name10, args));
@@ -674080,6 +674141,11 @@ ${blob}
674080
674141
  }
674081
674142
  if (!this.isToolAllowedByProfile(tool.name, tool.aliases))
674082
674143
  return;
674144
+ tool.bindExecutionScope?.({
674145
+ sessionId: this._sessionId,
674146
+ taskEpoch: this._taskEpoch,
674147
+ ownerId: this._interruptionOwnerId
674148
+ });
674083
674149
  const registeredTool = withTaskCompleteCloseoutContract(tool);
674084
674150
  this.tools.set(registeredTool.name, registeredTool);
674085
674151
  if (registeredTool.name === "generate_image") {
@@ -675003,8 +675069,8 @@ ${notice}`;
675003
675069
  _fsWriteFileSync(_pathJoin(dir, `${this._activeRunId || this._sessionId}-epoch-${taskEpoch}.json`), JSON.stringify(archive, null, 2), "utf8");
675004
675070
  } catch {
675005
675071
  }
675006
- for (const todo of todos)
675007
- this._supersededTodoIds.add(todo.id);
675072
+ writeTodos(this._sessionId, []);
675073
+ this._supersededTodoIds.clear();
675008
675074
  this._workboard = null;
675009
675075
  }
675010
675076
  /**
@@ -675035,8 +675101,8 @@ ${notice}`;
675035
675101
  }, null, 2), "utf8");
675036
675102
  } catch {
675037
675103
  }
675038
- for (const todo of todos)
675039
- this._supersededTodoIds.add(todo.id);
675104
+ writeTodos(this._sessionId, []);
675105
+ this._supersededTodoIds.clear();
675040
675106
  this._workboard = null;
675041
675107
  this.emit({
675042
675108
  type: "status",
@@ -676867,7 +676933,6 @@ Respond with the assessment and take the selected evidence-backed action.`;
676867
676933
  this.aborted = false;
676868
676934
  this._abortController = new AbortController();
676869
676935
  this._turnAbortController = new AbortController();
676870
- this._sessionId = this.options.sessionId && String(this.options.sessionId) || process.env["OMNIUS_SESSION_ID"] && String(process.env["OMNIUS_SESSION_ID"]) || `session-${Date.now()}`;
676871
676936
  this._taskEpoch += 1;
676872
676937
  this._contextMemoryTaskRecordId = null;
676873
676938
  this._contextMemoryArtifactByPath.clear();
@@ -676899,10 +676964,8 @@ Respond with the assessment and take the selected evidence-backed action.`;
676899
676964
  }
676900
676965
  this._interruptionLifecycle = null;
676901
676966
  } else {
676902
- if (recovered.phase === "paused" && recovered.recovered) {
676903
- if (!this._resumeInterrupted) {
676904
- throw new Error(`Run ${recovered.runId} is paused after recovery. Resume that exact generation instead of starting new work.`);
676905
- }
676967
+ const recoveredPause = recovered.phase === "paused" && recovered.recovered;
676968
+ if (recoveredPause && this._resumeInterrupted) {
676906
676969
  this._taskEpoch = recovered.taskEpoch;
676907
676970
  this._activeRunId = recovered.runId;
676908
676971
  const resumed = lifecycle.requestResume(recovered.owner, "explicit restart recovery requested");
@@ -676912,6 +676975,11 @@ Respond with the assessment and take the selected evidence-backed action.`;
676912
676975
  if (!acknowledged.accepted)
676913
676976
  throw new Error(acknowledged.reason);
676914
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
+ }
676915
676983
  lifecycle.beginRun({
676916
676984
  sessionId: this._sessionId,
676917
676985
  runId: this._activeRunId,
@@ -677189,8 +677257,8 @@ Respond with the assessment and take the selected evidence-backed action.`;
677189
677257
  this._fileRegistry.clear();
677190
677258
  this._memexArchive.clear();
677191
677259
  this._todoOutputLedger.clear();
677192
- setTodoSessionId(this._sessionId);
677193
- const continuesPriorTask = this._allowImplicitContinuation || this._isContinuationResumeGoal(persistentTaskGoal);
677260
+ this._supersededTodoIds.clear();
677261
+ const continuesPriorTask = this._allowImplicitContinuation || this._resumeInterrupted;
677194
677262
  if (!continuesPriorTask) {
677195
677263
  this._hidePriorSessionTodosForFreshTask(persistentTaskGoal);
677196
677264
  }
@@ -677696,7 +677764,7 @@ ${dynamicProjectContext}`
677696
677764
  let estimatedTokens = 0;
677697
677765
  let toolCallCount = 0;
677698
677766
  let substantiveProgressCount = 0;
677699
- const substantiveProgressFingerprints = /* @__PURE__ */ new Set();
677767
+ const convergenceProgress = new ConvergenceProgressTracker();
677700
677768
  let completed = false;
677701
677769
  let summary = "";
677702
677770
  let acceptedTaskCompleteStatus;
@@ -677765,6 +677833,7 @@ ${dynamicProjectContext}`
677765
677833
  let consecutiveTextOnly = 0;
677766
677834
  let consecutiveThinkOnly = 0;
677767
677835
  let loopInterventionCount = 0;
677836
+ let loopCircuitBreakerLatched = false;
677768
677837
  const MAX_CONSECUTIVE_TEXT_ONLY = 3;
677769
677838
  const MAX_CONSECUTIVE_THINK_ONLY = 6;
677770
677839
  let narratedToolCallCount = 0;
@@ -679485,34 +679554,32 @@ Respond with EXACTLY this structure before your next tool call:
679485
679554
  nextSelfEval = now2 + selfEvalInterval;
679486
679555
  }
679487
679556
  const turnsRemaining = this.options.maxTurns - turn;
679488
- 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
+ });
679489
679566
  const recentOutcomes = this._adversaryToolOutcomes.slice(-6);
679490
679567
  const recentSuccesses = recentOutcomes.filter((o2) => o2.succeeded).length;
679491
679568
  const uniqueResults = new Set(recentOutcomes.map((o2) => o2.preview.slice(0, 40))).size;
679492
- const isActive = recentSuccesses >= 2 && uniqueResults >= 2;
679493
- if (isActive) {
679494
- const extension3 = 30;
679495
- this.options.maxTurns += extension3;
679496
- this.emit({
679497
- type: "status",
679498
- content: `Adversary triage: activity detected (${recentSuccesses} recent successes, ${uniqueResults} unique results) — extending turn limit by ${extension3} (now ${this.options.maxTurns})`,
679499
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
679500
- });
679501
- const detailsLines = recentOutcomes.map((o2) => `- ${o2.tool}: ${o2.succeeded ? "OK" : "ERR"} — ${o2.preview}`);
679502
- this.emit({
679503
- type: "debug_adversary",
679504
- turn,
679505
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
679506
- content: `Timeout triage: EXTENDED by ${extension3} turns (active session detected)`,
679507
- adversaryAction: {
679508
- detection: "none",
679509
- recentSuccesses,
679510
- recentFailures: recentOutcomes.length - recentSuccesses,
679511
- intervention: `Extended maxTurns to ${this.options.maxTurns}`,
679512
- details: [`Recent outcomes:`, ...detailsLines].join("\n")
679513
- }
679514
- });
679515
- }
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
+ });
679516
679583
  }
679517
679584
  const steeringBoundary = await this._applySteeringAtSafeBoundary(messages2, turn);
679518
679585
  if (steeringBoundary !== "continue") {
@@ -680824,7 +680891,7 @@ Corrective action: try a different approach first: read relevant files, adjust a
680824
680891
  turn,
680825
680892
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
680826
680893
  });
680827
- 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.`;
680828
680895
  this.emit({
680829
680896
  type: "tool_result",
680830
680897
  toolName: tc.name,
@@ -683035,22 +683102,6 @@ Evidence: ${evidencePreview}`.slice(0, 500);
683035
683102
  turn,
683036
683103
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
683037
683104
  });
683038
- const branchCacheHit = result.completionEvidenceMetrics?.["branchCacheHit"] === true;
683039
- const nonSubstantiveResult = result.noop === true || branchCacheHit || repeatShortCircuit !== null && result.runtimeAuthored === true;
683040
- if (!nonSubstantiveResult) {
683041
- const observationFingerprint = [
683042
- this._buildToolFingerprint(tc.name, tc.arguments ?? {}),
683043
- result.success ? "ok" : "error",
683044
- result.beforeHash ?? "",
683045
- result.afterHash ?? "",
683046
- realMutationPaths.join(","),
683047
- this.quickHash(String(result.output ?? result.error ?? result.llmContent ?? ""))
683048
- ].join("|");
683049
- if (!substantiveProgressFingerprints.has(observationFingerprint)) {
683050
- substantiveProgressFingerprints.add(observationFingerprint);
683051
- substantiveProgressCount++;
683052
- }
683053
- }
683054
683105
  this._taskState.toolCallCount++;
683055
683106
  if (realFileMutation) {
683056
683107
  this._lastFileWriteTurn = turn;
@@ -683405,13 +683456,36 @@ ${delegateDir}` : delegateDir;
683405
683456
  });
683406
683457
  } catch {
683407
683458
  }
683408
- 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, {
683409
683460
  repeatShortCircuit: Boolean(repeatShortCircuit),
683410
683461
  focusDecisionKind: focusDecision?.kind,
683411
683462
  shellFilesystemMutation,
683412
683463
  turn
683413
683464
  });
683414
- 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)) {
683415
683489
  this._lastTodoWriteTurn = turn;
683416
683490
  }
683417
683491
  if (tc.name === "file_read" || tc.name === "list_directory" || tc.name === "find_files" || tc.name === "grep_search") {
@@ -683957,21 +684031,19 @@ ${sr.result.output}`;
683957
684031
  freqMap.set(key2, (freqMap.get(key2) ?? 0) + 1);
683958
684032
  }
683959
684033
  const topRepeated = [...freqMap.entries()].sort((a2, b) => b[1] - a2[1]).slice(0, 2).map(([k, v]) => `${k} (${v}x)`).join(", ");
683960
- loopInterventionCount++;
683961
684034
  const loopTier2 = this.options.modelTier ?? "large";
683962
684035
  const maxInterventions = loopTier2 === "small" ? 3 : loopTier2 === "medium" ? 5 : 8;
684036
+ loopInterventionCount = Math.min(maxInterventions, loopInterventionCount + 1);
683963
684037
  let loopCircuitBreakerFired = false;
683964
684038
  const loopInterventionOrdinal = loopInterventionCount;
683965
- if (loopInterventionCount >= maxInterventions) {
684039
+ if (loopInterventionCount >= maxInterventions && !loopCircuitBreakerLatched) {
683966
684040
  loopCircuitBreakerFired = true;
684041
+ loopCircuitBreakerLatched = true;
683967
684042
  this.emit({
683968
684043
  type: "status",
683969
- 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`,
683970
684045
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
683971
684046
  });
683972
- const partialResults = this._taskState.completedSteps.length > 0 ? this._taskState.completedSteps.join(". ") : `Progress made before loop: ${topRepeated}`;
683973
- void partialResults;
683974
- loopInterventionCount = 0;
683975
684047
  }
683976
684048
  const findings = [];
683977
684049
  for (const [key2, entry] of recentToolResults) {
@@ -684315,7 +684387,9 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684315
684387
  }
684316
684388
  }
684317
684389
  const todosNow = this.readSessionTodos() || [];
684318
- 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;
684319
684393
  stagnationWindow.push({
684320
684394
  turn,
684321
684395
  ts: Date.now(),
@@ -684326,6 +684400,40 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684326
684400
  });
684327
684401
  } catch {
684328
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
+ }
684329
684437
  }
684330
684438
  let prevCycleSubstantiveProgress = substantiveProgressCount;
684331
684439
  while (!completed && !this.aborted && !this._completionIncompleteVerification && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles) {
@@ -684334,7 +684442,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684334
684442
  if (bruteForceCycle > 1 && substantiveProgressCount === prevCycleSubstantiveProgress) {
684335
684443
  this.emit({
684336
684444
  type: "status",
684337
- 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).`,
684338
684446
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
684339
684447
  });
684340
684448
  break;
@@ -684344,7 +684452,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
684344
684452
  consecutiveThinkOnly = 0;
684345
684453
  this.emit({
684346
684454
  type: "status",
684347
- 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)`,
684348
684456
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
684349
684457
  });
684350
684458
  this._reg61CooldownUntilTurn = -1;
@@ -702517,6 +702625,7 @@ __export(dist_exports3, {
702517
702625
  CoherenceGate: () => CoherenceGate,
702518
702626
  ContentAddressedArtifactStore: () => ContentAddressedArtifactStore,
702519
702627
  ConvergenceBreaker: () => ConvergenceBreaker,
702628
+ ConvergenceProgressTracker: () => ConvergenceProgressTracker,
702520
702629
  CoordinatorManager: () => CoordinatorManager,
702521
702630
  CostTracker: () => CostTracker,
702522
702631
  DEFAULT_ADAPTIVE_POLICY_CONFIG: () => DEFAULT_ADAPTIVE_POLICY_CONFIG,
@@ -702641,6 +702750,7 @@ __export(dist_exports3, {
702641
702750
  checkMilestoneComplete: () => checkMilestoneComplete,
702642
702751
  chooseCheapModelRoute: () => chooseCheapModelRoute,
702643
702752
  claimAssertion: () => claimAssertion,
702753
+ classifyAuthoritativeProgress: () => classifyAuthoritativeProgress,
702644
702754
  classifyBreadth: () => classifyBreadth,
702645
702755
  classifyClaimKind: () => classifyClaimKind,
702646
702756
  classifyCompletionClaim: () => classifyCompletionClaim,
@@ -702999,6 +703109,7 @@ var init_dist8 = __esm({
702999
703109
  init_mergeRunner();
703000
703110
  init_retryController();
703001
703111
  init_agenticRunner();
703112
+ init_convergence_progress();
703002
703113
  init_inference_pressure();
703003
703114
  init_canonical_context_projection();
703004
703115
  init_runtime_rollout();
@@ -715497,6 +715608,9 @@ function adaptExecutionTool(tool, options2 = {}) {
715497
715608
  return mapExecutionToolResult(result);
715498
715609
  }
715499
715610
  };
715611
+ if (typeof tool.bindExecutionScope === "function") {
715612
+ adapted.bindExecutionScope = (scope) => tool.bindExecutionScope(scope);
715613
+ }
715500
715614
  if (typeof tool.cancel === "function") {
715501
715615
  adapted.cancel = () => tool.cancel();
715502
715616
  }
@@ -731239,6 +731353,7 @@ __export(omnius_directory_exports, {
731239
731353
  appendCompactionAudit: () => appendCompactionAudit,
731240
731354
  buildContextRestorePrompt: () => buildContextRestorePrompt,
731241
731355
  buildContextRestoreSnapshot: () => buildContextRestoreSnapshot,
731356
+ buildExactContinuationSnapshot: () => buildExactContinuationSnapshot,
731242
731357
  buildHandoffPrompt: () => buildHandoffPrompt,
731243
731358
  cleanPromptForDiary: () => cleanPromptForDiary,
731244
731359
  clearTaskHandoff: () => clearTaskHandoff,
@@ -731260,6 +731375,8 @@ __export(omnius_directory_exports, {
731260
731375
  loadSessionHistory: () => loadSessionHistory,
731261
731376
  loadTuiSessionState: () => loadTuiSessionState,
731262
731377
  loadUsageHistory: () => loadUsageHistory,
731378
+ pendingTaskMatchesInterruptionIdentity: () => pendingTaskMatchesInterruptionIdentity,
731379
+ pendingTaskMatchesRunningInterruption: () => pendingTaskMatchesRunningInterruption,
731263
731380
  readIndexData: () => readIndexData,
731264
731381
  readIndexMeta: () => readIndexMeta,
731265
731382
  readTaskHandoff: () => readTaskHandoff2,
@@ -731659,21 +731776,64 @@ function loadRecentSessions(repoRoot, limit2 = 5) {
731659
731776
  return [];
731660
731777
  }
731661
731778
  }
731779
+ function isValidPendingTask(value2, repoRoot) {
731780
+ if (!value2 || typeof value2 !== "object") return false;
731781
+ const task = value2;
731782
+ if (typeof task.prompt !== "string" || !task.prompt.trim() || typeof task.progressSummary !== "string" || !Array.isArray(task.filesModified) || !task.filesModified.every((item) => typeof item === "string") || typeof task.bruteForce !== "boolean" || typeof task.savedAt !== "string" || !Number.isFinite(Date.parse(task.savedAt)) || typeof task.toolCallCount !== "number" || !Number.isFinite(task.toolCallCount) || task.toolCallCount < 0) {
731783
+ return false;
731784
+ }
731785
+ if (task.schema !== void 0 && task.schema !== "omnius.pending-task.v2") {
731786
+ return false;
731787
+ }
731788
+ if (task.projectRoot !== void 0 && resolve77(task.projectRoot) !== resolve77(repoRoot)) {
731789
+ return false;
731790
+ }
731791
+ if (task.interruption !== void 0) {
731792
+ const interruption = task.interruption;
731793
+ if (typeof interruption.sessionId !== "string" || !interruption.sessionId.trim() || typeof interruption.runId !== "string" || !interruption.runId.trim() || !Number.isInteger(interruption.taskEpoch) || interruption.taskEpoch < 1 || !Number.isInteger(interruption.ownerGeneration) || interruption.ownerGeneration < 1 || interruption.phase !== "pausing" && interruption.phase !== "paused") {
731794
+ return false;
731795
+ }
731796
+ }
731797
+ if (task.resumeOnStartup === true && !task.interruption) return false;
731798
+ return true;
731799
+ }
731800
+ function pendingTaskMatchesRunningInterruption(pendingTask, candidate) {
731801
+ return candidate?.phase === "running" && pendingTaskMatchesInterruptionIdentity(pendingTask, candidate);
731802
+ }
731803
+ function pendingTaskMatchesInterruptionIdentity(pendingTask, candidate) {
731804
+ const expected = pendingTask.interruption;
731805
+ return Boolean(
731806
+ expected && candidate && candidate.sessionId === expected.sessionId && candidate.runId === expected.runId && candidate.taskEpoch === expected.taskEpoch && candidate.ownerGeneration === expected.ownerGeneration
731807
+ );
731808
+ }
731662
731809
  function savePendingTask(repoRoot, task) {
731663
731810
  const historyDir = join162(repoRoot, OMNIUS_DIR2, "history");
731664
731811
  mkdirSync95(historyDir, { recursive: true });
731665
- writeFileSync85(
731666
- join162(historyDir, PENDING_TASK_FILE),
731667
- JSON.stringify(task, null, 2) + "\n",
731668
- "utf-8"
731669
- );
731812
+ const filePath = join162(historyDir, PENDING_TASK_FILE);
731813
+ const tempPath = `${filePath}.tmp-${process.pid}`;
731814
+ const checkpoint = {
731815
+ ...task,
731816
+ schema: "omnius.pending-task.v2",
731817
+ projectRoot: resolve77(repoRoot)
731818
+ };
731819
+ const payload = JSON.stringify(checkpoint, null, 2) + "\n";
731820
+ writeFileSync85(tempPath, payload, "utf-8");
731821
+ try {
731822
+ renameSync30(tempPath, filePath);
731823
+ } catch {
731824
+ writeFileSync85(filePath, payload, "utf-8");
731825
+ try {
731826
+ unlinkSync37(tempPath);
731827
+ } catch {
731828
+ }
731829
+ }
731670
731830
  }
731671
731831
  function loadPendingTask(repoRoot) {
731672
731832
  const filePath = join162(repoRoot, OMNIUS_DIR2, "history", PENDING_TASK_FILE);
731673
731833
  try {
731674
731834
  if (!existsSync148(filePath)) return null;
731675
731835
  const data = JSON.parse(readFileSync124(filePath, "utf-8"));
731676
- return data;
731836
+ return isValidPendingTask(data, repoRoot) ? data : null;
731677
731837
  } catch {
731678
731838
  return null;
731679
731839
  }
@@ -732047,6 +732207,13 @@ function saveSessionContext(repoRoot, entry) {
732047
732207
  ctx3.entries.push(normalizedEntry);
732048
732208
  }
732049
732209
  }
732210
+ ctx3.entries = ctx3.entries.map((item, index) => ({ item, index })).sort((a2, b) => {
732211
+ const left = Date.parse(a2.item.savedAt || "");
732212
+ const right = Date.parse(b.item.savedAt || "");
732213
+ const leftTime = Number.isFinite(left) ? left : 0;
732214
+ const rightTime = Number.isFinite(right) ? right : 0;
732215
+ return leftTime - rightTime || a2.index - b.index;
732216
+ }).map(({ item }) => item);
732050
732217
  if (ctx3.entries.length > ctx3.maxEntries) {
732051
732218
  ctx3.entries = ctx3.entries.slice(-ctx3.maxEntries);
732052
732219
  }
@@ -732180,80 +732347,12 @@ function isManualSessionEntry(entry) {
732180
732347
  const summary = normalizeSessionText(entry.summary || entry.assistantResponse, 160).toLowerCase();
732181
732348
  return entry.source === "manual" || task === "(manual save)" || entry.toolCalls === 0 && summary.startsWith("manual context save");
732182
732349
  }
732183
- function isDeicticContinuationGoal(goal) {
732184
- const tokens3 = normalizeSessionText(cleanPromptForDiary(goal), 180).toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter(Boolean);
732185
- if (tokens3.length === 0 || tokens3.length > 14) return false;
732186
- const hasResumeVerb = tokens3.some(
732187
- (token) => token === "continue" || token === "resume" || token === "proceed" || token === "complete" || token === "finish"
732188
- );
732189
- if (!hasResumeVerb) return false;
732190
- const deicticCount = tokens3.filter((token) => DEICTIC_CONTINUATION_TERMS.has(token)).length;
732191
- return deicticCount / tokens3.length >= 0.7;
732192
- }
732193
732350
  function readRestoreWorkboard(repoRoot, runId) {
732194
732351
  if (!runId) return null;
732195
732352
  return readJsonOrNull(
732196
732353
  join162(repoRoot, OMNIUS_DIR2, "workboards", runId, "active.json")
732197
732354
  );
732198
732355
  }
732199
- function scoreRestoreLedger(ledger, workboard) {
732200
- const status = ledger.status || "open";
732201
- if (status === "approved") return -1e3;
732202
- const evidence = ledger.evidence ?? [];
732203
- const unresolvedCount = ledger.unresolved?.length ?? 0;
732204
- const activeCards = (workboard?.cards ?? []).filter(
732205
- (card) => card.status === "open" || card.status === "in_progress" || card.status === "needs_changes" || card.status === "blocked"
732206
- ).length;
732207
- const mutationTools = /* @__PURE__ */ new Set(["file_edit", "file_patch", "batch_edit", "file_write"]);
732208
- const mutationEvidence = evidence.filter((item) => mutationTools.has(item.toolName || "")).length;
732209
- const failedMutationEvidence = evidence.filter(
732210
- (item) => mutationTools.has(item.toolName || "") && item.success === false
732211
- ).length;
732212
- const targetPathEvidence = evidence.filter((item) => (item.targetPaths?.length ?? 0) > 0).length;
732213
- const blockedEvidence = evidence.filter((item) => {
732214
- const summary = String(item.summary ?? "").toLowerCase();
732215
- return summary.includes("[focus supervisor block]") || summary.includes("stale") || summary.includes("blocked");
732216
- }).length;
732217
- let score = 0;
732218
- if (status === "open") score += 10;
732219
- else if (status === "incomplete_verification") score += 18;
732220
- else if (status === "request_changes" || status === "blocked") score += 16;
732221
- else score += 4;
732222
- score += Math.min(evidence.length, 20);
732223
- score += Math.min(unresolvedCount, 10) * 4;
732224
- score += Math.min(activeCards, 8) * 3;
732225
- score += Math.min(mutationEvidence, 8) * 5;
732226
- score += Math.min(failedMutationEvidence, 8) * 3;
732227
- score += Math.min(targetPathEvidence, 6) * 2;
732228
- score += Math.min(blockedEvidence, 6) * 2;
732229
- const goal = normalizeSessionText(ledger.goal, 260);
732230
- if (goal.length >= 80) score += 5;
732231
- if (isDeicticContinuationGoal(goal) && mutationEvidence === 0 && unresolvedCount === 0 && activeCards === 0) {
732232
- score -= 25;
732233
- }
732234
- return score;
732235
- }
732236
- function selectActiveTaskAnchor(repoRoot) {
732237
- const ledgerDir = join162(repoRoot, OMNIUS_DIR2, "completion-ledgers");
732238
- try {
732239
- if (!existsSync148(ledgerDir)) return null;
732240
- const candidates = readdirSync48(ledgerDir).filter((name10) => name10.endsWith(".json")).map((name10) => {
732241
- const filePath = join162(ledgerDir, name10);
732242
- const ledger = readJsonOrNull(filePath);
732243
- if (!ledger || !ledger.goal) return null;
732244
- const runId = ledger.runId || name10.replace(/\.json$/, "");
732245
- const workboard = readRestoreWorkboard(repoRoot, runId);
732246
- const score = scoreRestoreLedger(ledger, workboard);
732247
- const mtimeMs = statSync58(filePath).mtimeMs;
732248
- return { ledger: { ...ledger, runId }, workboard, score, mtimeMs };
732249
- }).filter((item) => Boolean(item));
732250
- candidates.sort((a2, b) => b.score - a2.score || b.mtimeMs - a2.mtimeMs);
732251
- const selected = candidates.find((candidate) => candidate.score >= 18);
732252
- return selected ?? null;
732253
- } catch {
732254
- return null;
732255
- }
732256
- }
732257
732356
  function formatActiveTaskAnchor(selected) {
732258
732357
  const ledger = selected.ledger;
732259
732358
  const evidence = ledger.evidence ?? [];
@@ -732285,31 +732384,9 @@ ${evidenceLines.join("\n")}
732285
732384
  ${unresolvedLines.join("\n")}
732286
732385
  ` : "") + (boardCards.length > 0 ? `Active workboard cards:
732287
732386
  ${boardCards.join("\n")}
732288
- ` : "") + `Continuation rule: when the user asks to continue, resume this active task unless the new prompt names a different target.
732387
+ ` : "") + `Continuation rule: this frontier is active only because its exact durable interruption identity was selected.
732289
732388
  </active-task-anchor>`;
732290
732389
  }
732291
- function uniqueSessionIds(ids) {
732292
- const seen = /* @__PURE__ */ new Set();
732293
- const out = [];
732294
- for (const raw of ids) {
732295
- const id2 = String(raw || "").trim();
732296
- if (!id2 || seen.has(id2)) continue;
732297
- seen.add(id2);
732298
- out.push(id2);
732299
- }
732300
- return out;
732301
- }
732302
- function collectRestoreSessionIds(ctx3, activeTask, historySessionId) {
732303
- const entries2 = ctx3?.entries ?? [];
732304
- const usefulEntries = entries2.filter((entry) => !isManualSessionEntry(entry));
732305
- const chronological = usefulEntries.length > 0 ? usefulEntries : entries2;
732306
- return uniqueSessionIds([
732307
- activeTask?.ledger.runId,
732308
- ...[...chronological].reverse().map((entry) => entry.sessionId),
732309
- ...[...entries2].reverse().map((entry) => entry.sessionId),
732310
- historySessionId
732311
- ]);
732312
- }
732313
732390
  function childMapForTodos(todos) {
732314
732391
  const byId = new Set(todos.map((todo) => todo.id));
732315
732392
  const byParent = /* @__PURE__ */ new Map();
@@ -732382,20 +732459,8 @@ ${treeLines.join("\n")}${omitted > 0 ? `
732382
732459
  Continuation contract: this is the live checklist from the restored run. Continue it and update it with todo_write; do not recreate a parallel plan unless the new user request explicitly changes direction.
732383
732460
  </restored-todo-state>`;
732384
732461
  }
732385
- function findRestoredTodoState(sessionIds) {
732386
- for (const sessionId of sessionIds) {
732387
- const todos = readTodos(sessionId).filter((todo) => todo && todo.id && todo.content);
732388
- if (todos.length === 0) continue;
732389
- return {
732390
- sessionId,
732391
- todos,
732392
- block: buildRestoredTodoBlock(sessionId, todos)
732393
- };
732394
- }
732395
- return null;
732396
- }
732397
- function buildRestoreHistoryAnchor(repoRoot) {
732398
- const [latest] = listSessions(repoRoot);
732462
+ function buildRestoreHistoryAnchor(repoRoot, exactSessionId) {
732463
+ const latest = exactSessionId ? listSessions(repoRoot).find((entry) => entry.id === exactSessionId) : listSessions(repoRoot)[0];
732399
732464
  if (!latest?.id) return null;
732400
732465
  const lines = loadSessionHistory(repoRoot, latest.id) ?? [];
732401
732466
  const recordedLineCount = lines.filter((line) => String(line ?? "").trim()).length;
@@ -732411,6 +732476,76 @@ Recall contract: do not replay or ingest the transcript wholesale. Use the struc
732411
732476
  </restored-interface-history>`;
732412
732477
  return { sessionId: latest.id, path: path16, lineCount: recordedLineCount, block };
732413
732478
  }
732479
+ function buildExactContinuationSnapshot(repoRoot, pendingTask) {
732480
+ const identity3 = pendingTask.interruption;
732481
+ if (!identity3) return null;
732482
+ if (pendingTask.projectRoot && resolve77(pendingTask.projectRoot) !== resolve77(repoRoot)) {
732483
+ return null;
732484
+ }
732485
+ const ledgerPath2 = join162(
732486
+ repoRoot,
732487
+ OMNIUS_DIR2,
732488
+ "completion-ledgers",
732489
+ `${identity3.runId}.json`
732490
+ );
732491
+ const rawLedger = readJsonOrNull(ledgerPath2);
732492
+ const ledger = rawLedger && (!rawLedger.runId || rawLedger.runId === identity3.runId) && (!rawLedger.taskEpoch || rawLedger.taskEpoch === identity3.taskEpoch) ? { ...rawLedger, runId: identity3.runId } : null;
732493
+ const rawWorkboard = readRestoreWorkboard(repoRoot, identity3.runId);
732494
+ const workboard = rawWorkboard && (!rawWorkboard.runId || rawWorkboard.runId === identity3.runId) && (!rawWorkboard.taskEpoch || rawWorkboard.taskEpoch === identity3.taskEpoch) ? rawWorkboard : null;
732495
+ const activeTaskAnchor = ledger ? formatActiveTaskAnchor({ ledger, workboard }) : null;
732496
+ const exactTodos = readTodos(identity3.sessionId).filter(
732497
+ (todo) => todo && todo.id && todo.content
732498
+ );
732499
+ const restoredTodos = exactTodos.length > 0 ? {
732500
+ sessionId: identity3.sessionId,
732501
+ todos: exactTodos,
732502
+ block: buildRestoredTodoBlock(identity3.sessionId, exactTodos)
732503
+ } : null;
732504
+ const historyAnchor = buildRestoreHistoryAnchor(repoRoot, identity3.sessionId);
732505
+ const contextEntries = (loadSessionContext(repoRoot)?.entries ?? []).filter((entry) => entry.sessionId === identity3.sessionId).sort((a2, b) => a2.savedAt.localeCompare(b.savedAt)).slice(-4);
732506
+ const contextLines = contextEntries.map((entry) => {
732507
+ const status = entry.completed ? "done" : "partial";
732508
+ const outcome = normalizeSessionText(
732509
+ entry.assistantResponse || entry.summary || entry.task,
732510
+ 220
732511
+ );
732512
+ return `- [${status}] ${outcome}`;
732513
+ });
732514
+ const progressLines = [
732515
+ `<exact-continuation schema="omnius.exact-continuation.v1">`,
732516
+ `project_root=${resolve77(repoRoot)}`,
732517
+ `session_id=${identity3.sessionId}`,
732518
+ `run_id=${identity3.runId}`,
732519
+ `task_epoch=${identity3.taskEpoch}`,
732520
+ `owner_generation=${identity3.ownerGeneration}`,
732521
+ `original_user_goal_json=${JSON.stringify(pendingTask.prompt)}`,
732522
+ pendingTask.progressSummary ? `checkpoint_progress=${normalizeSessionText(pendingTask.progressSummary, 500)}` : null,
732523
+ pendingTask.filesModified.length > 0 ? `checkpoint_files=${pendingTask.filesModified.slice(0, 20).map((item) => normalizeSessionText(item, 160)).join(", ")}` : null,
732524
+ "Authority contract: continue only this interrupted run. Treat all other project history as historical orientation, not an active frontier.",
732525
+ activeTaskAnchor,
732526
+ contextLines.length > 0 ? `Same-session chronology:
732527
+ ${contextLines.join("\n")}` : null,
732528
+ restoredTodos?.block,
732529
+ historyAnchor?.block,
732530
+ `</exact-continuation>`
732531
+ ].filter((line) => Boolean(line));
732532
+ return {
732533
+ prompt: progressLines.join("\n"),
732534
+ sourceSessionId: identity3.sessionId,
732535
+ todoSessionId: restoredTodos?.sessionId,
732536
+ todoCount: restoredTodos?.todos.length ?? 0,
732537
+ historySessionId: historyAnchor?.sessionId,
732538
+ historyPath: historyAnchor?.path,
732539
+ historyLineCount: historyAnchor?.lineCount,
732540
+ selection: {
732541
+ kind: "exact_interruption",
732542
+ sessionId: identity3.sessionId,
732543
+ runId: identity3.runId,
732544
+ taskEpoch: identity3.taskEpoch,
732545
+ ownerGeneration: identity3.ownerGeneration
732546
+ }
732547
+ };
732548
+ }
732414
732549
  function appendRestoreBlocks(base3, blocks) {
732415
732550
  return [base3, ...blocks].filter((part) => part && part.trim()).join("\n\n");
732416
732551
  }
@@ -732441,53 +732576,24 @@ function sessionContextToHistoryBoxData(ctx3, title = "Session History") {
732441
732576
  }
732442
732577
  function buildContextRestoreSnapshot(repoRoot) {
732443
732578
  const ctx3 = loadSessionContext(repoRoot);
732444
- const handoffPrompt = buildHandoffPrompt(repoRoot);
732445
- const activeTask = selectActiveTaskAnchor(repoRoot);
732446
- const activeTaskAnchor = activeTask ? formatActiveTaskAnchor(activeTask) : null;
732447
732579
  const historyAnchor = buildRestoreHistoryAnchor(repoRoot);
732448
- const restoreSessionIds = collectRestoreSessionIds(ctx3, activeTask, historyAnchor?.sessionId);
732449
- const restoredTodos = findRestoredTodoState(restoreSessionIds);
732450
- const sourceSessionId = restoredTodos?.sessionId ?? restoreSessionIds[0];
732451
- if (handoffPrompt) {
732452
- const safeHandoffPrompt = stripModelContextNoise(handoffPrompt);
732453
- const usefulEntries2 = (ctx3?.entries ?? []).filter((entry) => !isManualSessionEntry(entry));
732454
- const baseCtx = ctx3 && ctx3.entries.length > 0 ? `
732455
-
732456
- <session-recap>
732457
- Recent tasks: ${(usefulEntries2.length > 0 ? usefulEntries2 : ctx3.entries).slice(-3).map(
732458
- (e2) => `[${e2.completed ? "done" : "partial"}] ${normalizeSessionText(e2.summary || e2.task, 80)}`
732459
- ).join(", ")}
732460
- </session-recap>` : "";
732461
- const prompt2 = appendRestoreBlocks(
732462
- safeHandoffPrompt + (activeTaskAnchor ? `
732463
-
732464
- ${activeTaskAnchor}` : "") + baseCtx,
732465
- [restoredTodos?.block, historyAnchor?.block]
732466
- );
732467
- return {
732468
- prompt: prompt2,
732469
- sourceSessionId,
732470
- todoSessionId: restoredTodos?.sessionId,
732471
- todoCount: restoredTodos?.todos.length ?? 0,
732472
- historySessionId: historyAnchor?.sessionId,
732473
- historyPath: historyAnchor?.path,
732474
- historyLineCount: historyAnchor?.lineCount
732475
- };
732476
- }
732580
+ const usefulEntries = (ctx3?.entries ?? []).filter(
732581
+ (entry) => !isManualSessionEntry(entry)
732582
+ );
732583
+ const chronological = usefulEntries.length > 0 ? usefulEntries : ctx3?.entries ?? [];
732584
+ const sourceSessionId = chronological.at(-1)?.sessionId ?? historyAnchor?.sessionId;
732477
732585
  if (!ctx3 || ctx3.entries.length === 0) {
732478
- const prompt2 = appendRestoreBlocks(activeTaskAnchor ?? "", [restoredTodos?.block, historyAnchor?.block]);
732586
+ const prompt2 = appendRestoreBlocks("", [historyAnchor?.block]);
732479
732587
  if (!prompt2.trim()) return null;
732480
732588
  return {
732481
732589
  prompt: prompt2,
732482
732590
  sourceSessionId,
732483
- todoSessionId: restoredTodos?.sessionId,
732484
- todoCount: restoredTodos?.todos.length ?? 0,
732591
+ todoCount: 0,
732485
732592
  historySessionId: historyAnchor?.sessionId,
732486
732593
  historyPath: historyAnchor?.path,
732487
732594
  historyLineCount: historyAnchor?.lineCount
732488
732595
  };
732489
732596
  }
732490
- const usefulEntries = ctx3.entries.filter((entry) => !isManualSessionEntry(entry));
732491
732597
  const recent = (usefulEntries.length > 0 ? usefulEntries : ctx3.entries).slice(-20);
732492
732598
  const chronology = recent.map((e2) => {
732493
732599
  const status = e2.completed ? "done" : "partial";
@@ -732511,9 +732617,7 @@ Provenance: ${last2.provenance} (file_read to expand)` : "";
732511
732617
  KG summary: .omnius/context/kg-summary/latest.md (file_read to expand; legacy pointer: .omnius/context/kg-summary-latest.md)`;
732512
732618
  const prompt = appendRestoreBlocks(
732513
732619
  `<session-recap>
732514
- ` + (activeTaskAnchor ? `${activeTaskAnchor}
732515
-
732516
- ` : "") + `Project chronology (older to newer):
732620
+ Project chronology (older to newer):
732517
732621
  Scope: full retained rolling window (${recent.length} entr${recent.length === 1 ? "y" : "ies"}).
732518
732622
  ${chronology.join("\n")}
732519
732623
  ` + (latestCompleted ? `
@@ -732521,16 +732625,14 @@ ${latestCompleted}
732521
732625
  ` : "\n") + `
732522
732626
  Recent structured task state (older to newer; transcript omitted from model context):
732523
732627
  ${recentTaskState}
732524
- ` + (activeTaskAnchor ? `For continuation prompts, resume the active task anchor above; use chronology only to avoid repeating completed work.${prov}${kg}
732525
- ` : `Continue from the latest exchange and do not repeat completed work.${prov}${kg}
732526
- `) + `</session-recap>`,
732527
- [restoredTodos?.block, historyAnchor?.block]
732628
+ This chronology is historical orientation only. It does not select an active task, todo tree, or run. Use an exact interruption checkpoint or an explicit lifecycle command for continuation authority.${prov}${kg}
732629
+ </session-recap>`,
732630
+ [historyAnchor?.block]
732528
732631
  );
732529
732632
  return {
732530
732633
  prompt,
732531
732634
  sourceSessionId,
732532
- todoSessionId: restoredTodos?.sessionId,
732533
- todoCount: restoredTodos?.todos.length ?? 0,
732635
+ todoCount: 0,
732534
732636
  historySessionId: historyAnchor?.sessionId,
732535
732637
  historyPath: historyAnchor?.path,
732536
732638
  historyLineCount: historyAnchor?.lineCount
@@ -732982,7 +733084,7 @@ function deleteUsageRecord(kind, value2, repoRoot) {
732982
733084
  remove(join162(repoRoot, OMNIUS_DIR2, USAGE_HISTORY_FILE));
732983
733085
  }
732984
733086
  }
732985
- var OMNIUS_DIR2, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, COMPACTION_AUDIT_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, MODEL_CONTEXT_NOISE_LINE_RE, DEFAULT_RESTORED_SESSION_HISTORY_MAX_LINES, DEICTIC_CONTINUATION_TERMS, SESSIONS_DIR, SESSIONS_INDEX, TUI_STATE_SUFFIX, AUTHORED_SESSION_LINE, VISUAL_CHROME_LINE, SESSION_TITLE_STATUS_LINE, SKIP_DIRS2, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
733087
+ var OMNIUS_DIR2, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, COMPACTION_AUDIT_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, MODEL_CONTEXT_NOISE_LINE_RE, DEFAULT_RESTORED_SESSION_HISTORY_MAX_LINES, SESSIONS_DIR, SESSIONS_INDEX, TUI_STATE_SUFFIX, AUTHORED_SESSION_LINE, VISUAL_CHROME_LINE, SESSION_TITLE_STATUS_LINE, SKIP_DIRS2, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
732986
733088
  var init_omnius_directory = __esm({
732987
733089
  "packages/cli/src/tui/omnius-directory.ts"() {
732988
733090
  init_dist5();
@@ -733019,34 +733121,6 @@ var init_omnius_directory = __esm({
733019
733121
  LOCK_RETRY_MAX = 100;
733020
733122
  MODEL_CONTEXT_NOISE_LINE_RE = /^\s*(?:🔊|E Task timeout reached|E Incomplete:|⚠ Task incomplete|Tokens:\s*[\d,]+|▹\s*continue\b|·\s*(?:Starting fresh|Context (?:auto-)?restored|Nexus P2P network connected|REST API:|Voice feedback enabled|Memory maintenance:|Memory:|reclaimed\s+(?:\d|space\b)|physical footprint:)|.*\bSHELL TRANSCRIPT\b.*).*$/i;
733021
733123
  DEFAULT_RESTORED_SESSION_HISTORY_MAX_LINES = 600;
733022
- DEICTIC_CONTINUATION_TERMS = /* @__PURE__ */ new Set([
733023
- "again",
733024
- "and",
733025
- "back",
733026
- "carry",
733027
- "complete",
733028
- "continue",
733029
- "finish",
733030
- "from",
733031
- "last",
733032
- "latest",
733033
- "left",
733034
- "my",
733035
- "off",
733036
- "please",
733037
- "previous",
733038
- "prior",
733039
- "proceed",
733040
- "request",
733041
- "resume",
733042
- "task",
733043
- "that",
733044
- "the",
733045
- "this",
733046
- "to",
733047
- "try",
733048
- "work"
733049
- ]);
733050
733124
  SESSIONS_DIR = "sessions";
733051
733125
  SESSIONS_INDEX = "sessions-index.json";
733052
733126
  TUI_STATE_SUFFIX = ".tui-state.json";
@@ -767195,7 +767269,7 @@ sleep 1
767195
767269
  }
767196
767270
  const paused = ctx3.pauseTask?.() ?? false;
767197
767271
  if (interruptionAccepted(paused)) {
767198
- ctx3.savePendingTaskState?.();
767272
+ ctx3.savePendingTaskState?.({ reason: "pause", resumeOnStartup: false });
767199
767273
  renderInfo(interruptionMessage(paused, "Task paused."));
767200
767274
  } else {
767201
767275
  renderWarning(interruptionMessage(paused, "Could not pause the task."));
@@ -777335,7 +777409,7 @@ async function handleUpdate(subcommand, ctx3) {
777335
777409
  `Updated Omnius v${currentVersion} → v${terminal.installed_version ?? info.latestVersion}; daemon v${terminal.daemon_version ?? info.latestVersion}${terminal.tray_was_running ? ", indicator restarted" : ""}.`
777336
777410
  );
777337
777411
  ctx3.contextSave?.();
777338
- ctx3.savePendingTaskState?.();
777412
+ ctx3.savePendingTaskState?.({ reason: "update", resumeOnStartup: true });
777339
777413
  if (ctx3.hasActiveTask?.()) ctx3.abortActiveTask?.({ preserveResume: true });
777340
777414
  ctx3.killEphemeral?.({ preserveInfrastructure: true });
777341
777415
  process.exit(120);
@@ -777924,7 +777998,7 @@ async function handleUpdate(subcommand, ctx3) {
777924
777998
  !daemonUpgrade.ok ? `Daemon still at ${daemonUpgrade.observedVersion ?? "unknown"} — restarting client anyway` : daemonUpgrade.action === "restarted" ? `Daemon upgraded to ${expectedDaemonVersion}` : `Daemon verified at ${expectedDaemonVersion}`
777925
777999
  );
777926
778000
  ctx3.contextSave?.();
777927
- const hadActiveTask = ctx3.savePendingTaskState?.() ?? false;
778001
+ const hadActiveTask = ctx3.savePendingTaskState?.({ reason: "update", resumeOnStartup: true }) ?? false;
777928
778002
  const resumeFlag = hadActiveTask ? "1" : "update-only";
777929
778003
  installOverlay.stop("Restarting with new version...");
777930
778004
  await new Promise((r2) => setTimeout(r2, 1500));
@@ -803546,13 +803620,12 @@ function normalizeTelegramCallbackQuery(update2) {
803546
803620
  };
803547
803621
  }
803548
803622
  function adaptTool5(tool, todoSessionId, progress) {
803623
+ if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803624
+ tool.bindExecutionScope?.({ sessionId: todoSessionId });
803625
+ }
803549
803626
  return adaptExecutionTool(tool, {
803550
803627
  onProgress: (toolName, event) => progress?.onProgress(toolName, event),
803551
- execute: async (_tool, args, invoke) => {
803552
- const previousTodoSession = todoSessionId ? getTodoSessionId() : "";
803553
- if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803554
- setTodoSessionId(todoSessionId);
803555
- }
803628
+ execute: async (_tool, _args, invoke) => {
803556
803629
  try {
803557
803630
  const result = await invoke();
803558
803631
  progress?.complete(tool.name, result);
@@ -803564,10 +803637,6 @@ function adaptTool5(tool, todoSessionId, progress) {
803564
803637
  error: err instanceof Error ? err.message : String(err)
803565
803638
  });
803566
803639
  throw err;
803567
- } finally {
803568
- if (todoSessionId && (tool.name === "todo_write" || tool.name === "todo_read")) {
803569
- setTodoSessionId(previousTodoSession);
803570
- }
803571
803640
  }
803572
803641
  }
803573
803642
  });
@@ -832606,9 +832675,6 @@ async function runSelfImprovementCycle(repoRoot) {
832606
832675
  } catch {
832607
832676
  }
832608
832677
  }
832609
- function isExplicitRestoredTaskContinuation(input) {
832610
- return /^(?:continue|resume|pick\s+up|carry\s+on)\b/i.test(input.trim());
832611
- }
832612
832678
  function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled, sessionMetrics, realtimeEnabled, restoredRunSessionId, restoredOrientation, actualUserGoal, actionTreeStore, resumeInterrupted = false) {
832613
832679
  const voiceStyleMap = {
832614
832680
  concise: 1,
@@ -833002,6 +833068,9 @@ Only tools allowed by this profile are visible and executable.`
833002
833068
  sessionId,
833003
833069
  interruptionOwnerId: `tui:${sessionId}`,
833004
833070
  resumeInterrupted,
833071
+ // A caller-supplied restore projection is the sole prior-context source.
833072
+ // Do not append the global singleton handoff beside it.
833073
+ skipCrossTaskHandoff: Boolean(restoredOrientation) || resumeInterrupted,
833005
833074
  maxTurns: realtimeEnabled ? Math.min(effectiveMaxTurns, 8) : effectiveMaxTurns,
833006
833075
  maxTokens: realtimeEnabled ? 512 : 16384,
833007
833076
  temperature: realtimeEnabled ? 0.6 : 0,
@@ -834512,7 +834581,8 @@ ${entry.fullContent}`
834512
834581
  const systemContext = [
834513
834582
  `Working directory: ${repoRoot}`,
834514
834583
  emotionContext,
834515
- restoredOrientation ? `[RESTORED ORIENTATIONhistorical context, not a user instruction]
834584
+ restoredOrientation ? resumeInterrupted ? `[EXACT INTERRUPTED RUN durable lifecycle authority]
834585
+ ${restoredOrientation.slice(0, 4e3)}` : `[RESTORED ORIENTATION — historical context, not a user instruction]
834516
834586
  ${sanitizeRestorePromptForModel(restoredOrientation).slice(0, 4e3)}` : ""
834517
834587
  ].filter(Boolean).join("\n\n");
834518
834588
  resetNarrationContext();
@@ -835134,7 +835204,8 @@ async function startInteractive(config, repoPath2) {
835134
835204
  }
835135
835205
  const resumeFlag = process.env.__OMNIUS_RESUMED ?? "";
835136
835206
  const isResumed = resumeFlag !== "";
835137
- const hasTaskToResume = resumeFlag === "1";
835207
+ const startupPendingTask = loadPendingTask(repoRoot);
835208
+ const hasTaskToResume = resumeFlag === "1" || startupPendingTask?.resumeOnStartup === true;
835138
835209
  delete process.env.__OMNIUS_RESUMED;
835139
835210
  if (isResumed && process.stdout.isTTY) {
835140
835211
  process.stdout.write(
@@ -835196,10 +835267,44 @@ async function startInteractive(config, repoPath2) {
835196
835267
  let restoredTaskSessionId = null;
835197
835268
  let resumeInterruptedTask = false;
835198
835269
  let pendingTaskResumeArmed = false;
835270
+ let armedPendingTask = null;
835199
835271
  let saveVisualSessionSnapshotRef = null;
835200
835272
  let terminalRestoredForExit = false;
835201
835273
  let interactiveExiting = false;
835202
835274
  let idleMemoryMaintenance = null;
835275
+ const persistActiveTaskForRestart = (exitReason) => {
835276
+ if (!activeTask || !lastSubmittedPrompt) return false;
835277
+ if (!activeTask.runner.isPaused) {
835278
+ const receipt2 = activeTask.runner.pause();
835279
+ if (receipt2?.accepted === false) return false;
835280
+ }
835281
+ const interruption = pendingInterruptionIdentity(activeTask);
835282
+ if (!interruption) return false;
835283
+ savePendingTask(repoRoot, {
835284
+ prompt: lastSubmittedPrompt,
835285
+ progressSummary: `Task paused for ${exitReason}. ${activeTask.toolCallCount} tool calls completed.`,
835286
+ filesModified: Array.from(activeTask.filesTouched),
835287
+ bruteForce: bruteForceEnabled,
835288
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
835289
+ toolCallCount: activeTask.toolCallCount,
835290
+ exitReason,
835291
+ resumeOnStartup: true,
835292
+ interruption
835293
+ });
835294
+ return true;
835295
+ };
835296
+ const armPendingTaskResume = (pendingTask) => {
835297
+ restoredTaskSessionId = pendingTask.interruption?.sessionId ?? null;
835298
+ resumeInterruptedTask = Boolean(pendingTask.interruption);
835299
+ pendingTaskResumeArmed = true;
835300
+ armedPendingTask = pendingTask;
835301
+ const exactSnapshot = buildExactContinuationSnapshot(repoRoot, pendingTask);
835302
+ restoredSessionContext = exactSnapshot?.prompt ?? [
835303
+ `Original user goal: ${pendingTask.prompt}`,
835304
+ pendingTask.progressSummary,
835305
+ pendingTask.filesModified.length > 0 ? `Modified: ${pendingTask.filesModified.slice(0, 20).join(", ")}` : ""
835306
+ ].filter(Boolean).join("\n");
835307
+ };
835203
835308
  const restoreTerminalForExit = () => {
835204
835309
  if (!process.stdout.isTTY || terminalRestoredForExit) return;
835205
835310
  terminalRestoredForExit = true;
@@ -835231,18 +835336,7 @@ async function startInteractive(config, repoPath2) {
835231
835336
  }
835232
835337
  if (activeTask) {
835233
835338
  if (interruption === "pause") {
835234
- const receipt2 = activeTask.runner.pause();
835235
- if (receipt2?.accepted !== false && lastSubmittedPrompt) {
835236
- savePendingTask(repoRoot, {
835237
- prompt: lastSubmittedPrompt,
835238
- progressSummary: `Task paused during process shutdown. ${activeTask.toolCallCount} tool calls completed.`,
835239
- filesModified: Array.from(activeTask.filesTouched),
835240
- bruteForce: bruteForceEnabled,
835241
- savedAt: (/* @__PURE__ */ new Date()).toISOString(),
835242
- toolCallCount: activeTask.toolCallCount,
835243
- interruption: pendingInterruptionIdentity(activeTask)
835244
- });
835245
- }
835339
+ persistActiveTaskForRestart("shutdown");
835246
835340
  } else {
835247
835341
  activeTask.runner.abort();
835248
835342
  discardPendingTask(repoRoot);
@@ -837089,9 +837183,9 @@ This is an independent background session started from /background.`
837089
837183
  if (options2.injectNextTask !== false) {
837090
837184
  restoredSessionContext = snapshot.prompt;
837091
837185
  }
837092
- const restoredId = snapshot.todoSessionId || snapshot.sourceSessionId || null;
837093
- restoredTaskSessionId = restoredId;
837094
- if (restoredId) {
837186
+ const restoredId = options2.bindTaskIdentity && snapshot.selection ? snapshot.selection.sessionId : null;
837187
+ if (restoredId && snapshot.selection?.kind === "exact_interruption") {
837188
+ restoredTaskSessionId = restoredId;
837095
837189
  try {
837096
837190
  process.env["OMNIUS_SESSION_ID"] = restoredId;
837097
837191
  } catch {
@@ -838195,8 +838289,14 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
838195
838289
  } catch {
838196
838290
  }
838197
838291
  },
838198
- savePendingTaskState() {
838292
+ savePendingTaskState(options2) {
838199
838293
  if (lastSubmittedPrompt && activeTask) {
838294
+ if (!activeTask.runner.isPaused) {
838295
+ const receipt2 = activeTask.runner.pause();
838296
+ if (receipt2?.accepted === false) return false;
838297
+ }
838298
+ const interruption = pendingInterruptionIdentity(activeTask);
838299
+ if (!interruption) return false;
838200
838300
  const activeToolCallCount = activeTask.toolCallCount;
838201
838301
  const activeFilesTouched = Array.from(activeTask.filesTouched);
838202
838302
  savePendingTask(repoRoot, {
@@ -838206,7 +838306,9 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
838206
838306
  bruteForce: bruteForceEnabled,
838207
838307
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
838208
838308
  toolCallCount: activeToolCallCount,
838209
- interruption: pendingInterruptionIdentity(activeTask)
838309
+ exitReason: options2?.reason ?? "pause",
838310
+ resumeOnStartup: options2?.resumeOnStartup === true,
838311
+ interruption
838210
838312
  });
838211
838313
  return true;
838212
838314
  }
@@ -839881,19 +839983,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
839881
839983
  resumeTask() {
839882
839984
  const pendingTask = loadPendingTask(repoRoot);
839883
839985
  if (!pendingTask) return false;
839884
- restoredTaskSessionId = pendingTask.interruption?.sessionId ?? null;
839885
- resumeInterruptedTask = Boolean(pendingTask.interruption);
839886
- pendingTaskResumeArmed = true;
839986
+ armPendingTaskResume(pendingTask);
839887
839987
  setTimeout(() => {
839888
- const resumeContext = [
839889
- `Continuing previous task: ${pendingTask.prompt}`,
839890
- pendingTask.progressSummary ? `Progress: ${pendingTask.progressSummary}` : "",
839891
- pendingTask.filesModified.length > 0 ? `Modified: ${pendingTask.filesModified.slice(0, 5).join(", ")}` : "",
839892
- "Continue where you left off."
839893
- ].filter(Boolean).join("\n");
839894
839988
  const shortPrompt = pendingTask.prompt.slice(0, 40) + (pendingTask.prompt.length > 40 ? "..." : "");
839895
839989
  writeContent(() => renderInfo(`Picking up: ${shortPrompt}`));
839896
- rl.emit("line", resumeContext);
839990
+ rl.emit("line", pendingTask.prompt);
839897
839991
  }, 100);
839898
839992
  return true;
839899
839993
  },
@@ -840151,7 +840245,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
840151
840245
  }
840152
840246
  statusBar.ensureMonitorTimer();
840153
840247
  showPrompt();
840154
- if (!isResumed) {
840248
+ if (!isResumed && !hasTaskToResume) {
840155
840249
  const savedCtx = loadSessionContext(repoRoot);
840156
840250
  if (savedCtx && savedCtx.entries.length > 0) {
840157
840251
  const lastEntry = savedCtx.entries[savedCtx.entries.length - 1];
@@ -840277,30 +840371,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
840277
840371
  }
840278
840372
  startReminderDispatcher();
840279
840373
  if (hasTaskToResume) {
840280
- const pendingTask = loadPendingTask(repoRoot);
840374
+ const pendingTask = startupPendingTask ?? loadPendingTask(repoRoot);
840281
840375
  if (pendingTask) {
840282
- restoredTaskSessionId = pendingTask.interruption?.sessionId ?? null;
840283
- resumeInterruptedTask = Boolean(pendingTask.interruption);
840284
- pendingTaskResumeArmed = true;
840376
+ armPendingTaskResume(pendingTask);
840285
840377
  setTimeout(() => {
840286
- const restoreSnapshot = buildContextRestoreSnapshot(repoRoot);
840287
- if (restoreSnapshot) {
840288
- applyRestoreSnapshot(restoreSnapshot, {
840289
- replayVisual: false,
840290
- injectNextTask: false
840291
- });
840292
- }
840293
- const sessionCtx = restoreSnapshot?.prompt ?? buildContextRestorePrompt(repoRoot);
840294
- const resumeContext = [
840295
- sessionCtx || "",
840296
- `Continuing task after update: ${pendingTask.prompt}`,
840297
- pendingTask.progressSummary ? `Progress: ${pendingTask.progressSummary}` : "",
840298
- pendingTask.filesModified.length > 0 ? `Modified: ${pendingTask.filesModified.slice(0, 5).join(", ")}` : "",
840299
- "Continue where you left off."
840300
- ].filter(Boolean).join("\n");
840301
840378
  const shortPrompt = pendingTask.prompt.slice(0, 40) + (pendingTask.prompt.length > 40 ? "..." : "");
840302
840379
  writeContent(() => renderInfo(`Picking up: ${shortPrompt}`));
840303
- rl.emit("line", resumeContext);
840380
+ rl.emit("line", pendingTask.prompt);
840304
840381
  }, 100);
840305
840382
  }
840306
840383
  }
@@ -840492,10 +840569,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
840492
840569
  if (quitMatch === "quit" || quitMatch === "exit" || quitMatch === "q") {
840493
840570
  interactiveExiting = true;
840494
840571
  saveVisualSessionSnapshot();
840495
- if (activeTask) {
840496
- activeTask.runner.abort();
840497
- discardPendingTask(repoRoot);
840498
- }
840572
+ if (activeTask) persistActiveTaskForRestart("quit");
840499
840573
  idleMemoryMaintenance?.stop();
840500
840574
  taskManager.stopAll();
840501
840575
  if (blessEngine?.isActive) blessEngine.stop();
@@ -840513,10 +840587,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
840513
840587
  if (cmdResult === "exit") {
840514
840588
  interactiveExiting = true;
840515
840589
  saveVisualSessionSnapshot();
840516
- if (activeTask) {
840517
- activeTask.runner.abort();
840518
- discardPendingTask(repoRoot);
840519
- }
840590
+ if (activeTask) persistActiveTaskForRestart("quit");
840520
840591
  idleMemoryMaintenance?.stop();
840521
840592
  taskManager.stopAll();
840522
840593
  if (telegramBridge) await telegramBridge.stopAndWait();
@@ -841078,6 +841149,10 @@ ${formatTaskCompletionMeta(previousMetaForIntake)}`
841078
841149
  );
841079
841150
  }
841080
841151
  if (promptRecallCancelled) return;
841152
+ const pendingTaskForAdmission = pendingTaskResumeArmed ? armedPendingTask : null;
841153
+ if (pendingTaskForAdmission) {
841154
+ fullInput = pendingTaskForAdmission.prompt;
841155
+ }
841081
841156
  lastSubmittedPrompt = fullInput;
841082
841157
  const taskPreview = fullInput.length > 100 ? fullInput.slice(0, 100) + "..." : fullInput;
841083
841158
  emotionEngine.setCurrentTask(taskPreview);
@@ -841094,18 +841169,19 @@ ${formatTaskCompletionMeta(previousMetaForIntake)}`
841094
841169
  } catch {
841095
841170
  }
841096
841171
  let taskInput = fullInput;
841097
- const reuseRestoredTaskSession = Boolean(restoredTaskSessionId) && (resumeInterruptedTask || isExplicitRestoredTaskContinuation(fullInput));
841172
+ const reuseRestoredTaskSession = Boolean(restoredTaskSessionId) && resumeInterruptedTask && Boolean(pendingTaskForAdmission?.interruption);
841098
841173
  let taskSessionId = reuseRestoredTaskSession ? restoredTaskSessionId : null;
841099
841174
  const adoptInterruptedGeneration = reuseRestoredTaskSession && resumeInterruptedTask;
841100
841175
  const consumePendingTaskAfterAdmission = pendingTaskResumeArmed;
841101
841176
  let restoredOrientation = null;
841102
841177
  if (restoredSessionContext) {
841103
- restoredOrientation = sanitizeRestorePromptForModel(restoredSessionContext);
841178
+ restoredOrientation = pendingTaskForAdmission ? restoredSessionContext : sanitizeRestorePromptForModel(restoredSessionContext);
841104
841179
  restoredSessionContext = null;
841105
841180
  }
841106
841181
  restoredTaskSessionId = null;
841107
841182
  resumeInterruptedTask = false;
841108
841183
  pendingTaskResumeArmed = false;
841184
+ armedPendingTask = null;
841109
841185
  if (taskIntakePacket) {
841110
841186
  taskInput = `${taskIntakePacket}
841111
841187
 
@@ -841162,14 +841238,23 @@ ${taskInput}`;
841162
841238
  realtimeEnabled,
841163
841239
  taskSessionId,
841164
841240
  restoredOrientation,
841165
- fullInput,
841241
+ pendingTaskForAdmission?.prompt ?? fullInput,
841166
841242
  idleActionTree,
841167
841243
  adoptInterruptedGeneration
841168
841244
  );
841169
841245
  activeTask = task;
841170
841246
  if (consumePendingTaskAfterAdmission) {
841171
841247
  const adopted = task.runner.interruptionState;
841172
- if (!adoptInterruptedGeneration || adopted?.phase === "running" && adopted.sessionId === taskSessionId) {
841248
+ if (pendingTaskForAdmission && !pendingTaskForAdmission.interruption || pendingTaskForAdmission && pendingTaskMatchesRunningInterruption(
841249
+ pendingTaskForAdmission,
841250
+ adopted ? {
841251
+ sessionId: adopted.sessionId,
841252
+ runId: adopted.runId,
841253
+ taskEpoch: adopted.taskEpoch,
841254
+ ownerGeneration: adopted.owner.generation,
841255
+ phase: adopted.phase
841256
+ } : null
841257
+ )) {
841173
841258
  discardPendingTask(repoRoot);
841174
841259
  }
841175
841260
  }
@@ -841227,6 +841312,21 @@ ${taskInput}`;
841227
841312
  scheduleAgentSNREval(lastSubmittedPrompt);
841228
841313
  }
841229
841314
  if (activeTask) {
841315
+ if (pendingTaskForAdmission?.interruption) {
841316
+ const state5 = activeTask.runner.interruptionState;
841317
+ if (state5 && state5.phase !== "running" && state5.phase !== "pausing" && state5.phase !== "paused" && state5.phase !== "resuming" && pendingTaskMatchesInterruptionIdentity(
841318
+ pendingTaskForAdmission,
841319
+ {
841320
+ sessionId: state5.sessionId,
841321
+ runId: state5.runId,
841322
+ taskEpoch: state5.taskEpoch,
841323
+ ownerGeneration: state5.owner.generation,
841324
+ phase: state5.phase
841325
+ }
841326
+ )) {
841327
+ discardPendingTask(repoRoot);
841328
+ }
841329
+ }
841230
841330
  sessionFilesTouched = Array.from(activeTask.filesTouched);
841231
841331
  sessionToolCallCount = activeTask.toolCallCount;
841232
841332
  sessionAvailableTools = Array.from(activeTask.availableTools);
@@ -841506,7 +841606,7 @@ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.cat
841506
841606
  peerMesh = null;
841507
841607
  inferenceRouter = null;
841508
841608
  }
841509
- if (activeTask) activeTask.runner.abort();
841609
+ if (activeTask) persistActiveTaskForRestart("shutdown");
841510
841610
  taskManager.stopAll();
841511
841611
  killAllFullSubAgents();
841512
841612
  idleMemoryMaintenance?.stop();
@@ -841600,6 +841700,8 @@ ${c3.dim("(Use /quit to exit)")}
841600
841700
  bruteForce: bruteForceEnabled,
841601
841701
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
841602
841702
  toolCallCount: activeTask.toolCallCount,
841703
+ exitReason: "pause",
841704
+ resumeOnStartup: false,
841603
841705
  interruption: pendingInterruptionIdentity(activeTask)
841604
841706
  });
841605
841707
  }