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 +231 -84
- package/docs/DISCOVERY.json +40 -0
- package/docs/DISCOVERY.md +1 -0
- package/docs/work-orders/runtime-health-remediation/TRACKER.md +12 -0
- package/docs/work-orders/runtime-health-remediation/WO-21-task-convergence-and-todo-scope.md +115 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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 =
|
|
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 =
|
|
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
|
-
|
|
674969
|
-
|
|
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
|
-
|
|
675001
|
-
|
|
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
|
-
|
|
676865
|
-
|
|
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
|
-
|
|
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
|
|
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 &&
|
|
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
|
|
679455
|
-
|
|
679456
|
-
|
|
679457
|
-
|
|
679458
|
-
|
|
679459
|
-
|
|
679460
|
-
|
|
679461
|
-
|
|
679462
|
-
|
|
679463
|
-
|
|
679464
|
-
|
|
679465
|
-
|
|
679466
|
-
|
|
679467
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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}
|
|
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,
|
|
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
|
});
|
package/docs/DISCOVERY.json
CHANGED
|
@@ -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.
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
9
|
+
"version": "1.0.690",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED