omnius 1.0.387 → 1.0.389

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -25024,7 +25024,7 @@ var init_list_directory = __esm({
25024
25024
  ListDirectoryTool = class {
25025
25025
  name = "list_directory";
25026
25026
  aliases = ["ls", "dir"];
25027
- description = "List files and directories at a given path. Shows file sizes and types. Output includes full relative paths you can use directly in subsequent tool calls.";
25027
+ description = "List files and directories at a given path. Shows file sizes and types. Output includes full relative paths.";
25028
25028
  parameters = {
25029
25029
  type: "object",
25030
25030
  properties: {
@@ -25075,20 +25075,12 @@ var init_list_directory = __esm({
25075
25075
  }
25076
25076
  if (dirs.length > 0 || files.length > 0) {
25077
25077
  lines.push("");
25078
- lines.push("Next steps use these exact paths:");
25078
+ lines.push("Discovered child paths (data only):");
25079
25079
  if (dirs.length > 0) {
25080
- for (const d2 of dirs.slice(0, 8)) {
25081
- lines.push(` list_directory("${d2}")`);
25082
- }
25083
- if (dirs.length > 8)
25084
- lines.push(` ... and ${dirs.length - 8} more directories`);
25080
+ lines.push(` directories: ${dirs.slice(0, 8).join(", ")}${dirs.length > 8 ? ` (+${dirs.length - 8} more)` : ""}`);
25085
25081
  }
25086
25082
  if (files.length > 0) {
25087
- for (const f2 of files.slice(0, 5)) {
25088
- lines.push(` file_read("${f2}")`);
25089
- }
25090
- if (files.length > 5)
25091
- lines.push(` ... and ${files.length - 5} more files`);
25083
+ lines.push(` files: ${files.slice(0, 8).join(", ")}${files.length > 8 ? ` (+${files.length - 8} more)` : ""}`);
25092
25084
  }
25093
25085
  }
25094
25086
  return {
@@ -25126,7 +25118,7 @@ var init_list_directory = __esm({
25126
25118
  const remaining = entries.filter((entry) => !kgSummaries.includes(entry));
25127
25119
  const syntheticLines = [
25128
25120
  `g ${dirPath}/kg-summary-*.md ${kgSummaries.length} generated summaries collapsed, ${totalBytes} bytes`,
25129
- ` latest generated summary: file_read("${latestPath}")`
25121
+ ` latest generated summary path: ${latestPath}`
25130
25122
  ];
25131
25123
  return { entries: remaining, syntheticLines };
25132
25124
  }
@@ -572467,11 +572459,86 @@ function computeEffectiveThink(params) {
572467
572459
  return params.defaultThink;
572468
572460
  }
572469
572461
  function sanitizeHistoryThink(messages2) {
572470
- return messages2.map((m2) => {
572462
+ const referencedToolCallIds = /* @__PURE__ */ new Set();
572463
+ for (const message2 of messages2) {
572464
+ if (message2.role === "tool" && message2.tool_call_id) {
572465
+ referencedToolCallIds.add(message2.tool_call_id);
572466
+ }
572467
+ }
572468
+ const stripped = messages2.map((m2) => {
572471
572469
  if (m2.role !== "assistant" || typeof m2.content !== "string")
572472
572470
  return m2;
572473
572471
  return { ...m2, content: stripThinkBlocks(m2.content) };
572474
572472
  });
572473
+ const sanitized = [];
572474
+ for (let i2 = 0; i2 < stripped.length; i2++) {
572475
+ const current = stripped[i2];
572476
+ const next = stripped[i2 + 1];
572477
+ if (isOrphanDuplicateAssistantToolAnchor(current, next, referencedToolCallIds)) {
572478
+ continue;
572479
+ }
572480
+ sanitized.push(current);
572481
+ }
572482
+ return sanitized;
572483
+ }
572484
+ function isOrphanDuplicateAssistantToolAnchor(current, next, referencedToolCallIds) {
572485
+ if (!next)
572486
+ return false;
572487
+ if (current.role !== "assistant" || next.role !== "assistant")
572488
+ return false;
572489
+ if (!Array.isArray(current.tool_calls) || !Array.isArray(next.tool_calls)) {
572490
+ return false;
572491
+ }
572492
+ if (current.tool_calls.length === 0 || next.tool_calls.length === 0) {
572493
+ return false;
572494
+ }
572495
+ if (assistantVisibleText(current).length > 0)
572496
+ return false;
572497
+ if (toolCallsFingerprint(current.tool_calls) !== toolCallsFingerprint(next.tool_calls)) {
572498
+ return false;
572499
+ }
572500
+ const currentIds = current.tool_calls.map((call) => call.id).filter(Boolean);
572501
+ const nextIds = next.tool_calls.map((call) => call.id).filter(Boolean);
572502
+ const currentReferenced = currentIds.some((id) => referencedToolCallIds.has(id));
572503
+ const nextReferenced = nextIds.some((id) => referencedToolCallIds.has(id));
572504
+ return !currentReferenced && (nextReferenced || currentIds.length > 0);
572505
+ }
572506
+ function assistantVisibleText(message2) {
572507
+ if (typeof message2.content === "string")
572508
+ return message2.content.trim();
572509
+ if (!Array.isArray(message2.content))
572510
+ return "";
572511
+ return message2.content.map((part) => part.type === "text" ? part.text ?? "" : "").join("").trim();
572512
+ }
572513
+ function toolCallsFingerprint(calls) {
572514
+ return calls.map((call) => {
572515
+ const args = normalizeToolCallArgumentString(call.function.arguments);
572516
+ return `${call.function.name}:${args}`;
572517
+ }).join("|");
572518
+ }
572519
+ function normalizeToolCallArgumentString(args) {
572520
+ try {
572521
+ return JSON.stringify(JSON.parse(args));
572522
+ } catch {
572523
+ return args.replace(/\s+/g, " ").trim();
572524
+ }
572525
+ }
572526
+ function normalizeEditComparable(text2) {
572527
+ return text2.replace(/\r\n/g, "\n").trim();
572528
+ }
572529
+ function previewInline(text2, max = 140) {
572530
+ const normalized = text2.replace(/\s+/g, " ").trim();
572531
+ if (normalized.length <= max)
572532
+ return JSON.stringify(normalized);
572533
+ return JSON.stringify(`${normalized.slice(0, max - 18)}...#${quickTextHash(normalized)}`);
572534
+ }
572535
+ function quickTextHash(input) {
572536
+ let hash = 2166136261;
572537
+ for (let index = 0; index < input.length; index++) {
572538
+ hash ^= input.charCodeAt(index);
572539
+ hash = Math.imul(hash, 16777619) >>> 0;
572540
+ }
572541
+ return hash.toString(16).padStart(8, "0");
572475
572542
  }
572476
572543
  function applyMemoryPrefixToMessages(messages2, memoryPrefix) {
572477
572544
  const prefix = memoryPrefix?.trim();
@@ -572946,6 +573013,8 @@ var init_agenticRunner = __esm({
572946
573013
  _completionCaveat = null;
572947
573014
  _completionLedger = null;
572948
573015
  _staleEditFamilies = /* @__PURE__ */ new Map();
573016
+ _lastReadTurnByPathKey = /* @__PURE__ */ new Map();
573017
+ _recentSuccessfulReplacements = [];
572949
573018
  // ── WO-AM-01/04/10: Associative memory stores ──
572950
573019
  // Episode store: every tool call → persistent episode with importance + decay
572951
573020
  // Temporal KG: entities + relations with temporal validity (valid_from/valid_until)
@@ -573194,7 +573263,7 @@ var init_agenticRunner = __esm({
573194
573263
  if (this.options.subAgent || this.options.recursionDepth > 0)
573195
573264
  return [];
573196
573265
  const normalizedGoal = goal.replace(/\s+/g, " ").trim();
573197
- if (normalizedGoal.length < 120)
573266
+ if (normalizedGoal.length < 120 && !this._isContinuationResumeGoal(normalizedGoal))
573198
573267
  return [];
573199
573268
  return [
573200
573269
  {
@@ -573258,6 +573327,44 @@ var init_agenticRunner = __esm({
573258
573327
  }
573259
573328
  ];
573260
573329
  }
573330
+ _isContinuationResumeGoal(goal) {
573331
+ const tokens = goal.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter(Boolean);
573332
+ if (tokens.length === 0 || tokens.length > 14)
573333
+ return false;
573334
+ const continuationTerms = /* @__PURE__ */ new Set([
573335
+ "again",
573336
+ "and",
573337
+ "back",
573338
+ "carry",
573339
+ "complete",
573340
+ "continue",
573341
+ "finish",
573342
+ "from",
573343
+ "last",
573344
+ "latest",
573345
+ "left",
573346
+ "my",
573347
+ "off",
573348
+ "please",
573349
+ "previous",
573350
+ "prior",
573351
+ "proceed",
573352
+ "request",
573353
+ "resume",
573354
+ "task",
573355
+ "that",
573356
+ "the",
573357
+ "this",
573358
+ "to",
573359
+ "try",
573360
+ "work"
573361
+ ]);
573362
+ const hasResumeVerb = tokens.some((token) => token === "continue" || token === "resume" || token === "proceed" || token === "complete" || token === "finish");
573363
+ if (!hasResumeVerb)
573364
+ return false;
573365
+ const continuationTokenCount = tokens.filter((token) => continuationTerms.has(token)).length;
573366
+ return continuationTokenCount / tokens.length >= 0.7;
573367
+ }
573261
573368
  /**
573262
573369
  * Build a compact workboard context string for injection into the
573263
573370
  * system prompt. Returns null when no active board exists or when
@@ -578763,6 +578870,8 @@ Respond with your assessment, then take action.`;
578763
578870
  this._completionLedger = null;
578764
578871
  this._focusTerminalLedgerRecorded = false;
578765
578872
  this._staleEditFamilies.clear();
578873
+ this._lastReadTurnByPathKey.clear();
578874
+ this._recentSuccessfulReplacements = [];
578766
578875
  this._lastWorldStateTurn = -1;
578767
578876
  this._fileWritesSinceLastWorldState = 0;
578768
578877
  this._resetVisualEvidenceState();
@@ -581153,7 +581262,7 @@ ${memoryLines.join("\n")}`
581153
581262
  this.proactivePrune(compacted, turn);
581154
581263
  this.microcompact(compacted, recentToolResults);
581155
581264
  this._insertContextFrame(compacted, await this._buildTurnContextFrame(turn, compacted, recentToolResults, environmentBlock));
581156
- let requestMessages = compacted;
581265
+ let requestMessages = sanitizeHistoryThink(compacted);
581157
581266
  {
581158
581267
  const _limits = this.contextLimits();
581159
581268
  const ceInput = {
@@ -581189,6 +581298,7 @@ ${memoryLines.join("\n")}`
581189
581298
  }
581190
581299
  }
581191
581300
  const { maxOutputTokens: effectiveMaxTokens } = this.contextLimits();
581301
+ requestMessages = sanitizeHistoryThink(requestMessages);
581192
581302
  const chatRequest = {
581193
581303
  messages: requestMessages,
581194
581304
  tools: toolDefs,
@@ -582148,6 +582258,69 @@ Use the saved fact to continue the promised synthesis or next concrete step, or
582148
582258
  systemGuidance: staleRewriteOutput
582149
582259
  };
582150
582260
  }
582261
+ const editReversalBlock = this.editReversalPreflightBlock(tc.name, tc.arguments ?? {}, turn);
582262
+ if (editReversalBlock) {
582263
+ const focusDecision2 = this._focusSupervisor?.evaluateProposedCall({
582264
+ turn,
582265
+ toolName: tc.name,
582266
+ args: tc.arguments ?? {},
582267
+ fingerprint: toolFingerprint,
582268
+ isReadLike: false,
582269
+ stalePreflightMessage: editReversalBlock,
582270
+ context: this._buildFocusContextSnapshot()
582271
+ });
582272
+ const reversalOutput = focusDecision2?.kind === "block_tool_call" ? focusDecision2.message : editReversalBlock;
582273
+ if (focusDecision2 && focusDecision2.kind !== "pass") {
582274
+ this._emitFocusSupervisorEvent({
582275
+ decision: focusDecision2.kind,
582276
+ state: focusDecision2.state,
582277
+ directiveId: focusDecision2.directive.id,
582278
+ reason: focusDecision2.directive.reason,
582279
+ requiredNextAction: focusDecision2.directive.requiredNextAction,
582280
+ blockedTool: tc.name,
582281
+ turn
582282
+ });
582283
+ this._maybeMarkFocusTerminalIncomplete({
582284
+ decision: focusDecision2,
582285
+ blockedTool: tc.name,
582286
+ turn
582287
+ });
582288
+ }
582289
+ this.emit({
582290
+ type: "tool_call",
582291
+ toolName: tc.name,
582292
+ toolArgs: tc.arguments,
582293
+ turn,
582294
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
582295
+ });
582296
+ this.emit({
582297
+ type: "tool_result",
582298
+ toolName: tc.name,
582299
+ success: false,
582300
+ content: reversalOutput.slice(0, 120),
582301
+ turn,
582302
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
582303
+ });
582304
+ this._tagSyntheticFailure({
582305
+ mode: "step_repetition",
582306
+ rationale: "edit attempted to reverse a recent successful replacement without fresh evidence"
582307
+ });
582308
+ if (this._completionLedger) {
582309
+ this._completionLedger = recordToolEvidence(this._completionLedger, {
582310
+ name: tc.name,
582311
+ success: false,
582312
+ outputPreview: reversalOutput.slice(0, 500),
582313
+ argsKey: tc.arguments ? JSON.stringify(tc.arguments).slice(0, 300) : ""
582314
+ });
582315
+ this._saveCompletionLedgerSafe();
582316
+ }
582317
+ return {
582318
+ tc,
582319
+ output: reversalOutput,
582320
+ success: false,
582321
+ systemGuidance: reversalOutput
582322
+ };
582323
+ }
582151
582324
  const baseIsReadLike = ![
582152
582325
  "file_write",
582153
582326
  "file_edit",
@@ -583782,6 +583955,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
583782
583955
  const filePath = typeof tc.arguments?.path === "string" ? tc.arguments.path : "";
583783
583956
  recordToolExecution(this._appState, tc.name, performance.now() - toolStart, result.success, filePath || void 0);
583784
583957
  this.noteStaleEditGuardOutcome(tc.name, tc.arguments ?? {}, result, turn);
583958
+ this.noteEditReversalOutcome(tc.name, tc.arguments ?? {}, result, turn);
583785
583959
  {
583786
583960
  const beforeDirective = this._focusSupervisor?.snapshot().directive?.id ?? null;
583787
583961
  this._focusSupervisor?.observeToolResult({
@@ -584846,8 +585020,9 @@ ${this.options.maxTurns && this.options.maxTurns > 0 ? `You have ${this.options.
584846
585020
  }
584847
585021
  }
584848
585022
  this._insertContextFrame(compactedMsgs, await this._buildTurnContextFrame(turn, compactedMsgs, void 0, bfEnvironmentBlock));
585023
+ const modelFacingMessages = sanitizeHistoryThink(compactedMsgs);
584849
585024
  const chatRequest = {
584850
- messages: compactedMsgs,
585025
+ messages: modelFacingMessages,
584851
585026
  tools: toolDefs,
584852
585027
  temperature: this.options.temperature,
584853
585028
  maxTokens: this.options.maxTokens,
@@ -586431,6 +586606,94 @@ ${marker}` : marker);
586431
586606
  `Stale target preview: ${active.preview}`
586432
586607
  ].join("\n");
586433
586608
  }
586609
+ editReversalPreflightBlock(toolName, args, turn) {
586610
+ if (process.env["OMNIUS_ALLOW_BLIND_EDIT_REVERSAL"] === "1")
586611
+ return null;
586612
+ const replacements = this.extractReplacementEdits(toolName, args);
586613
+ if (replacements.length === 0)
586614
+ return null;
586615
+ for (const replacement of replacements) {
586616
+ const prior = [...this._recentSuccessfulReplacements].reverse().find((entry) => entry.pathKey === replacement.pathKey && entry.oldHash === replacement.newHash && entry.newHash === replacement.oldHash && turn - entry.turn <= 8);
586617
+ if (!prior)
586618
+ continue;
586619
+ const lastReadTurn = this._lastReadTurnByPathKey.get(replacement.pathKey) ?? -1;
586620
+ if (lastReadTurn > prior.turn)
586621
+ continue;
586622
+ return [
586623
+ `[EDIT REVERSAL BLOCKED] ${toolName} would exactly reverse a recent successful edit on ${replacement.path}.`,
586624
+ `Prior edit at turn ${prior.turn}: ${previewInline(prior.oldText)} -> ${previewInline(prior.newText)}.`,
586625
+ `Proposed edit: ${previewInline(replacement.oldText)} -> ${previewInline(replacement.newText)}.`,
586626
+ ``,
586627
+ `Do not toggle a target back and forth after verification fails. First file_read the authoritative current file once, then choose one:`,
586628
+ `1. make a different edit based on the fresh file and the latest failure output,`,
586629
+ `2. run a verification command if the current file is already correct, or`,
586630
+ `3. report incomplete/blocked with the concrete failing evidence.`
586631
+ ].join("\n");
586632
+ }
586633
+ return null;
586634
+ }
586635
+ noteEditReversalOutcome(toolName, args, result, turn) {
586636
+ const path12 = this.extractPrimaryToolPath(args);
586637
+ if (toolName === "file_read" && path12 && result.success) {
586638
+ this._lastReadTurnByPathKey.set(this.staleEditPathKey(path12), turn);
586639
+ return;
586640
+ }
586641
+ if (!result.success || result.mutated === false)
586642
+ return;
586643
+ const replacements = this.extractReplacementEdits(toolName, args);
586644
+ if (replacements.length === 0)
586645
+ return;
586646
+ for (const replacement of replacements) {
586647
+ this._recentSuccessfulReplacements.push({
586648
+ ...replacement,
586649
+ tool: toolName,
586650
+ turn
586651
+ });
586652
+ }
586653
+ if (this._recentSuccessfulReplacements.length > 40) {
586654
+ this._recentSuccessfulReplacements = this._recentSuccessfulReplacements.slice(-40);
586655
+ }
586656
+ }
586657
+ extractReplacementEdits(toolName, args) {
586658
+ if (!args)
586659
+ return [];
586660
+ const out = [];
586661
+ const add3 = (path12, oldText, newText) => {
586662
+ if (typeof path12 !== "string" || !path12.trim())
586663
+ return;
586664
+ if (typeof oldText !== "string" || typeof newText !== "string")
586665
+ return;
586666
+ const normalizedOld = normalizeEditComparable(oldText);
586667
+ const normalizedNew = normalizeEditComparable(newText);
586668
+ if (!normalizedOld || !normalizedNew || normalizedOld === normalizedNew)
586669
+ return;
586670
+ out.push({
586671
+ path: path12.trim(),
586672
+ pathKey: this.staleEditPathKey(path12),
586673
+ oldText: normalizedOld,
586674
+ newText: normalizedNew,
586675
+ oldHash: this.quickHash(normalizedOld),
586676
+ newHash: this.quickHash(normalizedNew)
586677
+ });
586678
+ };
586679
+ if (toolName === "file_edit") {
586680
+ add3(this.extractPrimaryToolPath(args), args["old_string"] ?? args["oldString"] ?? args["oldText"], args["new_string"] ?? args["newString"] ?? args["newText"]);
586681
+ return out;
586682
+ }
586683
+ if (toolName === "batch_edit") {
586684
+ const fallbackPath = this.extractPrimaryToolPath(args);
586685
+ const edits = args["edits"];
586686
+ if (!Array.isArray(edits))
586687
+ return out;
586688
+ for (const edit of edits) {
586689
+ if (!edit || typeof edit !== "object")
586690
+ continue;
586691
+ const rec = edit;
586692
+ add3(rec["path"] ?? rec["file"] ?? rec["filePath"] ?? rec["file_path"] ?? fallbackPath, rec["old_string"] ?? rec["oldString"] ?? rec["oldText"], rec["new_string"] ?? rec["newString"] ?? rec["newText"]);
586693
+ }
586694
+ }
586695
+ return out;
586696
+ }
586434
586697
  noteStaleEditGuardOutcome(toolName, args, result, turn) {
586435
586698
  const path12 = this.extractPrimaryToolPath(args);
586436
586699
  const pathKey = path12 ? this.staleEditPathKey(path12) : "";
@@ -614782,6 +615045,129 @@ function loadSessionContext(repoRoot) {
614782
615045
  return null;
614783
615046
  }
614784
615047
  }
615048
+ function readJsonOrNull(filePath) {
615049
+ try {
615050
+ if (!existsSync113(filePath)) return null;
615051
+ return JSON.parse(readFileSync90(filePath, "utf-8"));
615052
+ } catch {
615053
+ return null;
615054
+ }
615055
+ }
615056
+ function isManualSessionEntry(entry) {
615057
+ const task = cleanPromptForDiary(entry.task).toLowerCase();
615058
+ const summary = normalizeSessionText(entry.summary || entry.assistantResponse, 160).toLowerCase();
615059
+ return entry.source === "manual" || task === "(manual save)" || entry.toolCalls === 0 && summary.startsWith("manual context save");
615060
+ }
615061
+ function isDeicticContinuationGoal(goal) {
615062
+ const tokens = normalizeSessionText(cleanPromptForDiary(goal), 180).toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter(Boolean);
615063
+ if (tokens.length === 0 || tokens.length > 14) return false;
615064
+ const hasResumeVerb = tokens.some(
615065
+ (token) => token === "continue" || token === "resume" || token === "proceed" || token === "complete" || token === "finish"
615066
+ );
615067
+ if (!hasResumeVerb) return false;
615068
+ const deicticCount = tokens.filter((token) => DEICTIC_CONTINUATION_TERMS.has(token)).length;
615069
+ return deicticCount / tokens.length >= 0.7;
615070
+ }
615071
+ function readRestoreWorkboard(repoRoot, runId) {
615072
+ if (!runId) return null;
615073
+ return readJsonOrNull(
615074
+ join127(repoRoot, OMNIUS_DIR, "workboards", runId, "active.json")
615075
+ );
615076
+ }
615077
+ function scoreRestoreLedger(ledger, workboard) {
615078
+ const status = ledger.status || "open";
615079
+ if (status === "approved") return -1e3;
615080
+ const evidence = ledger.evidence ?? [];
615081
+ const unresolvedCount = ledger.unresolved?.length ?? 0;
615082
+ const activeCards = (workboard?.cards ?? []).filter(
615083
+ (card) => card.status === "open" || card.status === "in_progress" || card.status === "needs_changes" || card.status === "blocked"
615084
+ ).length;
615085
+ const mutationTools = /* @__PURE__ */ new Set(["file_edit", "file_patch", "batch_edit", "file_write"]);
615086
+ const mutationEvidence = evidence.filter((item) => mutationTools.has(item.toolName || "")).length;
615087
+ const failedMutationEvidence = evidence.filter(
615088
+ (item) => mutationTools.has(item.toolName || "") && item.success === false
615089
+ ).length;
615090
+ const targetPathEvidence = evidence.filter((item) => (item.targetPaths?.length ?? 0) > 0).length;
615091
+ const blockedEvidence = evidence.filter((item) => {
615092
+ const summary = String(item.summary ?? "").toLowerCase();
615093
+ return summary.includes("[focus supervisor block]") || summary.includes("stale") || summary.includes("blocked");
615094
+ }).length;
615095
+ let score = 0;
615096
+ if (status === "open") score += 10;
615097
+ else if (status === "incomplete_verification") score += 18;
615098
+ else if (status === "request_changes" || status === "blocked") score += 16;
615099
+ else score += 4;
615100
+ score += Math.min(evidence.length, 20);
615101
+ score += Math.min(unresolvedCount, 10) * 4;
615102
+ score += Math.min(activeCards, 8) * 3;
615103
+ score += Math.min(mutationEvidence, 8) * 5;
615104
+ score += Math.min(failedMutationEvidence, 8) * 3;
615105
+ score += Math.min(targetPathEvidence, 6) * 2;
615106
+ score += Math.min(blockedEvidence, 6) * 2;
615107
+ const goal = normalizeSessionText(ledger.goal, 260);
615108
+ if (goal.length >= 80) score += 5;
615109
+ if (isDeicticContinuationGoal(goal) && mutationEvidence === 0 && unresolvedCount === 0 && activeCards === 0) {
615110
+ score -= 25;
615111
+ }
615112
+ return score;
615113
+ }
615114
+ function selectActiveTaskAnchor(repoRoot) {
615115
+ const ledgerDir = join127(repoRoot, OMNIUS_DIR, "completion-ledgers");
615116
+ try {
615117
+ if (!existsSync113(ledgerDir)) return null;
615118
+ const candidates = readdirSync39(ledgerDir).filter((name10) => name10.endsWith(".json")).map((name10) => {
615119
+ const filePath = join127(ledgerDir, name10);
615120
+ const ledger = readJsonOrNull(filePath);
615121
+ if (!ledger || !ledger.goal) return null;
615122
+ const runId = ledger.runId || name10.replace(/\.json$/, "");
615123
+ const workboard = readRestoreWorkboard(repoRoot, runId);
615124
+ const score = scoreRestoreLedger(ledger, workboard);
615125
+ const mtimeMs = statSync42(filePath).mtimeMs;
615126
+ return { ledger: { ...ledger, runId }, workboard, score, mtimeMs };
615127
+ }).filter((item) => Boolean(item));
615128
+ candidates.sort((a2, b) => b.score - a2.score || b.mtimeMs - a2.mtimeMs);
615129
+ const selected = candidates.find((candidate) => candidate.score >= 18);
615130
+ return selected ?? null;
615131
+ } catch {
615132
+ return null;
615133
+ }
615134
+ }
615135
+ function buildActiveTaskAnchor(repoRoot) {
615136
+ const selected = selectActiveTaskAnchor(repoRoot);
615137
+ if (!selected) return null;
615138
+ const ledger = selected.ledger;
615139
+ const evidence = ledger.evidence ?? [];
615140
+ const selectedEvidence = [...evidence.slice(0, 2), ...evidence.slice(-4)];
615141
+ const seenEvidence = /* @__PURE__ */ new Set();
615142
+ const evidenceLines = selectedEvidence.flatMap((item) => {
615143
+ const summary = normalizeSessionText(item.summary, 240);
615144
+ if (!summary || seenEvidence.has(summary)) return [];
615145
+ seenEvidence.add(summary);
615146
+ const status = item.success === false ? "failed" : item.success === true ? "ok" : "observed";
615147
+ const tool = item.toolName || "evidence";
615148
+ return [`- ${tool} ${status}: ${summary}`];
615149
+ });
615150
+ const unresolvedLines = (ledger.unresolved ?? []).slice(0, 4).map((item) => `- ${normalizeSessionText(item.text, 180)}`).filter((line) => line !== "- ");
615151
+ const boardCards = (selected.workboard?.cards ?? []).filter((card) => card.status !== "completed" && card.status !== "verified").slice(0, 5).map((card) => {
615152
+ const title = normalizeSessionText(card.title || card.id, 120);
615153
+ const status = card.status || "open";
615154
+ const lane = card.lane ? `/${card.lane}` : "";
615155
+ const evidenceCount = Array.isArray(card.evidence) ? card.evidence.length : 0;
615156
+ return `- ${title} (${status}${lane}, evidence=${evidenceCount})`;
615157
+ });
615158
+ return `<active-task-anchor>
615159
+ Source: completion ledger ${ledger.runId || "(unknown run)"}; status=${ledger.status || "open"}
615160
+ Goal: ${normalizeSessionText(ledger.goal, 420)}
615161
+ Recorded evidence: ${evidence.length}; unresolved items: ${ledger.unresolved?.length ?? 0}
615162
+ ` + (evidenceLines.length > 0 ? `Material evidence:
615163
+ ${evidenceLines.join("\n")}
615164
+ ` : "") + (unresolvedLines.length > 0 ? `Unresolved:
615165
+ ${unresolvedLines.join("\n")}
615166
+ ` : "") + (boardCards.length > 0 ? `Active workboard cards:
615167
+ ${boardCards.join("\n")}
615168
+ ` : "") + `Continuation rule: when the user asks to continue, resume this active task unless the new prompt names a different target.
615169
+ </active-task-anchor>`;
615170
+ }
614785
615171
  function formatSessionHistoryDisplay(ctx3) {
614786
615172
  return buildSessionHistoryBoxLines(
614787
615173
  sessionContextToHistoryBoxData(ctx3),
@@ -614810,18 +615196,23 @@ function sessionContextToHistoryBoxData(ctx3, title = "Session History") {
614810
615196
  function buildContextRestorePrompt(repoRoot) {
614811
615197
  const ctx3 = loadSessionContext(repoRoot);
614812
615198
  const handoffPrompt = buildHandoffPrompt(repoRoot);
615199
+ const activeTaskAnchor = buildActiveTaskAnchor(repoRoot);
614813
615200
  if (handoffPrompt) {
615201
+ const usefulEntries2 = (ctx3?.entries ?? []).filter((entry) => !isManualSessionEntry(entry));
614814
615202
  const baseCtx = ctx3 && ctx3.entries.length > 0 ? `
614815
615203
 
614816
615204
  <session-recap>
614817
- Recent tasks: ${ctx3.entries.slice(-3).map(
615205
+ Recent tasks: ${(usefulEntries2.length > 0 ? usefulEntries2 : ctx3.entries).slice(-3).map(
614818
615206
  (e2) => `[${e2.completed ? "done" : "partial"}] ${normalizeSessionText(e2.summary || e2.task, 80)}`
614819
615207
  ).join(", ")}
614820
615208
  </session-recap>` : "";
614821
- return handoffPrompt + baseCtx;
615209
+ return handoffPrompt + (activeTaskAnchor ? `
615210
+
615211
+ ${activeTaskAnchor}` : "") + baseCtx;
614822
615212
  }
614823
- if (!ctx3 || ctx3.entries.length === 0) return null;
614824
- const recent = ctx3.entries.slice(-5);
615213
+ if (!ctx3 || ctx3.entries.length === 0) return activeTaskAnchor;
615214
+ const usefulEntries = ctx3.entries.filter((entry) => !isManualSessionEntry(entry));
615215
+ const recent = (usefulEntries.length > 0 ? usefulEntries : ctx3.entries).slice(-5);
614825
615216
  const chronology = recent.map((e2) => {
614826
615217
  const status = e2.completed ? "done" : "partial";
614827
615218
  const summary = normalizeSessionText(e2.assistantResponse || e2.summary || e2.task, 140);
@@ -614829,8 +615220,8 @@ Recent tasks: ${ctx3.entries.slice(-3).map(
614829
615220
  const files = e2.filesModified && e2.filesModified.length > 0 ? ` | files: ${e2.filesModified.slice(0, 3).join(", ")}` : "";
614830
615221
  return `[${status}] ${summary}${tools}${files}`;
614831
615222
  });
614832
- const last2 = ctx3.entries[ctx3.entries.length - 1];
614833
- const lastCompleted = [...ctx3.entries].reverse().find((entry) => entry.completed);
615223
+ const last2 = recent[recent.length - 1] ?? ctx3.entries[ctx3.entries.length - 1];
615224
+ const lastCompleted = [...usefulEntries].reverse().find((entry) => entry.completed);
614834
615225
  const latestCompleted = lastCompleted ? `Latest completed task: ${normalizeSessionText(lastCompleted.assistantResponse || lastCompleted.summary || lastCompleted.task, 180)}` : "";
614835
615226
  const recentDialogue = recent.slice(-3).map((entry) => {
614836
615227
  const assistant = normalizeSessionText(entry.assistantResponse || entry.summary, 320) || "(no assistant reply captured)";
@@ -614843,15 +615234,18 @@ Provenance: ${last2.provenance} (file_read to expand)` : "";
614843
615234
  const kg = `
614844
615235
  KG summary: .omnius/context/kg-summary/latest.md (file_read to expand; legacy pointer: .omnius/context/kg-summary-latest.md)`;
614845
615236
  return `<session-recap>
614846
- Project chronology (older to newer):
615237
+ ` + (activeTaskAnchor ? `${activeTaskAnchor}
615238
+
615239
+ ` : "") + `Project chronology (older to newer):
614847
615240
  ${chronology.join("\n")}
614848
615241
  ` + (latestCompleted ? `
614849
615242
  ${latestCompleted}
614850
615243
  ` : "\n") + `
614851
615244
  Most recent exchanges (older to newer):
614852
615245
  ${recentDialogue}
614853
- Continue from the latest exchange and do not repeat completed work.${prov}${kg}
614854
- </session-recap>`;
615246
+ ` + (activeTaskAnchor ? `For continuation prompts, resume the active task anchor above; use chronology only to avoid repeating completed work.${prov}${kg}
615247
+ ` : `Continue from the latest exchange and do not repeat completed work.${prov}${kg}
615248
+ `) + `</session-recap>`;
614855
615249
  }
614856
615250
  function getLastTaskSummary(repoRoot) {
614857
615251
  const ctx3 = loadSessionContext(repoRoot);
@@ -615174,7 +615568,7 @@ function deleteUsageRecord(kind, value2, repoRoot) {
615174
615568
  remove(join127(repoRoot, OMNIUS_DIR, USAGE_HISTORY_FILE));
615175
615569
  }
615176
615570
  }
615177
- var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, SESSIONS_DIR, SESSIONS_INDEX, SKIP_DIRS3, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
615571
+ var OMNIUS_DIR, LEGACY_DIRS, SUBDIRS, gitignoreWatchers, gitignoreRetryTimers, CONTEXT_FILES, PENDING_TASK_FILE, HANDOFF_FILE, CONTEXT_SAVE_FILE, CONTEXT_LEDGER_FILE, MAX_CONTEXT_ENTRIES, MAX_SESSION_DIARY_ENTRIES, MAX_SESSION_DIARY_DETAILED_ENTRIES, MAX_CONTEXT_LEDGER_LINES, MAX_CONTEXT_LEDGER_BYTES, SAME_TASK_REPLACE_WINDOW_MS, LOCK_TIMEOUT_MS, LOCK_RETRY_MS, LOCK_RETRY_MAX, DEICTIC_CONTINUATION_TERMS, SESSIONS_DIR, SESSIONS_INDEX, SKIP_DIRS3, HOME_SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
615178
615572
  var init_omnius_directory = __esm({
615179
615573
  "packages/cli/src/tui/omnius-directory.ts"() {
615180
615574
  "use strict";
@@ -615207,6 +615601,34 @@ var init_omnius_directory = __esm({
615207
615601
  LOCK_TIMEOUT_MS = 5e3;
615208
615602
  LOCK_RETRY_MS = 50;
615209
615603
  LOCK_RETRY_MAX = 100;
615604
+ DEICTIC_CONTINUATION_TERMS = /* @__PURE__ */ new Set([
615605
+ "again",
615606
+ "and",
615607
+ "back",
615608
+ "carry",
615609
+ "complete",
615610
+ "continue",
615611
+ "finish",
615612
+ "from",
615613
+ "last",
615614
+ "latest",
615615
+ "left",
615616
+ "my",
615617
+ "off",
615618
+ "please",
615619
+ "previous",
615620
+ "prior",
615621
+ "proceed",
615622
+ "request",
615623
+ "resume",
615624
+ "task",
615625
+ "that",
615626
+ "the",
615627
+ "this",
615628
+ "to",
615629
+ "try",
615630
+ "work"
615631
+ ]);
615210
615632
  SESSIONS_DIR = "sessions";
615211
615633
  SESSIONS_INDEX = "sessions-index.json";
615212
615634
  SKIP_DIRS3 = /* @__PURE__ */ new Set([
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.387",
3
+ "version": "1.0.389",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.387",
9
+ "version": "1.0.389",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.387",
3
+ "version": "1.0.389",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",