omnius 1.0.452 → 1.0.454
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 +573 -125
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14361,6 +14361,22 @@ var init_file_edit = __esm({
|
|
|
14361
14361
|
}
|
|
14362
14362
|
const occurrences = countOccurrences(content, oldString);
|
|
14363
14363
|
if (occurrences === 0) {
|
|
14364
|
+
const alreadyAppliedLines = newString.length > 0 ? findMatchLines(content, newString) : [];
|
|
14365
|
+
if (alreadyAppliedLines.length > 0) {
|
|
14366
|
+
const lineInfo = alreadyAppliedLines.length === 1 ? `line ${alreadyAppliedLines[0]}` : `${alreadyAppliedLines.length} locations (lines ${alreadyAppliedLines.join(", ")})`;
|
|
14367
|
+
return {
|
|
14368
|
+
success: true,
|
|
14369
|
+
output: `[ALREADY APPLIED — ${filePath} already contains the requested new_string at ${lineInfo}; old_string is absent. No disk changes were made.]`,
|
|
14370
|
+
durationMs: performance.now() - start2,
|
|
14371
|
+
mutated: false,
|
|
14372
|
+
mutatedFiles: [],
|
|
14373
|
+
diff: buildCompactDiff(oldString, newString),
|
|
14374
|
+
noop: true,
|
|
14375
|
+
alreadyApplied: true,
|
|
14376
|
+
beforeHash,
|
|
14377
|
+
afterHash: beforeHash
|
|
14378
|
+
};
|
|
14379
|
+
}
|
|
14364
14380
|
const snippet = findClosestSnippet(content, oldString, 5);
|
|
14365
14381
|
let errorMsg = `old_string not found in ${filePath}.`;
|
|
14366
14382
|
if (snippet) {
|
|
@@ -28439,6 +28455,9 @@ function findMatchOffsetsBatch(haystack, needle) {
|
|
|
28439
28455
|
}
|
|
28440
28456
|
return offsets;
|
|
28441
28457
|
}
|
|
28458
|
+
function findMatchLinesBatch(haystack, needle) {
|
|
28459
|
+
return findMatchOffsetsBatch(haystack, needle).map((offset) => haystack.slice(0, offset).split("\n").length);
|
|
28460
|
+
}
|
|
28442
28461
|
function countOccurrences2(haystack, needle) {
|
|
28443
28462
|
let count = 0;
|
|
28444
28463
|
let pos = 0;
|
|
@@ -28587,6 +28606,7 @@ var init_batch_edit = __esm({
|
|
|
28587
28606
|
}
|
|
28588
28607
|
const results = [];
|
|
28589
28608
|
let successCount = 0;
|
|
28609
|
+
let alreadyAppliedCount = 0;
|
|
28590
28610
|
let failCount = 0;
|
|
28591
28611
|
const pendingWrites = [];
|
|
28592
28612
|
for (const [fullPath, fileEdits] of byFile) {
|
|
@@ -28607,10 +28627,18 @@ var init_batch_edit = __esm({
|
|
|
28607
28627
|
}
|
|
28608
28628
|
const originalContent = content;
|
|
28609
28629
|
let fileSuccessCount = 0;
|
|
28630
|
+
let fileAlreadyAppliedCount = 0;
|
|
28610
28631
|
let fileFailed = false;
|
|
28611
28632
|
for (const edit of fileEdits) {
|
|
28612
28633
|
const occurrences = countOccurrences2(content, edit.old_string);
|
|
28613
28634
|
if (occurrences === 0) {
|
|
28635
|
+
const alreadyAppliedLines = edit.new_string.length > 0 ? findMatchLinesBatch(content, edit.new_string) : [];
|
|
28636
|
+
if (alreadyAppliedLines.length > 0) {
|
|
28637
|
+
const lineInfo = alreadyAppliedLines.length === 1 ? `line ${alreadyAppliedLines[0]}` : `${alreadyAppliedLines.length} locations (lines ${alreadyAppliedLines.join(", ")})`;
|
|
28638
|
+
results.push(`ALREADY_APPLIED: ${edit.relPath} already contains requested new_string at ${lineInfo}; old_string is absent`);
|
|
28639
|
+
fileAlreadyAppliedCount++;
|
|
28640
|
+
continue;
|
|
28641
|
+
}
|
|
28614
28642
|
const snippet = findClosestSnippet(content, edit.old_string, 5);
|
|
28615
28643
|
if (snippet) {
|
|
28616
28644
|
const pct = Math.round(snippet.similarity * 100);
|
|
@@ -28657,6 +28685,8 @@ ${s2.content}`;
|
|
|
28657
28685
|
const afterHash = contentHash(content);
|
|
28658
28686
|
if (afterHash === beforeHash) {
|
|
28659
28687
|
results.push(`NO-OP: ${fileEdits[0].relPath} batch produced no disk changes`);
|
|
28688
|
+
successCount += fileAlreadyAppliedCount;
|
|
28689
|
+
alreadyAppliedCount += fileAlreadyAppliedCount;
|
|
28660
28690
|
continue;
|
|
28661
28691
|
}
|
|
28662
28692
|
pendingWrites.push({
|
|
@@ -28667,7 +28697,8 @@ ${s2.content}`;
|
|
|
28667
28697
|
afterHash,
|
|
28668
28698
|
editCount: fileSuccessCount
|
|
28669
28699
|
});
|
|
28670
|
-
successCount += fileSuccessCount;
|
|
28700
|
+
successCount += fileSuccessCount + fileAlreadyAppliedCount;
|
|
28701
|
+
alreadyAppliedCount += fileAlreadyAppliedCount;
|
|
28671
28702
|
} catch (error) {
|
|
28672
28703
|
results.push(`ERROR: ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
28673
28704
|
failCount++;
|
|
@@ -28692,7 +28723,7 @@ ${s2.content}`;
|
|
|
28692
28723
|
].join("\n") : dryRun ? [
|
|
28693
28724
|
`BATCH_VALIDATED_ONLY: ${appliedCount} edit(s) validated, 0 failed across ${byFile.size} file(s).`,
|
|
28694
28725
|
`NO FILES WERE MODIFIED. Call again with dry_run=false to apply this atomic batch.`
|
|
28695
|
-
].join("\n") : `BATCH_COMMITTED: ${appliedCount} edit(s) applied, 0 failed across ${byFile.size} file(s).`;
|
|
28726
|
+
].join("\n") : pendingWrites.length === 0 && alreadyAppliedCount > 0 ? `BATCH_NOOP_ALREADY_APPLIED: ${alreadyAppliedCount} edit(s) already satisfied, 0 failed across ${byFile.size} file(s).` : `BATCH_COMMITTED: ${appliedCount} edit(s) satisfied (${pendingWrites.reduce((sum, write2) => sum + write2.editCount, 0)} applied, ${alreadyAppliedCount} already applied), 0 failed across ${byFile.size} file(s).`;
|
|
28696
28727
|
const detailOutput = failCount === 0 ? dryRun ? results.join("\n") : results.map((line) => line.replace(/^VALIDATED_ONLY:/, "APPLIED:").replace(/ pending atomic commit/g, "")).join("\n") : results.join("\n");
|
|
28697
28728
|
return {
|
|
28698
28729
|
success: failCount === 0,
|
|
@@ -28703,6 +28734,7 @@ ${detailOutput}`,
|
|
|
28703
28734
|
mutated: failCount === 0 && !dryRun && pendingWrites.length > 0,
|
|
28704
28735
|
mutatedFiles: failCount === 0 && !dryRun ? pendingWrites.map((w) => w.relPath) : [],
|
|
28705
28736
|
noop: failCount === 0 && pendingWrites.length === 0,
|
|
28737
|
+
alreadyApplied: failCount === 0 && alreadyAppliedCount > 0,
|
|
28706
28738
|
dryRun: dryRun || void 0,
|
|
28707
28739
|
partial: false,
|
|
28708
28740
|
beforeHash: pendingWrites.length === 1 ? pendingWrites[0].beforeHash : void 0,
|
|
@@ -298462,11 +298494,12 @@ var init_todo_write = __esm({
|
|
|
298462
298494
|
|
|
298463
298495
|
## Nested decomposition
|
|
298464
298496
|
- Use stable ids plus parentId to create subtasks under a parent objective.
|
|
298497
|
+
- Prefer a parent + child leaf todos for any active objective that has multiple concrete actions; the TUI renders the active branch indented for the user and for your next-turn context.
|
|
298465
298498
|
- Work on leaf subtasks; a parent is completed only after every child is completed with evidence.
|
|
298466
298499
|
- When evidence changes the plan, rewrite the tree: add new child todos, block invalidated children with root cause, and keep the parent in_progress/blocked until the child set is truthful.
|
|
298467
298500
|
- If a tool fails for a child, do not mark that child completed. Reconfigure it: re-read/observe current state, choose a different target/tool, or leave it blocked.
|
|
298468
298501
|
|
|
298469
|
-
Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark completed if tests are failing or implementation is partial. The user watches this list in the chat UI in real time. Canonical
|
|
298502
|
+
Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark completed if tests are failing or implementation is partial. The user watches this list in the chat UI in real time. Canonical nested shape: todo_write({"todos":[{"id":"p1","content":"Implement feature","status":"in_progress","children":[{"id":"c1","content":"Inspect files","status":"in_progress"},{"id":"c2","content":"Make changes","status":"pending"},{"id":"c3","content":"Verify results","status":"pending"}]}]})`;
|
|
298470
298503
|
parameters = {
|
|
298471
298504
|
type: "object",
|
|
298472
298505
|
required: ["todos"],
|
|
@@ -298643,11 +298676,15 @@ Mark tasks complete IMMEDIATELY after finishing — don't batch. Never mark comp
|
|
|
298643
298676
|
const reminder = "Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Mark the current task in_progress and the next task pending. Proceed with the current task.";
|
|
298644
298677
|
const payload = {
|
|
298645
298678
|
reminder,
|
|
298646
|
-
decompositionContract: "Use parentId for sub-todos. Keep parents in_progress/blocked until all children are completed with objective evidence. When tool evidence invalidates a child, rewrite that child as blocked or split it into new children instead of overclaiming completion.",
|
|
298679
|
+
decompositionContract: "Use parentId for sub-todos. Split the active objective into concrete child leaf todos whenever it contains multiple remaining actions. Keep parents in_progress/blocked until all children are completed with objective evidence. When tool evidence invalidates a child, rewrite that child as blocked or split it into new children instead of overclaiming completion.",
|
|
298647
298680
|
oldTodos: result.oldTodos,
|
|
298648
298681
|
newTodos: result.newTodos,
|
|
298649
298682
|
verificationNudgeNeeded
|
|
298650
298683
|
};
|
|
298684
|
+
const hasNestedTodos = incoming.some((todo) => typeof todo.parentId === "string" && todo.parentId.trim().length > 0);
|
|
298685
|
+
if (incoming.length >= 3 && !hasNestedTodos) {
|
|
298686
|
+
payload["decompositionHint"] = "This checklist is still flat. If the current in_progress item expands into multiple actions, rewrite it as a parent with child todos using stable ids and parentId/children so the active branch stays visible.";
|
|
298687
|
+
}
|
|
298651
298688
|
if (repairNotes.length > 0) {
|
|
298652
298689
|
payload["inputRepair"] = Array.from(new Set(repairNotes));
|
|
298653
298690
|
payload["canonicalShape"] = {
|
|
@@ -573482,7 +573519,7 @@ function recordContextWindowDump(input) {
|
|
|
573482
573519
|
const payload = JSON.stringify(record, null, 2);
|
|
573483
573520
|
writeFileSync48(filePath, payload, "utf-8");
|
|
573484
573521
|
writeFileSync48(join111(dir, `latest-${record.agentType}.json`), payload, "utf-8");
|
|
573485
|
-
if (record.agentType === "main") {
|
|
573522
|
+
if (record.agentType === "main" || record.agentType === "sub-agent" || record.agentType === "adversary") {
|
|
573486
573523
|
writeFileSync48(join111(dir, "latest.json"), payload, "utf-8");
|
|
573487
573524
|
}
|
|
573488
573525
|
appendFileSync9(join111(dir, "index.jsonl"), JSON.stringify(toSummary(record, filePath)) + "\n", "utf-8");
|
|
@@ -574296,6 +574333,10 @@ function updateGlobalIndex(paths, runIndex) {
|
|
|
574296
574333
|
schemaVersion: 1,
|
|
574297
574334
|
updatedAt: now2,
|
|
574298
574335
|
root: paths.root,
|
|
574336
|
+
runCount: 0,
|
|
574337
|
+
latestRun: null,
|
|
574338
|
+
diagnoses: {},
|
|
574339
|
+
anchors: [],
|
|
574299
574340
|
runs: []
|
|
574300
574341
|
};
|
|
574301
574342
|
next.updatedAt = now2;
|
|
@@ -574315,8 +574356,35 @@ function updateGlobalIndex(paths, runIndex) {
|
|
|
574315
574356
|
entry,
|
|
574316
574357
|
...next.runs.filter((item) => item.runId !== runIndex.runId)
|
|
574317
574358
|
].slice(0, 100);
|
|
574359
|
+
next.runCount = next.runs.length;
|
|
574360
|
+
next.latestRun = {
|
|
574361
|
+
runId: runIndex.runId,
|
|
574362
|
+
sessionId: runIndex.sessionId,
|
|
574363
|
+
cwd: runIndex.cwd,
|
|
574364
|
+
updatedAt: runIndex.updatedAt,
|
|
574365
|
+
eventCount: runIndex.summary.eventCount,
|
|
574366
|
+
failureCount: runIndex.summary.failureCount,
|
|
574367
|
+
focusBlockCount: runIndex.summary.focusBlockCount,
|
|
574368
|
+
suspiciousShellCount: runIndex.summary.suspiciousShellCount,
|
|
574369
|
+
readmePath: runIndex.readmePath,
|
|
574370
|
+
indexPath: runIndex.indexPath
|
|
574371
|
+
};
|
|
574372
|
+
next.diagnoses = aggregateGlobalDiagnoses(next.runs);
|
|
574373
|
+
next.anchors = buildAnchors(runIndex.cwd, runIndex.runId, paths);
|
|
574318
574374
|
writeFileSync49(paths.globalIndexPath, JSON.stringify(next, null, 2), "utf-8");
|
|
574319
574375
|
}
|
|
574376
|
+
function aggregateGlobalDiagnoses(runs) {
|
|
574377
|
+
const totals = {};
|
|
574378
|
+
for (const run2 of runs) {
|
|
574379
|
+
const index = readJson2(run2.indexPath);
|
|
574380
|
+
if (!index?.diagnoses)
|
|
574381
|
+
continue;
|
|
574382
|
+
for (const [name10, count] of Object.entries(index.diagnoses)) {
|
|
574383
|
+
totals[name10] = (totals[name10] ?? 0) + count;
|
|
574384
|
+
}
|
|
574385
|
+
}
|
|
574386
|
+
return Object.fromEntries(Object.entries(totals).sort((a2, b) => b[1] - a2[1] || a2[0].localeCompare(b[0])));
|
|
574387
|
+
}
|
|
574320
574388
|
function buildAnchors(cwd4, runId, paths, extra = []) {
|
|
574321
574389
|
const omniusDir = join112(cwd4, ".omnius");
|
|
574322
574390
|
const raw = [
|
|
@@ -574701,14 +574769,13 @@ function resolveFocusSupervisorMode(configured, envValue = process.env["OMNIUS_F
|
|
|
574701
574769
|
function violatesDirective(directive, input) {
|
|
574702
574770
|
if (input.toolName === "task_complete") {
|
|
574703
574771
|
const completionStatus = normalizeCompletionStatus(input.completionStatus ?? input.args?.["status"] ?? input.args?.["completion_status"] ?? input.args?.["completionStatus"]);
|
|
574704
|
-
const
|
|
574705
|
-
|
|
574706
|
-
|
|
574707
|
-
|
|
574708
|
-
|
|
574709
|
-
return false;
|
|
574772
|
+
const summaryText = typeof input.args?.["summary"] === "string" ? input.args["summary"] : "";
|
|
574773
|
+
const summaryReportsDegraded = /^\s*(BLOCKED|INCOMPLETE|NEEDS[_ -]?INPUT)\b\s*:?/i.test(summaryText);
|
|
574774
|
+
const reportsDegradedCompletion = completionStatus === "blocked" || completionStatus === "incomplete" || completionStatus === "needs_input" || summaryReportsDegraded;
|
|
574775
|
+
if (directive.requiredNextAction === "report_blocked" || directive.requiredNextAction === "report_incomplete" || directive.state === "terminal_incomplete") {
|
|
574776
|
+
return !reportsDegradedCompletion;
|
|
574710
574777
|
}
|
|
574711
|
-
return
|
|
574778
|
+
return false;
|
|
574712
574779
|
}
|
|
574713
574780
|
if (input.toolName === "ask_user")
|
|
574714
574781
|
return false;
|
|
@@ -574989,6 +575056,11 @@ var init_focusSupervisor = __esm({
|
|
|
574989
575056
|
const family = actionFamily(input.toolName, input.args);
|
|
574990
575057
|
const prior = this.directive;
|
|
574991
575058
|
if (prior && violatesDirective(prior, input)) {
|
|
575059
|
+
if (prior.state === "warn") {
|
|
575060
|
+
this.lastDecision = "warn_not_enforced";
|
|
575061
|
+
this.lastReason = prior.reason;
|
|
575062
|
+
return this.pass();
|
|
575063
|
+
}
|
|
574992
575064
|
const strict = this.shouldStrictlyIntervene(input.context);
|
|
574993
575065
|
const sameTurnAsDirective = input.turn <= prior.createdTurn;
|
|
574994
575066
|
const alreadyCountedThisTurn = this.lastIgnoredDirectiveTurn === input.turn;
|
|
@@ -575105,6 +575177,10 @@ var init_focusSupervisor = __esm({
|
|
|
575105
575177
|
this.clearSatisfiedDirective("mutation landed", input.turn);
|
|
575106
575178
|
return;
|
|
575107
575179
|
}
|
|
575180
|
+
if (input.success && input.alreadyApplied) {
|
|
575181
|
+
this.clearSatisfiedDirective("requested edit already applied", input.turn);
|
|
575182
|
+
return;
|
|
575183
|
+
}
|
|
575108
575184
|
if (input.runtimeAuthored) {
|
|
575109
575185
|
this.lastDecision = "runtime_authored_not_satisfied";
|
|
575110
575186
|
this.lastReason = "runtime-authored control result did not execute the requested tool";
|
|
@@ -577627,7 +577703,7 @@ function computeTodoReminder(input) {
|
|
|
577627
577703
|
if (!input.todos || input.todos.length === 0) {
|
|
577628
577704
|
if (input.elicitCreateOnEmptyTodos) {
|
|
577629
577705
|
const content2 = `<system-reminder>
|
|
577630
|
-
No todo plan exists yet. Consider using the todo_write tool to create a plan for the current task — break the goal into
|
|
577706
|
+
No todo plan exists yet. Consider using the todo_write tool to create a plan for the current task — break the goal into parent objectives and child leaf subtasks when the work has multiple actions, mark the active leaf in_progress, and keep the rest pending. This helps you stay organized and track progress. Make sure that you NEVER mention this reminder to the user.
|
|
577631
577707
|
</system-reminder>`;
|
|
577632
577708
|
return { shouldInject: true, content: content2, reason: "elicit_create" };
|
|
577633
577709
|
}
|
|
@@ -577647,9 +577723,9 @@ No todo plan exists yet. Consider using the todo_write tool to create a plan for
|
|
|
577647
577723
|
return { shouldInject: false, content: null, reason: "recent_reminder" };
|
|
577648
577724
|
}
|
|
577649
577725
|
}
|
|
577650
|
-
const todoItems = input.todos
|
|
577726
|
+
const todoItems = formatTodoReminderItems(input.todos, 12);
|
|
577651
577727
|
const content = `<system-reminder>
|
|
577652
|
-
The todo_write tool hasn't been used recently. Review your todo list below. If you're working on a task, mark
|
|
577728
|
+
The todo_write tool hasn't been used recently. Review your todo list below. If you're working on a task, mark the active leaf in_progress. When you complete a leaf task, mark it completed immediately — don't batch completions. If the current item has multiple remaining actions, decompose it with stable ids and parentId/children instead of flattening the list. If the list is stale, clean it up. Make sure that you NEVER mention this reminder to the user.
|
|
577653
577729
|
|
|
577654
577730
|
Here are the existing contents of your todo list:
|
|
577655
577731
|
|
|
@@ -577657,6 +577733,40 @@ ${todoItems}
|
|
|
577657
577733
|
</system-reminder>`;
|
|
577658
577734
|
return { shouldInject: true, content, reason: "injected" };
|
|
577659
577735
|
}
|
|
577736
|
+
function formatTodoReminderItems(todos, maxRows) {
|
|
577737
|
+
const byId = new Map(todos.filter((todo) => typeof todo.id === "string" && todo.id.trim().length > 0).map((todo) => [todo.id, todo]));
|
|
577738
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
577739
|
+
const roots = [];
|
|
577740
|
+
for (const todo of todos) {
|
|
577741
|
+
if (todo.parentId && byId.has(todo.parentId)) {
|
|
577742
|
+
const siblings = byParent.get(todo.parentId) ?? [];
|
|
577743
|
+
siblings.push(todo);
|
|
577744
|
+
byParent.set(todo.parentId, siblings);
|
|
577745
|
+
} else {
|
|
577746
|
+
roots.push(todo);
|
|
577747
|
+
}
|
|
577748
|
+
}
|
|
577749
|
+
const rows = [];
|
|
577750
|
+
const seen = /* @__PURE__ */ new Set();
|
|
577751
|
+
const visit = (todo, depth) => {
|
|
577752
|
+
if (rows.length >= maxRows || seen.has(todo))
|
|
577753
|
+
return;
|
|
577754
|
+
seen.add(todo);
|
|
577755
|
+
const indent2 = " ".repeat(Math.min(depth, 4));
|
|
577756
|
+
rows.push(`${indent2}- [${todo.status}] ${todo.content}`);
|
|
577757
|
+
if (!todo.id)
|
|
577758
|
+
return;
|
|
577759
|
+
for (const child of byParent.get(todo.id) ?? [])
|
|
577760
|
+
visit(child, depth + 1);
|
|
577761
|
+
};
|
|
577762
|
+
for (const root of roots)
|
|
577763
|
+
visit(root, 0);
|
|
577764
|
+
for (const todo of todos)
|
|
577765
|
+
visit(todo, 0);
|
|
577766
|
+
if (todos.length > rows.length)
|
|
577767
|
+
rows.push(`... +${todos.length - rows.length} more`);
|
|
577768
|
+
return rows.join("\n");
|
|
577769
|
+
}
|
|
577660
577770
|
function stripThinkBlocks(s2) {
|
|
577661
577771
|
if (!s2)
|
|
577662
577772
|
return s2;
|
|
@@ -577946,6 +578056,7 @@ var init_agenticRunner = __esm({
|
|
|
577946
578056
|
init_context_compressor();
|
|
577947
578057
|
init_reflectionBuffer();
|
|
577948
578058
|
init_taskHandoff();
|
|
578059
|
+
init_artifactQuality();
|
|
577949
578060
|
init_messageLog();
|
|
577950
578061
|
init_contextTree();
|
|
577951
578062
|
init_codeGraphLink();
|
|
@@ -578844,16 +578955,21 @@ ${parts.join("\n")}
|
|
|
578844
578955
|
lines.push("implementation_state=mutation evidence is already recorded; do not treat discovery as the active phase");
|
|
578845
578956
|
}
|
|
578846
578957
|
}
|
|
578847
|
-
const todos = this.readSessionTodos() ?? [];
|
|
578958
|
+
const todos = this._normalizeTodosForPrompt(this.readSessionTodos() ?? []);
|
|
578848
578959
|
if (todos.length > 0) {
|
|
578849
|
-
const
|
|
578850
|
-
const
|
|
578851
|
-
const
|
|
578852
|
-
|
|
578853
|
-
|
|
578854
|
-
|
|
578960
|
+
const index = this._todoTreeIndex(todos);
|
|
578961
|
+
const leaves = this._todoLeaves(todos, index);
|
|
578962
|
+
const active = this._activeTodoForPrompt(todos, index);
|
|
578963
|
+
const inProgress = leaves.filter((todo) => todo.status === "in_progress");
|
|
578964
|
+
const pending2 = leaves.filter((todo) => todo.status === "pending");
|
|
578965
|
+
const blocked = leaves.filter((todo) => todo.status === "blocked");
|
|
578966
|
+
const completed = leaves.filter((todo) => todo.status === "completed");
|
|
578967
|
+
lines.push(`todo_leaf_counts=completed:${completed.length} in_progress:${inProgress.length} pending:${pending2.length} blocked:${blocked.length}`);
|
|
578968
|
+
if (active) {
|
|
578969
|
+
const branch = this._todoPath(active, index).map((todo) => todo.content.slice(0, 64)).join(" > ");
|
|
578970
|
+
lines.push(`todo_active_branch=${branch.slice(0, 220)}`);
|
|
578855
578971
|
} else if (pending2.length > 0) {
|
|
578856
|
-
lines.push("todo_contract=mark
|
|
578972
|
+
lines.push("todo_contract=mark one pending leaf todo in_progress before using the checklist as progress");
|
|
578857
578973
|
}
|
|
578858
578974
|
}
|
|
578859
578975
|
if (lines.length === 2)
|
|
@@ -578863,7 +578979,7 @@ ${parts.join("\n")}
|
|
|
578863
578979
|
}
|
|
578864
578980
|
_workboardTargetCardId(snapshot, toolName, result, meta = {}) {
|
|
578865
578981
|
const byId = new Map(snapshot.cards.map((card) => [card.id, card]));
|
|
578866
|
-
if (this._isProjectEditTool(toolName) && this._isRealProjectMutation(toolName, result)) {
|
|
578982
|
+
if (this._isProjectEditTool(toolName) && (this._isRealProjectMutation(toolName, result) || result?.alreadyApplied === true)) {
|
|
578867
578983
|
return byId.has("implement-repair") ? "implement-repair" : null;
|
|
578868
578984
|
}
|
|
578869
578985
|
if (toolName === "shell" && meta.shellFilesystemMutation === true) {
|
|
@@ -578889,9 +579005,9 @@ ${parts.join("\n")}
|
|
|
578889
579005
|
const dir = this._workboardDir();
|
|
578890
579006
|
let current = snapshot;
|
|
578891
579007
|
const byId = () => new Map(current.cards.map((card) => [card.id, card]));
|
|
578892
|
-
const
|
|
579008
|
+
const editEvidence = this._isProjectEditTool(toolName) && (this._isRealProjectMutation(toolName, result) || result?.alreadyApplied === true) || toolName === "shell" && result?.success === true && meta.shellFilesystemMutation === true;
|
|
578893
579009
|
try {
|
|
578894
|
-
if (
|
|
579010
|
+
if (editEvidence) {
|
|
578895
579011
|
let cards = byId();
|
|
578896
579012
|
const discovery = cards.get("discover-current-state");
|
|
578897
579013
|
if (discovery && discovery.status !== "verified" && discovery.evidence.length > 0) {
|
|
@@ -578924,7 +579040,7 @@ ${parts.join("\n")}
|
|
|
578924
579040
|
cardId: implementation2.id,
|
|
578925
579041
|
actor,
|
|
578926
579042
|
updates: { status: "in_progress", lane: "worker" },
|
|
578927
|
-
decisionReason: "Real project mutation landed; implementation is now the active phase."
|
|
579043
|
+
decisionReason: result?.alreadyApplied === true ? "Requested implementation edit was already present on disk; implementation evidence is now active." : "Real project mutation landed; implementation is now the active phase."
|
|
578928
579044
|
});
|
|
578929
579045
|
}
|
|
578930
579046
|
}
|
|
@@ -578961,7 +579077,7 @@ ${parts.join("\n")}
|
|
|
578961
579077
|
return;
|
|
578962
579078
|
if (meta.focusDecisionKind === "block_tool_call")
|
|
578963
579079
|
return;
|
|
578964
|
-
if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result)) {
|
|
579080
|
+
if (result?.success === true && this._isProjectEditTool(toolName) && !this._isRealProjectMutation(toolName, result) && result.alreadyApplied !== true) {
|
|
578965
579081
|
return;
|
|
578966
579082
|
}
|
|
578967
579083
|
const dir = this._workboardDir();
|
|
@@ -580430,6 +580546,127 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
580430
580546
|
return null;
|
|
580431
580547
|
}
|
|
580432
580548
|
}
|
|
580549
|
+
_normalizeTodosForPrompt(todos) {
|
|
580550
|
+
return todos.map((todo, index) => {
|
|
580551
|
+
const rawId = todo.id;
|
|
580552
|
+
const id2 = typeof rawId === "string" && rawId.trim().length > 0 ? rawId.trim() : `prompt-${index}`;
|
|
580553
|
+
const rawParentId = todo.parentId;
|
|
580554
|
+
const parentId = typeof rawParentId === "string" && rawParentId.trim().length > 0 ? rawParentId.trim() : void 0;
|
|
580555
|
+
const createdAt = typeof todo.createdAt === "number" ? todo.createdAt : 0;
|
|
580556
|
+
const updatedAt = typeof todo.updatedAt === "number" ? todo.updatedAt : createdAt;
|
|
580557
|
+
return {
|
|
580558
|
+
...todo,
|
|
580559
|
+
id: id2,
|
|
580560
|
+
...parentId ? { parentId } : {},
|
|
580561
|
+
createdAt,
|
|
580562
|
+
updatedAt
|
|
580563
|
+
};
|
|
580564
|
+
});
|
|
580565
|
+
}
|
|
580566
|
+
_todoTreeIndex(todos) {
|
|
580567
|
+
const byId = new Map(todos.map((todo) => [todo.id, todo]));
|
|
580568
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
580569
|
+
const roots = [];
|
|
580570
|
+
for (const todo of todos) {
|
|
580571
|
+
if (todo.parentId && byId.has(todo.parentId)) {
|
|
580572
|
+
const siblings = byParent.get(todo.parentId) ?? [];
|
|
580573
|
+
siblings.push(todo);
|
|
580574
|
+
byParent.set(todo.parentId, siblings);
|
|
580575
|
+
} else {
|
|
580576
|
+
roots.push(todo);
|
|
580577
|
+
}
|
|
580578
|
+
}
|
|
580579
|
+
return { byId, byParent, roots };
|
|
580580
|
+
}
|
|
580581
|
+
_todoDisplayStatus(todo, index, seen = /* @__PURE__ */ new Set()) {
|
|
580582
|
+
if (seen.has(todo.id))
|
|
580583
|
+
return todo.status;
|
|
580584
|
+
seen.add(todo.id);
|
|
580585
|
+
const children2 = index.byParent.get(todo.id) ?? [];
|
|
580586
|
+
if (children2.length === 0)
|
|
580587
|
+
return todo.status;
|
|
580588
|
+
const childStatuses = children2.map((child) => this._todoDisplayStatus(child, index, new Set(seen)));
|
|
580589
|
+
if (childStatuses.some((status) => status === "blocked"))
|
|
580590
|
+
return "blocked";
|
|
580591
|
+
if (childStatuses.some((status) => status === "in_progress"))
|
|
580592
|
+
return "in_progress";
|
|
580593
|
+
if (childStatuses.every((status) => status === "completed"))
|
|
580594
|
+
return "completed";
|
|
580595
|
+
if (todo.status === "completed")
|
|
580596
|
+
return "in_progress";
|
|
580597
|
+
return todo.status;
|
|
580598
|
+
}
|
|
580599
|
+
_todoLeafStats(todo, index) {
|
|
580600
|
+
const children2 = index.byParent.get(todo.id) ?? [];
|
|
580601
|
+
if (children2.length === 0)
|
|
580602
|
+
return null;
|
|
580603
|
+
let completed = 0;
|
|
580604
|
+
let total = 0;
|
|
580605
|
+
const seen = /* @__PURE__ */ new Set();
|
|
580606
|
+
const visit = (node) => {
|
|
580607
|
+
if (seen.has(node.id))
|
|
580608
|
+
return;
|
|
580609
|
+
seen.add(node.id);
|
|
580610
|
+
const nested = index.byParent.get(node.id) ?? [];
|
|
580611
|
+
if (nested.length === 0) {
|
|
580612
|
+
total++;
|
|
580613
|
+
if (node.status === "completed")
|
|
580614
|
+
completed++;
|
|
580615
|
+
return;
|
|
580616
|
+
}
|
|
580617
|
+
for (const child of nested)
|
|
580618
|
+
visit(child);
|
|
580619
|
+
};
|
|
580620
|
+
for (const child of children2)
|
|
580621
|
+
visit(child);
|
|
580622
|
+
return { completed, total };
|
|
580623
|
+
}
|
|
580624
|
+
_todoLeaves(todos, index = this._todoTreeIndex(todos)) {
|
|
580625
|
+
const leaves = todos.filter((todo) => (index.byParent.get(todo.id) ?? []).length === 0);
|
|
580626
|
+
return leaves.length > 0 ? leaves : todos;
|
|
580627
|
+
}
|
|
580628
|
+
_todoPath(todo, index) {
|
|
580629
|
+
const path12 = [todo];
|
|
580630
|
+
const seen = /* @__PURE__ */ new Set([todo.id]);
|
|
580631
|
+
let cursor = todo;
|
|
580632
|
+
while (cursor?.parentId && index.byId.has(cursor.parentId)) {
|
|
580633
|
+
const parent = index.byId.get(cursor.parentId);
|
|
580634
|
+
if (!parent || seen.has(parent.id))
|
|
580635
|
+
break;
|
|
580636
|
+
path12.unshift(parent);
|
|
580637
|
+
seen.add(parent.id);
|
|
580638
|
+
cursor = parent;
|
|
580639
|
+
}
|
|
580640
|
+
return path12;
|
|
580641
|
+
}
|
|
580642
|
+
_activeTodoForPrompt(todos, index = this._todoTreeIndex(todos)) {
|
|
580643
|
+
const leaves = this._todoLeaves(todos, index);
|
|
580644
|
+
return leaves.find((todo) => todo.status === "in_progress") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "in_progress") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "blocked") ?? todos.find((todo) => this._todoDisplayStatus(todo, index) === "pending") ?? null;
|
|
580645
|
+
}
|
|
580646
|
+
_orderedTodosForPrompt(todos) {
|
|
580647
|
+
const index = this._todoTreeIndex(todos);
|
|
580648
|
+
const out = [];
|
|
580649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
580650
|
+
const visit = (todo, depth) => {
|
|
580651
|
+
if (seen.has(todo.id))
|
|
580652
|
+
return;
|
|
580653
|
+
seen.add(todo.id);
|
|
580654
|
+
out.push({
|
|
580655
|
+
todo,
|
|
580656
|
+
depth: Math.min(depth, 4),
|
|
580657
|
+
displayStatus: this._todoDisplayStatus(todo, index),
|
|
580658
|
+
childStats: this._todoLeafStats(todo, index)
|
|
580659
|
+
});
|
|
580660
|
+
for (const child of index.byParent.get(todo.id) ?? []) {
|
|
580661
|
+
visit(child, depth + 1);
|
|
580662
|
+
}
|
|
580663
|
+
};
|
|
580664
|
+
for (const root of index.roots)
|
|
580665
|
+
visit(root, 0);
|
|
580666
|
+
for (const todo of todos)
|
|
580667
|
+
visit(todo, 0);
|
|
580668
|
+
return out;
|
|
580669
|
+
}
|
|
580433
580670
|
_normalizeCommandForValidationMatch(command) {
|
|
580434
580671
|
return normalizeShellCommand(command);
|
|
580435
580672
|
}
|
|
@@ -580584,7 +580821,9 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
|
|
|
580584
580821
|
const realFileMutation = input.realFileMutation ?? this._isRealProjectMutation(input.toolName, input.result);
|
|
580585
580822
|
const attemptedTargetPaths = this._extractToolTargetPaths(input.toolName, input.args, input.result);
|
|
580586
580823
|
const realMutationPaths = input.realMutationPaths ?? (realFileMutation ? attemptedTargetPaths : []);
|
|
580587
|
-
|
|
580824
|
+
const shellVerification = input.toolName === "shell" && input.result.runtimeAuthored !== true && this._shellResultLooksLikeVerification(input.args, input.result);
|
|
580825
|
+
const shellVerificationFamily = shellVerification ? this._shellVerificationFamily(input.args) : "";
|
|
580826
|
+
if (input.result.success === true && this._isProjectEditTool(input.toolName) && !realFileMutation && input.result.alreadyApplied !== true) {
|
|
580588
580827
|
return;
|
|
580589
580828
|
}
|
|
580590
580829
|
if (this._shouldSuppressCompletionDiscoveryEvidence(input.toolName, input.result, attemptedTargetPaths)) {
|
|
@@ -580596,9 +580835,9 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
|
|
|
580596
580835
|
outputPreview: (input.outputPreview ?? this._toolEvidencePreview(input.result, void 0, 1200, input.toolName)).toString().slice(0, input.toolName === "audio_analyze" || input.toolName === "video_understand" ? 1200 : 500),
|
|
580597
580836
|
argsKey: input.argsKey.slice(0, 300),
|
|
580598
580837
|
targetPaths: attemptedTargetPaths,
|
|
580599
|
-
role: input.result.completionEvidenceRole ?? (realFileMutation ? "mutation" : input.result.runtimeAuthored === true ? "blocker" : void 0),
|
|
580600
|
-
family: input.result.completionEvidenceFamily ?? (input.result.runtimeAuthored === true ? "runtime_control" : void 0),
|
|
580601
|
-
verificationImpact: input.result.completionVerificationImpact ?? (realFileMutation ? "invalidates" : void 0),
|
|
580838
|
+
role: input.result.completionEvidenceRole ?? (input.result.alreadyApplied === true ? "mutation" : realFileMutation ? "mutation" : input.result.runtimeAuthored === true ? "blocker" : shellVerification ? "verification" : void 0),
|
|
580839
|
+
family: input.result.completionEvidenceFamily ?? (input.result.runtimeAuthored === true ? "runtime_control" : shellVerification ? shellVerificationFamily : void 0),
|
|
580840
|
+
verificationImpact: input.result.completionVerificationImpact ?? (input.result.alreadyApplied === true ? "neutral" : realFileMutation ? "invalidates" : void 0),
|
|
580602
580841
|
metrics: input.result.completionEvidenceMetrics
|
|
580603
580842
|
});
|
|
580604
580843
|
if (realFileMutation && realMutationPaths.length > 0) {
|
|
@@ -580617,6 +580856,20 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
|
|
|
580617
580856
|
}
|
|
580618
580857
|
this._saveCompletionLedgerSafe();
|
|
580619
580858
|
}
|
|
580859
|
+
_shellVerificationFamily(args) {
|
|
580860
|
+
const command = String(args?.["command"] ?? args?.["cmd"] ?? "").trim();
|
|
580861
|
+
const normalized = normalizeShellCommand(command);
|
|
580862
|
+
return `shell:${(normalized || command || "verification").slice(0, 160)}`;
|
|
580863
|
+
}
|
|
580864
|
+
_shellResultLooksLikeVerification(args, result) {
|
|
580865
|
+
const command = String(args?.["command"] ?? args?.["cmd"] ?? "");
|
|
580866
|
+
if (!command.trim())
|
|
580867
|
+
return false;
|
|
580868
|
+
const text2 = `${command}
|
|
580869
|
+
${result.output ?? ""}
|
|
580870
|
+
${result.error ?? ""}`;
|
|
580871
|
+
return /\b(test|tests|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|tsc|build|verify|verification|check)\b/i.test(text2);
|
|
580872
|
+
}
|
|
580620
580873
|
_shouldSuppressCompletionDiscoveryEvidence(toolName, result, targetPaths) {
|
|
580621
580874
|
if (result.success !== true)
|
|
580622
580875
|
return false;
|
|
@@ -581097,6 +581350,9 @@ ${context2 ?? ""}`;
|
|
|
581097
581350
|
const originalGoal = (this._taskState.originalGoal || this._taskState.goal || "").trim();
|
|
581098
581351
|
if (!originalGoal)
|
|
581099
581352
|
return { proceed: true };
|
|
581353
|
+
if (/^\s*(BLOCKED|INCOMPLETE|NEEDS_INPUT)\b\s*:?/i.test(proposedSummary)) {
|
|
581354
|
+
return { proceed: true };
|
|
581355
|
+
}
|
|
581100
581356
|
const actionable = toolCallLog.some((e2) => e2.mutated || e2.name === "shell" || e2.name === "file_write" || e2.name === "file_edit");
|
|
581101
581357
|
if (!actionable)
|
|
581102
581358
|
return { proceed: true };
|
|
@@ -581319,6 +581575,9 @@ ${outTail || "(no output captured)"}`,
|
|
|
581319
581575
|
const _verify = await this._runCompletionVerifyGate(turn);
|
|
581320
581576
|
if (!_verify.proceed)
|
|
581321
581577
|
return _verify;
|
|
581578
|
+
if (this.options.backwardPassReview === false || this.options.disableAdversaryCritic === true) {
|
|
581579
|
+
return { proceed: true };
|
|
581580
|
+
}
|
|
581322
581581
|
const _resolution = await this._runResolutionGate(turn, proposedSummary, toolCallLog);
|
|
581323
581582
|
if (!_resolution.proceed) {
|
|
581324
581583
|
return {
|
|
@@ -581662,14 +581921,18 @@ ${_checks}`
|
|
|
581662
581921
|
const parts = [];
|
|
581663
581922
|
const todos = this.readSessionTodos();
|
|
581664
581923
|
if (todos && todos.length > 0) {
|
|
581665
|
-
const
|
|
581666
|
-
const
|
|
581924
|
+
const index = this._todoTreeIndex(todos);
|
|
581925
|
+
const leaves = this._todoLeaves(todos, index);
|
|
581926
|
+
const current = this._activeTodoForPrompt(todos, index);
|
|
581927
|
+
const completedN = leaves.filter((t2) => t2.status === "completed").length;
|
|
581928
|
+
const totalN = leaves.length;
|
|
581667
581929
|
if (current) {
|
|
581668
|
-
|
|
581930
|
+
const path12 = this._todoPath(current, index).map((todo) => todo.content.slice(0, 64)).join(" > ");
|
|
581931
|
+
parts.push(`[plan: ${completedN}/${totalN} leaf tasks complete · active: ${path12.slice(0, 160)}]`);
|
|
581669
581932
|
} else {
|
|
581670
|
-
const nextPending =
|
|
581933
|
+
const nextPending = leaves.find((t2) => t2.status === "pending");
|
|
581671
581934
|
if (nextPending) {
|
|
581672
|
-
parts.push(`[plan: ${completedN}/${
|
|
581935
|
+
parts.push(`[plan: ${completedN}/${totalN} leaf tasks complete · next: ${nextPending.content.slice(0, 80)}]`);
|
|
581673
581936
|
}
|
|
581674
581937
|
}
|
|
581675
581938
|
}
|
|
@@ -582641,36 +582904,73 @@ ${latest.output || ""}`.trim();
|
|
|
582641
582904
|
*/
|
|
582642
582905
|
_renderTodoStateBlock(turn) {
|
|
582643
582906
|
try {
|
|
582644
|
-
const todos = this.readSessionTodos();
|
|
582907
|
+
const todos = this._normalizeTodosForPrompt(this.readSessionTodos() ?? []);
|
|
582645
582908
|
if (!todos || todos.length === 0)
|
|
582646
582909
|
return null;
|
|
582910
|
+
const index = this._todoTreeIndex(todos);
|
|
582911
|
+
const leaves = this._todoLeaves(todos, index);
|
|
582912
|
+
const completedLeaves = leaves.filter((t2) => t2.status === "completed");
|
|
582913
|
+
const active = this._activeTodoForPrompt(todos, index);
|
|
582914
|
+
const activePath = active ? this._todoPath(active, index).map((todo) => todo.content.slice(0, 80)).join(" > ") : "";
|
|
582647
582915
|
const lines = [
|
|
582648
|
-
"[CURRENT TODO PLAN — already in your state, no need to call todo_write to see it]"
|
|
582916
|
+
"[CURRENT TODO PLAN — already in your state, no need to call todo_write to see it]",
|
|
582917
|
+
`leaf_progress=${completedLeaves.length}/${leaves.length}`
|
|
582649
582918
|
];
|
|
582650
|
-
|
|
582651
|
-
|
|
582652
|
-
const
|
|
582653
|
-
const
|
|
582654
|
-
|
|
582655
|
-
|
|
582656
|
-
|
|
582657
|
-
|
|
582658
|
-
|
|
582659
|
-
|
|
582660
|
-
|
|
582661
|
-
|
|
582662
|
-
|
|
582663
|
-
|
|
582664
|
-
|
|
582919
|
+
if (activePath)
|
|
582920
|
+
lines.push(`active_branch=${activePath.slice(0, 220)}`);
|
|
582921
|
+
const ordered = this._orderedTodosForPrompt(todos);
|
|
582922
|
+
const activeIndex = active ? ordered.findIndex((entry) => entry.todo.id === active.id) : -1;
|
|
582923
|
+
let visible = ordered;
|
|
582924
|
+
if (ordered.length > 14) {
|
|
582925
|
+
const selected = [];
|
|
582926
|
+
const seen = /* @__PURE__ */ new Set();
|
|
582927
|
+
const add3 = (entry) => {
|
|
582928
|
+
if (!entry || selected.length >= 13 || seen.has(entry.todo.id))
|
|
582929
|
+
return;
|
|
582930
|
+
selected.push(entry);
|
|
582931
|
+
seen.add(entry.todo.id);
|
|
582932
|
+
};
|
|
582933
|
+
if (active && activeIndex >= 0) {
|
|
582934
|
+
const pathIds = new Set(this._todoPath(active, index).map((t2) => t2.id));
|
|
582935
|
+
for (const entry of ordered) {
|
|
582936
|
+
if (pathIds.has(entry.todo.id) && entry.todo.id !== active.id)
|
|
582937
|
+
add3(entry);
|
|
582938
|
+
}
|
|
582939
|
+
const siblings = active.parentId ? index.byParent.get(active.parentId) ?? [] : index.roots;
|
|
582940
|
+
const siblingIndex = siblings.findIndex((t2) => t2.id === active.id);
|
|
582941
|
+
if (siblingIndex > 0) {
|
|
582942
|
+
add3(ordered.find((entry) => entry.todo.id === siblings[siblingIndex - 1]?.id));
|
|
582943
|
+
}
|
|
582944
|
+
add3(ordered[activeIndex]);
|
|
582945
|
+
for (let i2 = siblingIndex + 1; i2 < siblings.length && selected.length < 13; i2++) {
|
|
582946
|
+
add3(ordered.find((entry) => entry.todo.id === siblings[i2]?.id));
|
|
582947
|
+
}
|
|
582948
|
+
for (const child of index.byParent.get(active.id) ?? []) {
|
|
582949
|
+
add3(ordered.find((entry) => entry.todo.id === child.id));
|
|
582950
|
+
}
|
|
582951
|
+
for (let i2 = activeIndex + 1; i2 < ordered.length && selected.length < 13; i2++) {
|
|
582952
|
+
add3(ordered[i2]);
|
|
582953
|
+
}
|
|
582954
|
+
}
|
|
582955
|
+
visible = selected.length > 0 ? selected : ordered.slice(0, 13);
|
|
582665
582956
|
}
|
|
582666
|
-
|
|
582667
|
-
|
|
582668
|
-
|
|
582669
|
-
|
|
582957
|
+
const statusMark = {
|
|
582958
|
+
completed: "OK",
|
|
582959
|
+
in_progress: ">>",
|
|
582960
|
+
pending: "--",
|
|
582961
|
+
blocked: "!!"
|
|
582962
|
+
};
|
|
582963
|
+
lines.push("tree:");
|
|
582964
|
+
for (const entry of visible) {
|
|
582965
|
+
const indent2 = " ".repeat(entry.depth);
|
|
582966
|
+
const childSuffix = entry.childStats && entry.childStats.total > 0 ? ` [${entry.childStats.completed}/${entry.childStats.total}]` : "";
|
|
582967
|
+
const blocker = entry.todo.blocker ? ` blocker=${entry.todo.blocker.slice(0, 120)}` : "";
|
|
582968
|
+
lines.push(`${indent2}${statusMark[entry.displayStatus]} ${entry.todo.content.slice(0, 140)}${childSuffix}${blocker}`);
|
|
582670
582969
|
}
|
|
582671
|
-
if (
|
|
582672
|
-
lines.push(
|
|
582970
|
+
if (ordered.length > visible.length) {
|
|
582971
|
+
lines.push(`... +${ordered.length - visible.length} more todo node(s)`);
|
|
582673
582972
|
}
|
|
582973
|
+
lines.push("decomposition_contract=Use stable ids plus parentId for subtasks. If the active item has multiple remaining actions, rewrite it as a parent with concrete child leaf todos; work leaf-first and do not complete a parent before all children are complete with evidence.");
|
|
582674
582974
|
lines.push(`(turn ${turn} — call todo_write ONLY to update state, never to re-read this plan)`);
|
|
582675
582975
|
return lines.join("\n");
|
|
582676
582976
|
} catch {
|
|
@@ -583217,6 +583517,7 @@ ${chunk.content}`, {
|
|
|
583217
583517
|
this._taskState.goal ? `Active task: (see user message above — the goal block defers to the user message as the authoritative source)` : null
|
|
583218
583518
|
].filter(Boolean).join("\n\n");
|
|
583219
583519
|
const filesystemBlock = this._renderFilesystemStateBlock(turn);
|
|
583520
|
+
const artifactContractBlock = this._contractRegistry.format(15);
|
|
583220
583521
|
const todoBlock = this._renderTodoStateBlock(turn);
|
|
583221
583522
|
const failureBlock = this._renderRecentFailuresBlock(turn);
|
|
583222
583523
|
const churnBlock = this._renderWriteChurnBlock(turn);
|
|
@@ -583259,6 +583560,13 @@ ${this._lastPprMemoryLines.slice(0, 5).join("\n")}` : null;
|
|
|
583259
583560
|
createdTurn: turn,
|
|
583260
583561
|
ttlTurns: 1
|
|
583261
583562
|
}),
|
|
583563
|
+
signalFromBlock("known_files", "turn.artifact-contracts", artifactContractBlock, {
|
|
583564
|
+
id: "artifact-contracts",
|
|
583565
|
+
dedupeKey: "turn.artifact-contracts",
|
|
583566
|
+
priority: 86,
|
|
583567
|
+
createdTurn: turn,
|
|
583568
|
+
ttlTurns: 1
|
|
583569
|
+
}),
|
|
583262
583570
|
signalFromBlock("action_contract", "turn.next-action-contract", actionContractBlock, {
|
|
583263
583571
|
id: "next-action-contract",
|
|
583264
583572
|
dedupeKey: "turn.next-action-contract",
|
|
@@ -584588,7 +584896,6 @@ Respond with your assessment, then take action.`;
|
|
|
584588
584896
|
const hardDeadlineMs = Number.isFinite(taskTimeoutMs) && taskTimeoutMs > 0 ? start2 + taskTimeoutMs : Number.POSITIVE_INFINITY;
|
|
584589
584897
|
let timedOut = false;
|
|
584590
584898
|
let timeoutSummary = "";
|
|
584591
|
-
let _timeoutAdvisoryInjected = false;
|
|
584592
584899
|
const remainingTaskMs = () => Number.isFinite(hardDeadlineMs) ? Math.max(0, hardDeadlineMs - Date.now()) : Number.POSITIVE_INFINITY;
|
|
584593
584900
|
const boundedRequestTimeoutMs = () => {
|
|
584594
584901
|
const configured = Number.isFinite(this.options.requestTimeoutMs) && this.options.requestTimeoutMs > 0 ? this.options.requestTimeoutMs : 3e5;
|
|
@@ -584618,7 +584925,8 @@ Respond with your assessment, then take action.`;
|
|
|
584618
584925
|
return false;
|
|
584619
584926
|
if (Date.now() < hardDeadlineMs)
|
|
584620
584927
|
return false;
|
|
584621
|
-
|
|
584928
|
+
markTaskTimedOut(phase, turn);
|
|
584929
|
+
return true;
|
|
584622
584930
|
};
|
|
584623
584931
|
const selfEvalInterval = taskTimeoutMs;
|
|
584624
584932
|
let nextSelfEval = start2 + selfEvalInterval;
|
|
@@ -586647,12 +586955,13 @@ ${_staleSamples.join("\n")}` : ``,
|
|
|
586647
586955
|
content: `[TASK DECOMPOSITION — Multi-step task detected]
|
|
586648
586956
|
|
|
586649
586957
|
MANDATORY FIRST ACTION: Call todo_write NOW with the complete plan.
|
|
586650
|
-
Each todo item is { content
|
|
586651
|
-
|
|
586958
|
+
Each todo item is { id, content, status, parentId? } or a parent with children/subtasks. Use stable ids.
|
|
586959
|
+
Represent broad objectives as parent todos and concrete work as child leaf todos. Mark the active leaf in_progress, the rest pending.
|
|
586652
586960
|
Only count substantive work phases. Do NOT count observing a tool result, reporting findings, or calling task_complete as todo phases.
|
|
586653
|
-
Example: todo_write({todos: [{content: "
|
|
586961
|
+
Example: todo_write({todos: [{id: "impl", content: "Implement requested change", status: "in_progress", children: [{id: "inspect", content: "Read relevant files", status: "in_progress"}, {id: "edit", content: "Make scoped edits", status: "pending"}, {id: "verify", content: "Run verification", status: "pending"}]}]})
|
|
586654
586962
|
|
|
586655
|
-
After EACH
|
|
586963
|
+
After EACH leaf finishes, call todo_write AGAIN with that leaf completed and the next leaf in_progress.
|
|
586964
|
+
If new work is discovered, decompose the current leaf into children or add siblings under the same parent instead of flattening a long list.
|
|
586656
586965
|
The user watches this checklist update live in the chat UI. Without it they cannot see your plan or track progress.
|
|
586657
586966
|
|
|
586658
586967
|
Then complete these phases IN ORDER:
|
|
@@ -586971,10 +587280,6 @@ ${memoryLines.join("\n")}`
|
|
|
586971
587280
|
this.proactivePrune(compacted, turn);
|
|
586972
587281
|
this.microcompact(compacted, recentToolResults);
|
|
586973
587282
|
this._insertContextFrame(compacted, await this._buildTurnContextFrame(turn, compacted, recentToolResults, environmentBlock));
|
|
586974
|
-
const contractBlock = this._contractRegistry.format(15);
|
|
586975
|
-
if (contractBlock) {
|
|
586976
|
-
compacted.push({ role: "system", content: contractBlock });
|
|
586977
|
-
}
|
|
586978
587283
|
this.gcSystemMessages(compacted);
|
|
586979
587284
|
let requestMessages = sanitizeHistoryThink(compacted);
|
|
586980
587285
|
{
|
|
@@ -588639,7 +588944,7 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
588639
588944
|
} else if (tc.name === "shell") {
|
|
588640
588945
|
const cmd = String(tc.arguments?.["command"] ?? "");
|
|
588641
588946
|
if (/\b(npm|pnpm|yarn)\s+test\b|\bjest\b|\bvitest\b/i.test(cmd)) {
|
|
588642
|
-
const passed =
|
|
588947
|
+
const passed = /\b(PASS|pass|passed|success|ok)\b|✓\s*all/i.test(result.output) && !/FAIL|✗/i.test(result.output);
|
|
588643
588948
|
this._worldFacts.lastTest = {
|
|
588644
588949
|
passed,
|
|
588645
588950
|
summary: result.output.slice(0, 200),
|
|
@@ -589106,7 +589411,8 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
589106
589411
|
const _shellCmd2 = String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? "");
|
|
589107
589412
|
const _declaredVerify = this._matchedDeclaredVerifyCommand(_shellCmd2);
|
|
589108
589413
|
const _lastVerifier = this._lastVerifierResult;
|
|
589109
|
-
const
|
|
589414
|
+
const _directShellValidation = result.success === true && this._shellResultLooksLikeVerification(tc.arguments, result);
|
|
589415
|
+
const _noTodoDirectValidation = !_declaredVerify && (this.readSessionTodos() || []).length === 0 && this._taskState.modifiedFiles.size > 0 && (_lastVerifier?.outcomeClass === "verified" || _directShellValidation);
|
|
589110
589416
|
const _legacyGenericValidation = process.env["OMNIUS_ENABLE_GENERIC_COMPLETION_COMMAND_HEURISTIC"] === "1" && /\b(build|test|run\b|start\b|serve\b|verify|check)\b/i.test(_shellCmd2);
|
|
589111
589417
|
if (_declaredVerify || _noTodoDirectValidation || _legacyGenericValidation) {
|
|
589112
589418
|
this._lastBuildSuccessTurn = turn;
|
|
@@ -589281,7 +589587,7 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
589281
589587
|
}
|
|
589282
589588
|
const cacheableMemoryNoop = tc.name === "memory_write" && result.success && result.noop;
|
|
589283
589589
|
const cacheableProjectWriteNoop = this._isProjectEditTool(tc.name) && result.success && !this._isRealProjectMutation(tc.name, result) && (result.noop === true || result.mutated === false);
|
|
589284
|
-
if (isReadLike && result.success || tc.name === "shell" || cacheableMemoryNoop || cacheableProjectWriteNoop) {
|
|
589590
|
+
if (isReadLike && result.success && result.runtimeAuthored !== true || tc.name === "shell" && result.runtimeAuthored !== true || cacheableMemoryNoop || cacheableProjectWriteNoop) {
|
|
589285
589591
|
recentToolResults.set(toolFingerprint, {
|
|
589286
589592
|
result: tc.name === "shell" ? this._buildShellCacheResult(tc.arguments, result) : (
|
|
589287
589593
|
// Read-like results are SERVED BACK to the model when the
|
|
@@ -589400,7 +589706,7 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
589400
589706
|
}
|
|
589401
589707
|
}
|
|
589402
589708
|
}
|
|
589403
|
-
if (!result.success) {
|
|
589709
|
+
if (!result.success && result.runtimeAuthored !== true) {
|
|
589404
589710
|
this._recentFailures.push({
|
|
589405
589711
|
tool: tc.name,
|
|
589406
589712
|
fingerprint: toolFingerprint,
|
|
@@ -589638,7 +589944,7 @@ ${bookkeepingGuidance}` : bookkeepingGuidance;
|
|
|
589638
589944
|
argsPreview: toolArgsPreview,
|
|
589639
589945
|
targetPaths: toolTargetPaths,
|
|
589640
589946
|
family: result.completionEvidenceFamily ?? (result.runtimeAuthored === true ? "runtime_control" : void 0),
|
|
589641
|
-
role: result.completionEvidenceRole ?? (realFileMutation || shellFilesystemMutation ? "mutation" : result.runtimeAuthored === true ? "blocker" : void 0),
|
|
589947
|
+
role: result.completionEvidenceRole ?? (result.alreadyApplied === true ? "mutation" : realFileMutation || shellFilesystemMutation ? "mutation" : result.runtimeAuthored === true ? "blocker" : void 0),
|
|
589642
589948
|
turn
|
|
589643
589949
|
});
|
|
589644
589950
|
if (this._toolEvents.length > 200)
|
|
@@ -589787,6 +590093,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
|
|
|
589787
590093
|
mutated: realFileMutation || shellFilesystemMutation,
|
|
589788
590094
|
isReadLike,
|
|
589789
590095
|
noop: tc.name === "todo_write" ? this._isTodoWriteNoopResult(result) : result.noop,
|
|
590096
|
+
alreadyApplied: result.alreadyApplied === true,
|
|
589790
590097
|
runtimeAuthored: result.runtimeAuthored === true
|
|
589791
590098
|
});
|
|
589792
590099
|
const focusSnapshotAfter = this._focusSupervisor?.snapshot();
|
|
@@ -590885,10 +591192,6 @@ ${this.options.maxTurns && this.options.maxTurns > 0 ? `You have ${this.options.
|
|
|
590885
591192
|
}
|
|
590886
591193
|
}
|
|
590887
591194
|
this._insertContextFrame(compactedMsgs, await this._buildTurnContextFrame(turn, compactedMsgs, void 0, bfEnvironmentBlock));
|
|
590888
|
-
const bfContractBlock = this._contractRegistry.format(15);
|
|
590889
|
-
if (bfContractBlock) {
|
|
590890
|
-
compactedMsgs.push({ role: "system", content: bfContractBlock });
|
|
590891
|
-
}
|
|
590892
591195
|
this.gcSystemMessages(compactedMsgs);
|
|
590893
591196
|
const modelFacingMessages = sanitizeHistoryThink(compactedMsgs);
|
|
590894
591197
|
const chatRequest = {
|
|
@@ -591431,7 +591734,7 @@ ${this._completionCaveat}` : this._completionCaveat;
|
|
|
591431
591734
|
if (this._completionLedger) {
|
|
591432
591735
|
this._completionLedger = finalizeCompletionLedgerTruth(this._completionLedger);
|
|
591433
591736
|
this._saveCompletionLedgerSafe();
|
|
591434
|
-
if (completed && this._completionLedger.status === "incomplete_verification") {
|
|
591737
|
+
if (completed && this._completionLedger.status === "incomplete_verification" && this.options.backwardPassReview !== false && !this._completionCaveat) {
|
|
591435
591738
|
completed = false;
|
|
591436
591739
|
const unresolvedSummary = this._completionLedger.unresolved.slice(-3).map((item) => item.text).join("; ");
|
|
591437
591740
|
const caveat = unresolvedSummary ? `Incomplete verification: ${unresolvedSummary}` : "Incomplete verification: unresolved evidence remains in the completion ledger.";
|
|
@@ -592093,33 +592396,49 @@ ${caveat}` : caveat;
|
|
|
592093
592396
|
this._saveWorkboard();
|
|
592094
592397
|
await this._hookManager.runSessionHook("session_end", this._sessionId);
|
|
592095
592398
|
try {
|
|
592096
|
-
const
|
|
592097
|
-
|
|
592098
|
-
|
|
592099
|
-
|
|
592100
|
-
|
|
592101
|
-
|
|
592102
|
-
|
|
592103
|
-
|
|
592104
|
-
|
|
592105
|
-
|
|
592106
|
-
|
|
592107
|
-
|
|
592108
|
-
|
|
592109
|
-
|
|
592110
|
-
|
|
592111
|
-
|
|
592112
|
-
|
|
592113
|
-
|
|
592114
|
-
|
|
592115
|
-
|
|
592116
|
-
|
|
592117
|
-
|
|
592118
|
-
|
|
592119
|
-
|
|
592120
|
-
|
|
592121
|
-
|
|
592122
|
-
|
|
592399
|
+
const trajectoryQuality = assessTaskArtifactQuality({
|
|
592400
|
+
artifactMode: this.options.artifactMode,
|
|
592401
|
+
goal: persistentTaskGoal,
|
|
592402
|
+
summary,
|
|
592403
|
+
toolsUsed: toolCallLog.map((tc) => tc.name),
|
|
592404
|
+
filesTouched: [...this._taskState.modifiedFiles.keys()],
|
|
592405
|
+
turns: messages2.filter((m2) => m2.role === "assistant").length
|
|
592406
|
+
});
|
|
592407
|
+
if (!trajectoryQuality.eligible) {
|
|
592408
|
+
this.emit({
|
|
592409
|
+
type: "status",
|
|
592410
|
+
content: `Trajectory log skipped: ${trajectoryQuality.reason ?? "not eligible"}`,
|
|
592411
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
592412
|
+
});
|
|
592413
|
+
} else {
|
|
592414
|
+
const fs11 = await import("node:fs");
|
|
592415
|
+
const path12 = await import("node:path");
|
|
592416
|
+
const trajDir = path12.join(this.omniusStateDir(), "trajectories");
|
|
592417
|
+
fs11.mkdirSync(trajDir, { recursive: true });
|
|
592418
|
+
const trajectory = {
|
|
592419
|
+
id: this._sessionId,
|
|
592420
|
+
// CLEAN task — this file is the prerequisite for ALL future RL/RFT
|
|
592421
|
+
// training. Storing the scaffolded version would teach future models
|
|
592422
|
+
// to reproduce signpost text as part of their task understanding.
|
|
592423
|
+
task: persistentTaskGoal.slice(0, 1e3),
|
|
592424
|
+
outcome: completed ? "pass" : this.aborted ? "aborted" : runStatus === "incomplete_verification" ? "incomplete_verification" : "timeout",
|
|
592425
|
+
model: this.backend.model ?? "unknown",
|
|
592426
|
+
modelTier: this.options.modelTier ?? "large",
|
|
592427
|
+
turns: messages2.filter((m2) => m2.role === "assistant").length,
|
|
592428
|
+
toolCalls: toolCallLog.map((tc) => ({
|
|
592429
|
+
name: tc.name,
|
|
592430
|
+
argsFingerprint: tc.argsKey.slice(0, 100)
|
|
592431
|
+
})),
|
|
592432
|
+
totalToolCalls: toolCallCount,
|
|
592433
|
+
totalTokens,
|
|
592434
|
+
filesModified: [...this._taskState.modifiedFiles.keys()],
|
|
592435
|
+
failedApproaches: this._taskState.failedApproaches,
|
|
592436
|
+
completedSteps: this._taskState.completedSteps,
|
|
592437
|
+
durationMs,
|
|
592438
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
592439
|
+
};
|
|
592440
|
+
fs11.appendFileSync(path12.join(trajDir, `trajectories.jsonl`), JSON.stringify(trajectory) + "\n", "utf-8");
|
|
592441
|
+
}
|
|
592123
592442
|
} catch {
|
|
592124
592443
|
}
|
|
592125
592444
|
let filesEdited;
|
|
@@ -592615,7 +592934,7 @@ ${marker}` : marker);
|
|
|
592615
592934
|
if (!target)
|
|
592616
592935
|
return;
|
|
592617
592936
|
const targetPathKey = this.staleEditPathKey(target.path);
|
|
592618
|
-
if (this._isRealProjectMutation(toolName, result)) {
|
|
592937
|
+
if (this._isRealProjectMutation(toolName, result) || result.alreadyApplied === true) {
|
|
592619
592938
|
for (const [key2, entry] of this._staleEditFamilies) {
|
|
592620
592939
|
if (entry.pathKey === targetPathKey)
|
|
592621
592940
|
this._staleEditFamilies.delete(key2);
|
|
@@ -629489,6 +629808,8 @@ var init_overlay_lock = __esm({
|
|
|
629489
629808
|
// packages/cli/src/tui/tui-tasks-renderer.ts
|
|
629490
629809
|
var tui_tasks_renderer_exports = {};
|
|
629491
629810
|
__export(tui_tasks_renderer_exports, {
|
|
629811
|
+
__testOrderTodosForDisplay: () => __testOrderTodosForDisplay,
|
|
629812
|
+
__testSelectPanelTodoRows: () => __testSelectPanelTodoRows,
|
|
629492
629813
|
getTuiTasksScope: () => getTuiTasksScope,
|
|
629493
629814
|
onTuiTasksHeightChange: () => onTuiTasksHeightChange,
|
|
629494
629815
|
refreshTuiTasks: () => refreshTuiTasks,
|
|
@@ -629646,7 +629967,7 @@ function computeTargetHeight() {
|
|
|
629646
629967
|
if (!_lastTodos || _lastTodos.length === 0) return 0;
|
|
629647
629968
|
const allDone = _lastTodos.every((t2) => t2 && t2.status === "completed");
|
|
629648
629969
|
if (allDone) return 0;
|
|
629649
|
-
const itemRows =
|
|
629970
|
+
const itemRows = selectPanelTodoRows(_lastTodos, MAX_VISIBLE_ROWS - 1).rowCount;
|
|
629650
629971
|
return 1 + itemRows;
|
|
629651
629972
|
}
|
|
629652
629973
|
function applyHeightChange(nextHeight) {
|
|
@@ -629724,9 +630045,9 @@ function buildTodoProgressBar(todos, maxWidth) {
|
|
|
629724
630045
|
if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET3}`;
|
|
629725
630046
|
return out;
|
|
629726
630047
|
}
|
|
629727
|
-
function
|
|
630048
|
+
function buildTodoTreeIndex(todos) {
|
|
629728
630049
|
const byParent = /* @__PURE__ */ new Map();
|
|
629729
|
-
const byId = new
|
|
630050
|
+
const byId = new Map(todos.map((t2) => [t2.id, t2]));
|
|
629730
630051
|
const roots = [];
|
|
629731
630052
|
for (const todo of todos) {
|
|
629732
630053
|
if (todo.parentId && byId.has(todo.parentId)) {
|
|
@@ -629737,20 +630058,146 @@ function orderTodosForDisplay(todos) {
|
|
|
629737
630058
|
roots.push(todo);
|
|
629738
630059
|
}
|
|
629739
630060
|
}
|
|
630061
|
+
return { byId, byParent, roots };
|
|
630062
|
+
}
|
|
630063
|
+
function childLeafStats(todo, index) {
|
|
630064
|
+
const children2 = index.byParent.get(todo.id) ?? [];
|
|
630065
|
+
if (children2.length === 0) return void 0;
|
|
630066
|
+
let completed = 0;
|
|
630067
|
+
let total = 0;
|
|
630068
|
+
const seen = /* @__PURE__ */ new Set();
|
|
630069
|
+
const visit = (node) => {
|
|
630070
|
+
if (seen.has(node.id)) return;
|
|
630071
|
+
seen.add(node.id);
|
|
630072
|
+
const nested = index.byParent.get(node.id) ?? [];
|
|
630073
|
+
if (nested.length === 0) {
|
|
630074
|
+
total++;
|
|
630075
|
+
if (node.status === "completed") completed++;
|
|
630076
|
+
return;
|
|
630077
|
+
}
|
|
630078
|
+
for (const child of nested) visit(child);
|
|
630079
|
+
};
|
|
630080
|
+
for (const child of children2) visit(child);
|
|
630081
|
+
return { completed, total };
|
|
630082
|
+
}
|
|
630083
|
+
function displayStatusFor(todo, index, seen = /* @__PURE__ */ new Set()) {
|
|
630084
|
+
if (seen.has(todo.id)) return todo.status;
|
|
630085
|
+
seen.add(todo.id);
|
|
630086
|
+
const children2 = index.byParent.get(todo.id) ?? [];
|
|
630087
|
+
if (children2.length === 0) return todo.status;
|
|
630088
|
+
const childStatuses = children2.map((child) => displayStatusFor(child, index, new Set(seen)));
|
|
630089
|
+
if (childStatuses.some((status) => status === "blocked")) return "blocked";
|
|
630090
|
+
if (childStatuses.some((status) => status === "in_progress")) return "in_progress";
|
|
630091
|
+
if (childStatuses.every((status) => status === "completed")) return "completed";
|
|
630092
|
+
if (todo.status === "completed") return "in_progress";
|
|
630093
|
+
return todo.status;
|
|
630094
|
+
}
|
|
630095
|
+
function toDisplayTodo(todo, depth, index) {
|
|
630096
|
+
return {
|
|
630097
|
+
...todo,
|
|
630098
|
+
depth: Math.min(depth, 4),
|
|
630099
|
+
displayStatus: displayStatusFor(todo, index),
|
|
630100
|
+
childSummary: childLeafStats(todo, index)
|
|
630101
|
+
};
|
|
630102
|
+
}
|
|
630103
|
+
function orderTodosForDisplay(todos) {
|
|
630104
|
+
const index = buildTodoTreeIndex(todos);
|
|
629740
630105
|
const out = [];
|
|
629741
630106
|
const seen = /* @__PURE__ */ new Set();
|
|
629742
630107
|
const visit = (todo, depth) => {
|
|
629743
630108
|
if (seen.has(todo.id)) return;
|
|
629744
630109
|
seen.add(todo.id);
|
|
629745
|
-
out.push(
|
|
629746
|
-
for (const child of byParent.get(todo.id) ?? []) {
|
|
630110
|
+
out.push(toDisplayTodo(todo, depth, index));
|
|
630111
|
+
for (const child of index.byParent.get(todo.id) ?? []) {
|
|
629747
630112
|
visit(child, depth + 1);
|
|
629748
630113
|
}
|
|
629749
630114
|
};
|
|
629750
|
-
for (const root of roots) visit(root, 0);
|
|
630115
|
+
for (const root of index.roots) visit(root, 0);
|
|
629751
630116
|
for (const todo of todos) visit(todo, 0);
|
|
629752
630117
|
return out;
|
|
629753
630118
|
}
|
|
630119
|
+
function leafTodosForProgress(todos) {
|
|
630120
|
+
const index = buildTodoTreeIndex(todos);
|
|
630121
|
+
const leaves = todos.filter((todo) => (index.byParent.get(todo.id) ?? []).length === 0);
|
|
630122
|
+
return leaves.length > 0 ? leaves : todos;
|
|
630123
|
+
}
|
|
630124
|
+
function activeDisplayIndex(displayTodos) {
|
|
630125
|
+
const inProgressLeaf = displayTodos.findIndex(
|
|
630126
|
+
(todo, index, all2) => todo.status === "in_progress" && !all2.some((candidate) => candidate.parentId === todo.id)
|
|
630127
|
+
);
|
|
630128
|
+
if (inProgressLeaf >= 0) return inProgressLeaf;
|
|
630129
|
+
const inProgress = displayTodos.findIndex((todo) => todo.displayStatus === "in_progress");
|
|
630130
|
+
if (inProgress >= 0) return inProgress;
|
|
630131
|
+
const blocked = displayTodos.findIndex((todo) => todo.displayStatus === "blocked");
|
|
630132
|
+
if (blocked >= 0) return blocked;
|
|
630133
|
+
return displayTodos.findIndex((todo) => todo.displayStatus === "pending");
|
|
630134
|
+
}
|
|
630135
|
+
function selectFocusedTodoItems(todos, maxItems) {
|
|
630136
|
+
const displayTodos = orderTodosForDisplay(todos);
|
|
630137
|
+
if (maxItems <= 0) return [];
|
|
630138
|
+
if (displayTodos.length <= maxItems) return displayTodos;
|
|
630139
|
+
const activeIndex = activeDisplayIndex(displayTodos);
|
|
630140
|
+
if (activeIndex < 0 || activeIndex < maxItems) return displayTodos.slice(0, maxItems);
|
|
630141
|
+
const rawIndex = buildTodoTreeIndex(todos);
|
|
630142
|
+
const displayById = new Map(displayTodos.map((todo) => [todo.id, todo]));
|
|
630143
|
+
const selected = [];
|
|
630144
|
+
const seen = /* @__PURE__ */ new Set();
|
|
630145
|
+
const add3 = (todo) => {
|
|
630146
|
+
if (!todo || selected.length >= maxItems || seen.has(todo.id)) return;
|
|
630147
|
+
selected.push(todo);
|
|
630148
|
+
seen.add(todo.id);
|
|
630149
|
+
};
|
|
630150
|
+
const active = displayTodos[activeIndex];
|
|
630151
|
+
const ancestors = [];
|
|
630152
|
+
let cursor = active;
|
|
630153
|
+
while (cursor?.parentId && rawIndex.byId.has(cursor.parentId)) {
|
|
630154
|
+
const parent = rawIndex.byId.get(cursor.parentId);
|
|
630155
|
+
if (!parent) break;
|
|
630156
|
+
const displayParent = displayById.get(parent.id);
|
|
630157
|
+
if (displayParent) ancestors.unshift(displayParent);
|
|
630158
|
+
cursor = parent;
|
|
630159
|
+
}
|
|
630160
|
+
for (const ancestor of ancestors) add3(ancestor);
|
|
630161
|
+
const siblings = active.parentId ? rawIndex.byParent.get(active.parentId) ?? [] : rawIndex.roots;
|
|
630162
|
+
const activeSiblingIndex = siblings.findIndex((todo) => todo.id === active.id);
|
|
630163
|
+
if (activeSiblingIndex > 0) add3(displayById.get(siblings[activeSiblingIndex - 1].id));
|
|
630164
|
+
add3(active);
|
|
630165
|
+
for (let i2 = activeSiblingIndex + 1; i2 < siblings.length && selected.length < maxItems; i2++) {
|
|
630166
|
+
add3(displayById.get(siblings[i2].id));
|
|
630167
|
+
}
|
|
630168
|
+
const activeChildren = rawIndex.byParent.get(active.id) ?? [];
|
|
630169
|
+
for (const child of activeChildren) add3(displayById.get(child.id));
|
|
630170
|
+
for (let i2 = activeIndex + 1; i2 < displayTodos.length && selected.length < maxItems; i2++) {
|
|
630171
|
+
add3(displayTodos[i2]);
|
|
630172
|
+
}
|
|
630173
|
+
for (let i2 = activeIndex - 1; i2 >= 0 && selected.length < maxItems; i2--) {
|
|
630174
|
+
add3(displayTodos[i2]);
|
|
630175
|
+
}
|
|
630176
|
+
return selected;
|
|
630177
|
+
}
|
|
630178
|
+
function selectPanelTodoRows(todos, maxRows) {
|
|
630179
|
+
const displayTodos = orderTodosForDisplay(todos);
|
|
630180
|
+
if (maxRows <= 0 || displayTodos.length === 0) {
|
|
630181
|
+
return { items: [], omitted: displayTodos.length, rowCount: 0 };
|
|
630182
|
+
}
|
|
630183
|
+
if (displayTodos.length <= maxRows) {
|
|
630184
|
+
return { items: displayTodos, omitted: 0, rowCount: displayTodos.length };
|
|
630185
|
+
}
|
|
630186
|
+
const itemBudget = Math.max(1, maxRows - 1);
|
|
630187
|
+
const items = selectFocusedTodoItems(todos, itemBudget);
|
|
630188
|
+
const omitted = Math.max(0, displayTodos.length - items.length);
|
|
630189
|
+
return {
|
|
630190
|
+
items,
|
|
630191
|
+
omitted,
|
|
630192
|
+
rowCount: Math.min(maxRows, items.length + (omitted > 0 ? 1 : 0))
|
|
630193
|
+
};
|
|
630194
|
+
}
|
|
630195
|
+
function __testOrderTodosForDisplay(todos) {
|
|
630196
|
+
return orderTodosForDisplay(todos);
|
|
630197
|
+
}
|
|
630198
|
+
function __testSelectPanelTodoRows(todos, maxRows) {
|
|
630199
|
+
return selectPanelTodoRows(todos, maxRows);
|
|
630200
|
+
}
|
|
629754
630201
|
function render() {
|
|
629755
630202
|
if (!_enabled) return;
|
|
629756
630203
|
if (!panelEffectivelyVisible()) {
|
|
@@ -629766,29 +630213,30 @@ function render() {
|
|
|
629766
630213
|
}
|
|
629767
630214
|
const L = layout();
|
|
629768
630215
|
const cols = Math.max(20, termCols());
|
|
629769
|
-
const
|
|
629770
|
-
const
|
|
630216
|
+
const progressTodos = leafTodosForProgress(_lastTodos);
|
|
630217
|
+
const completed = progressTodos.filter((t2) => t2.status === "completed").length;
|
|
630218
|
+
const total = progressTodos.length;
|
|
629771
630219
|
const headerColor = ACCENT;
|
|
629772
630220
|
const lines = [];
|
|
629773
630221
|
const headerPrefix = `tasks ${headerColor}${completed}/${total}${RESET3} `;
|
|
629774
630222
|
const headerPrefixWidth = visualLen(headerPrefix);
|
|
629775
630223
|
const maxBarWidth = Math.max(0, cols - 2 - headerPrefixWidth);
|
|
629776
|
-
const progressBar = buildTodoProgressBar(
|
|
630224
|
+
const progressBar = buildTodoProgressBar(progressTodos, maxBarWidth);
|
|
629777
630225
|
const headerText = `${headerPrefix}${progressBar}`;
|
|
629778
630226
|
lines.push(headerText);
|
|
629779
|
-
const
|
|
629780
|
-
const visible =
|
|
630227
|
+
const selection = selectPanelTodoRows(_lastTodos, MAX_VISIBLE_ROWS - 1);
|
|
630228
|
+
const visible = selection.items;
|
|
629781
630229
|
for (const t2 of visible) {
|
|
629782
|
-
const { mark, color } = statusToAnsi(t2.
|
|
630230
|
+
const { mark, color } = statusToAnsi(t2.displayStatus);
|
|
629783
630231
|
const contentWidth = Math.max(4, cols - 8);
|
|
629784
|
-
const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth
|
|
629785
|
-
const
|
|
630232
|
+
const indent2 = t2.depth > 0 ? `${" ".repeat(t2.depth)}- ` : "";
|
|
630233
|
+
const childSummary = t2.childSummary && t2.childSummary.total > 0 ? ` [${t2.childSummary.completed}/${t2.childSummary.total}]` : "";
|
|
630234
|
+
const contentText = indent2 + t2.content + childSummary + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
|
|
629786
630235
|
const truncated = truncate2(contentText, contentWidth);
|
|
629787
630236
|
lines.push(`${color}${mark}${RESET3} ${color}${truncated}${RESET3}`);
|
|
629788
630237
|
}
|
|
629789
|
-
if (
|
|
629790
|
-
|
|
629791
|
-
lines[lines.length - 1] = `${DIM_LABEL}… +${more} more${RESET3}`;
|
|
630238
|
+
if (selection.omitted > 0) {
|
|
630239
|
+
lines.push(`${DIM_LABEL}… +${selection.omitted} more${RESET3}`);
|
|
629792
630240
|
}
|
|
629793
630241
|
let out = HIDE + SAVE;
|
|
629794
630242
|
const newTop = L.tasksTop;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.454",
|
|
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.454",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED