@gamaze/hicortex 0.20.6 → 0.20.7
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/reconsolidation.js +31 -21
- package/package.json +1 -1
package/dist/reconsolidation.js
CHANGED
|
@@ -133,13 +133,6 @@ exports.DEFAULT_RECONSOLIDATION_MAX_MINUTES = 120;
|
|
|
133
133
|
* Invalid → default.
|
|
134
134
|
*/
|
|
135
135
|
exports.DEFAULT_RECONSOLIDATION_MAX_CALLS = 600;
|
|
136
|
-
/**
|
|
137
|
-
* Candidates between mid-scan cursor persists (#401). The cursor also
|
|
138
|
-
* persists at EVERY scan-loop exit path (deadline, call/budget cap,
|
|
139
|
-
* discovery failure), so a killed run loses at most K-1 candidates of scan
|
|
140
|
-
* progress instead of the whole night.
|
|
141
|
-
*/
|
|
142
|
-
const RECONSOLIDATION_CURSOR_PERSIST_EVERY = 50;
|
|
143
136
|
/** Neighbor pool size before older/similarity filtering narrows to top 5 (supersession mirror). */
|
|
144
137
|
const CORRECTION_NEIGHBOR_POOL = 15;
|
|
145
138
|
/** Older-neighbor pairs kept per candidate after filtering (supersession mirror). */
|
|
@@ -615,21 +608,36 @@ async function stageReconsolidation(db, llm, budget, embedFn, dryRun, stateDir,
|
|
|
615
608
|
};
|
|
616
609
|
// ---- #401: mid-scan cursor persistence. Called at EVERY scan-loop exit
|
|
617
610
|
// path (deadline, call/budget cap, mark-verify budget stop, discovery
|
|
618
|
-
// failure)
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
let scanBatch = 0;
|
|
611
|
+
// failure) AND after every fully-considered candidate, so a killed run
|
|
612
|
+
// loses at most the candidate in flight. The end-of-stage updateState
|
|
613
|
+
// below stays the authoritative final write (it also applies the
|
|
614
|
+
// pendingMinRowid hold — that variable is only ever set AFTER the scan
|
|
615
|
+
// loop, so it is null at every call site here). updateState is
|
|
616
|
+
// load→mutate→temp-rename atomic.
|
|
625
617
|
let callsUsed = 0;
|
|
626
618
|
let deadlineStopped = false;
|
|
627
619
|
let callCapStopped = false;
|
|
620
|
+
// #402 follow-up (reviewer note 1): the hard-kill orphan floor. Queued
|
|
621
|
+
// merges and open rewrite groups are applied only in the POST-scan
|
|
622
|
+
// phases — until then their verdicts exist only in memory, and a
|
|
623
|
+
// SIGKILL/OOM between two persists would strand them BEHIND the persisted
|
|
624
|
+
// cursor (the next run would skip them forever). This tracks the smallest
|
|
625
|
+
// candidate rowid contributing to queued-but-unapplied work;
|
|
626
|
+
// persistCursor clamps every checkpoint below it so a resumed run
|
|
627
|
+
// re-detects the pairs (dup-over-loss). Deliberately SEPARATE from the
|
|
628
|
+
// post-loop pendingMinRowid hold above — different lifetime, different
|
|
629
|
+
// writers.
|
|
630
|
+
let scanPendingMinRowid = null;
|
|
631
|
+
const notePendingRowid = (rowid) => {
|
|
632
|
+
scanPendingMinRowid =
|
|
633
|
+
scanPendingMinRowid === null ? rowid : Math.min(scanPendingMinRowid, rowid);
|
|
634
|
+
};
|
|
628
635
|
const persistCursor = () => {
|
|
629
636
|
if (dryRun)
|
|
630
637
|
return;
|
|
638
|
+
const checkpoint = scanPendingMinRowid !== null ? Math.min(cursor, scanPendingMinRowid - 1) : cursor;
|
|
631
639
|
(0, state_js_1.updateState)((s) => {
|
|
632
|
-
s.reconsolidationCursor =
|
|
640
|
+
s.reconsolidationCursor = checkpoint;
|
|
633
641
|
}, stateDir);
|
|
634
642
|
};
|
|
635
643
|
const groups = new Map();
|
|
@@ -648,6 +656,7 @@ async function stageReconsolidation(db, llm, budget, embedFn, dryRun, stateDir,
|
|
|
648
656
|
candidateRowid: trigger.__rowid,
|
|
649
657
|
explicit,
|
|
650
658
|
});
|
|
659
|
+
notePendingRowid(trigger.__rowid); // orphan floor — group unapplied until the rewrite phase
|
|
651
660
|
}
|
|
652
661
|
};
|
|
653
662
|
for (const candidate of rows) {
|
|
@@ -784,6 +793,7 @@ async function stageReconsolidation(db, llm, budget, embedFn, dryRun, stateDir,
|
|
|
784
793
|
}
|
|
785
794
|
else {
|
|
786
795
|
queuedMerges.push({ oldId: neighbor.id, newId: candidate.id, candidateRowid: candidate.__rowid });
|
|
796
|
+
notePendingRowid(candidate.__rowid); // orphan floor — merge unapplied until the merge phase
|
|
787
797
|
}
|
|
788
798
|
continue;
|
|
789
799
|
}
|
|
@@ -817,12 +827,12 @@ async function stageReconsolidation(db, llm, budget, embedFn, dryRun, stateDir,
|
|
|
817
827
|
// verdict "none" → nothing to do
|
|
818
828
|
}
|
|
819
829
|
cursor = candidate.__rowid;
|
|
820
|
-
// #
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
830
|
+
// #402 follow-up (reviewer note 1): persist after EVERY fully-considered
|
|
831
|
+
// candidate — the 50-candidate batch left a kill window that could
|
|
832
|
+
// strand several candidates of scan progress. updateState is an atomic
|
|
833
|
+
// temp-rename of a small file and the loop cadence is seconds per
|
|
834
|
+
// candidate; the cost is negligible.
|
|
835
|
+
persistCursor();
|
|
826
836
|
}
|
|
827
837
|
if (deadlineStopped) {
|
|
828
838
|
console.log(`[hicortex] Reconsolidation: wall-clock deadline reached (reconsolidationMaxMinutes) — ` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.7",
|
|
4
4
|
"description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, Pi, and opencode.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|