@gamaze/hicortex 0.21.0 → 0.22.0
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/assets/dashboard.html +274 -25
- package/dist/calibration.d.ts +30 -0
- package/dist/calibration.js +31 -1
- package/dist/capture-health.d.ts +42 -5
- package/dist/capture-health.js +57 -7
- package/dist/capture-pause.d.ts +7 -4
- package/dist/capture-pause.js +7 -4
- package/dist/consolidate.d.ts +19 -0
- package/dist/consolidate.js +116 -51
- package/dist/dashboard.d.ts +41 -16
- package/dist/dashboard.js +110 -41
- package/dist/db.js +16 -0
- package/dist/dedup.js +4 -1
- package/dist/eval/decay-eval.d.ts +2 -3
- package/dist/eval/decay-eval.js +6 -44
- package/dist/eval/ranking-eval.js +1 -1
- package/dist/eval/ranking-fixtures.d.ts +5 -5
- package/dist/eval/ranking-fixtures.js +7 -7
- package/dist/eval/run-eval.js +1 -1
- package/dist/graph.d.ts +1 -2
- package/dist/graph.js +3 -4
- package/dist/mcp-server.js +1 -1
- package/dist/nofit.d.ts +2 -1
- package/dist/nofit.js +2 -1
- package/dist/recall-index.d.ts +3 -2
- package/dist/recall-index.js +3 -2
- package/dist/retrieval.d.ts +9 -3
- package/dist/retrieval.js +21 -32
- package/dist/status.js +18 -0
- package/dist/storage.d.ts +2 -1
- package/dist/storage.js +6 -1
- package/dist/types.d.ts +25 -4
- package/package.json +1 -1
- package/server.json +4 -4
package/dist/dashboard.js
CHANGED
|
@@ -113,38 +113,17 @@ function computeDashboardMetrics(db) {
|
|
|
113
113
|
},
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
-
/**
|
|
117
|
-
* One grouped pass over memory_links for per-memory link counts (batch —
|
|
118
|
-
* never per-row queries); shared by the field payload and stage_counts.
|
|
119
|
-
* Counts BOTH directions (a memory's connectivity is symmetric).
|
|
120
|
-
*/
|
|
121
|
-
function linkCountsByMemory(db) {
|
|
122
|
-
const rows = db
|
|
123
|
-
.prepare(`SELECT id, SUM(c) AS c FROM (
|
|
124
|
-
SELECT source_id AS id, COUNT(*) AS c FROM memory_links GROUP BY source_id
|
|
125
|
-
UNION ALL
|
|
126
|
-
SELECT target_id AS id, COUNT(*) AS c FROM memory_links GROUP BY target_id
|
|
127
|
-
) GROUP BY id`)
|
|
128
|
-
.all();
|
|
129
|
-
const linkCounts = new Map();
|
|
130
|
-
for (const r of rows)
|
|
131
|
-
linkCounts.set(r.id, r.c);
|
|
132
|
-
return linkCounts;
|
|
133
|
-
}
|
|
134
116
|
/**
|
|
135
117
|
* Derive one memory's {strength, stage} — the exact math of the field
|
|
136
|
-
* payload: effectiveStrength (importance = base
|
|
137
|
-
*
|
|
118
|
+
* payload: effectiveStrength (importance = base) rounded to the 1e-6 wire
|
|
119
|
+
* format, then the recency gate (days since
|
|
138
120
|
* last_accessed, falling back to created_at; unparseable → null, the gate is
|
|
139
121
|
* skipped rather than guessing "very old") and the calibrated strength bands.
|
|
140
122
|
*/
|
|
141
|
-
function deriveStageForRow(row,
|
|
123
|
+
function deriveStageForRow(row, now, nowDay) {
|
|
142
124
|
const base = row.base_strength ?? 0.5;
|
|
143
|
-
const accessCount = row.access_count ?? 0;
|
|
144
125
|
const effStr = (0, retrieval_js_1.effectiveStrength)(base, row.last_accessed, now, {
|
|
145
126
|
importance: base,
|
|
146
|
-
accessCount,
|
|
147
|
-
linkCount,
|
|
148
127
|
});
|
|
149
128
|
const strength = Math.round(effStr * 1e6) / 1e6;
|
|
150
129
|
const refDay = utcDayNumber(row.last_accessed) ?? utcDayNumber(row.created_at);
|
|
@@ -160,15 +139,14 @@ function deriveStageForRow(row, linkCount, now, nowDay) {
|
|
|
160
139
|
function computeStageCounts(db) {
|
|
161
140
|
const now = new Date();
|
|
162
141
|
const nowDay = Math.floor(now.getTime() / 86_400_000);
|
|
163
|
-
const linkCounts = linkCountsByMemory(db);
|
|
164
142
|
const rows = db
|
|
165
|
-
.prepare(`SELECT id, base_strength, last_accessed,
|
|
143
|
+
.prepare(`SELECT id, base_strength, last_accessed, created_at
|
|
166
144
|
FROM memories
|
|
167
145
|
WHERE COALESCE(status, '') != 'absorbed'`)
|
|
168
146
|
.all();
|
|
169
147
|
const counts = { forming: 0, belief: 0, truth: 0, fading: 0 };
|
|
170
148
|
for (const r of rows) {
|
|
171
|
-
counts[deriveStageForRow(r,
|
|
149
|
+
counts[deriveStageForRow(r, now, nowDay).stage]++;
|
|
172
150
|
}
|
|
173
151
|
return counts;
|
|
174
152
|
}
|
|
@@ -397,7 +375,7 @@ function backfillSnapshots(db) {
|
|
|
397
375
|
// LIVE composition (so day-one, before any snapshot is written, still shows
|
|
398
376
|
// the current corpus shape), and builds the digest for the selected day.
|
|
399
377
|
// ---------------------------------------------------------------------------
|
|
400
|
-
const VALID_RANGES = new Set(["7d", "30d", "90d", "all"]);
|
|
378
|
+
const VALID_RANGES = new Set(["7d", "30d", "90d", "180d", "all"]);
|
|
401
379
|
const DEFAULT_RANGE = "30d";
|
|
402
380
|
const DEFAULT_DIGEST_LIMIT = 10;
|
|
403
381
|
function rangeToCutoff(range) {
|
|
@@ -597,7 +575,7 @@ function handleDashboardData(db, query, config) {
|
|
|
597
575
|
by_source_agent: live.by_source_agent,
|
|
598
576
|
by_source_machine: live.by_source_machine,
|
|
599
577
|
},
|
|
600
|
-
capture_health: (0, capture_health_js_1.readCaptureHealth)(db),
|
|
578
|
+
capture_health: { ...(0, capture_health_js_1.readCaptureHealth)(db), ...(0, capture_health_js_1.readCaptureHealthWindow)(db) },
|
|
601
579
|
// #423 phase 3: pauses + presence in one block — one source of truth
|
|
602
580
|
// for the rail's dots, toggles and the capture card's PAUSED badges.
|
|
603
581
|
fleet: {
|
|
@@ -690,8 +668,6 @@ function utcDayNumber(iso) {
|
|
|
690
668
|
function handleDashboardField(db) {
|
|
691
669
|
const now = new Date();
|
|
692
670
|
const nowDay = Math.floor(now.getTime() / 86_400_000);
|
|
693
|
-
// Shared derivation inputs (one batched pass — never per-row queries).
|
|
694
|
-
const linkCounts = linkCountsByMemory(db);
|
|
695
671
|
const rows = db
|
|
696
672
|
.prepare(`SELECT id, content, base_strength, last_accessed, access_count,
|
|
697
673
|
created_at, domain, source_agent, source_machine
|
|
@@ -702,7 +678,7 @@ function handleDashboardField(db) {
|
|
|
702
678
|
const memories = rows.map((r) => {
|
|
703
679
|
// Shared per-row derivation — the SAME math the snapshot stage_counts
|
|
704
680
|
// uses (deriveStageForRow); the field only adds its wire fields.
|
|
705
|
-
const { strength, stage } = deriveStageForRow(r,
|
|
681
|
+
const { strength, stage } = deriveStageForRow(r, now, nowDay);
|
|
706
682
|
return {
|
|
707
683
|
id: r.id,
|
|
708
684
|
title: (0, recall_index_js_1.memoryTitle)(r.content, calibration_js_1.RECALL_TITLE_CHARS),
|
|
@@ -787,9 +763,12 @@ function dashboardFieldHandler(getDb) {
|
|
|
787
763
|
}
|
|
788
764
|
};
|
|
789
765
|
}
|
|
790
|
-
|
|
766
|
+
// #452 (owner directive 2026-09-16): the default window is 30 days for ALL
|
|
767
|
+
// views — longer ranges are opt-in via the page's selector. MAX stays 365:
|
|
768
|
+
// it is the generic API cap and an explicit ?days= is honored up to it.
|
|
769
|
+
const EVENTS_DEFAULT_DAYS = 30;
|
|
791
770
|
const EVENTS_MAX_DAYS = 365;
|
|
792
|
-
/** Parse the ?days= param: integer 1..365, default
|
|
771
|
+
/** Parse the ?days= param: integer 1..365, default 30. Returns null when the
|
|
793
772
|
* value is present but invalid (→ 400); undefined when absent (→ default). */
|
|
794
773
|
function parseEventsDays(raw) {
|
|
795
774
|
if (raw === undefined)
|
|
@@ -801,9 +780,9 @@ function parseEventsDays(raw) {
|
|
|
801
780
|
return n;
|
|
802
781
|
}
|
|
803
782
|
/**
|
|
804
|
-
* The pure data handler for GET /dashboard/events?days=N. Buckets the
|
|
805
|
-
*
|
|
806
|
-
*
|
|
783
|
+
* The pure data handler for GET /dashboard/events?days=N. Buckets the event
|
|
784
|
+
* sources into UTC nights (only nights with events, ascending) within the
|
|
785
|
+
* last `days` calendar days (today inclusive).
|
|
807
786
|
*/
|
|
808
787
|
function handleDashboardEvents(db, query) {
|
|
809
788
|
const parsed = parseEventsDays(query.days);
|
|
@@ -828,14 +807,31 @@ function handleDashboardEvents(db, query) {
|
|
|
828
807
|
const nightFor = (date) => {
|
|
829
808
|
let n = nights.get(date);
|
|
830
809
|
if (!n) {
|
|
831
|
-
n = {
|
|
810
|
+
n = {
|
|
811
|
+
date,
|
|
812
|
+
added: [],
|
|
813
|
+
enriched: [],
|
|
814
|
+
merged: [],
|
|
815
|
+
linked: [],
|
|
816
|
+
superseded: [],
|
|
817
|
+
retracted: [],
|
|
818
|
+
rewritten: [],
|
|
819
|
+
by_agent: {},
|
|
820
|
+
};
|
|
832
821
|
nights.set(date, n);
|
|
833
822
|
}
|
|
834
823
|
return n;
|
|
835
824
|
};
|
|
836
825
|
const bumpAgent = (n, agent, key) => {
|
|
837
826
|
const k = agent ?? "(unknown)";
|
|
838
|
-
const rec = n.by_agent[k] ?? {
|
|
827
|
+
const rec = n.by_agent[k] ?? {
|
|
828
|
+
learned: 0,
|
|
829
|
+
enriched: 0,
|
|
830
|
+
merged: 0,
|
|
831
|
+
superseded: 0,
|
|
832
|
+
retracted: 0,
|
|
833
|
+
rewritten: 0,
|
|
834
|
+
};
|
|
839
835
|
rec[key] += 1;
|
|
840
836
|
n.by_agent[k] = rec;
|
|
841
837
|
};
|
|
@@ -855,10 +851,12 @@ function handleDashboardEvents(db, query) {
|
|
|
855
851
|
// enriched — memory_history rows, DISTINCT memory per night (one rewrite of
|
|
856
852
|
// the same memory on a night is one enrichment). LEFT JOIN: the memory row
|
|
857
853
|
// can be gone (hard-deleted legacy losers); those count under "(unknown)".
|
|
854
|
+
// #452: reconsolidation rewrites are EXCLUDED — they render as `rewritten`
|
|
855
|
+
// below, so one rewrite is one caption, never "Enriched"+"Rewritten".
|
|
858
856
|
const enrichRows = db
|
|
859
857
|
.prepare(`SELECT h.memory_id, h.created_at, m.source_agent AS agent
|
|
860
858
|
FROM memory_history h LEFT JOIN memories m ON m.id = h.memory_id
|
|
861
|
-
WHERE h.created_at >=
|
|
859
|
+
WHERE h.created_at >= ? AND h.cause <> 'reconsolidation'`)
|
|
862
860
|
.all(cutoffIso);
|
|
863
861
|
const enrichedSeen = new Set(); // `${date}|${memory_id}` dedup
|
|
864
862
|
for (const r of enrichRows) {
|
|
@@ -898,8 +896,79 @@ function handleDashboardEvents(db, query) {
|
|
|
898
896
|
continue;
|
|
899
897
|
nightFor(date).linked.push({ a: r.source_id, b: r.target_id });
|
|
900
898
|
}
|
|
899
|
+
// superseded (#452) — superseded_by links born in the window; agent = the
|
|
900
|
+
// OLD memory's source (the event belongs to the demoted claim). The link
|
|
901
|
+
// ALSO stays in `linked` above — supersession is still a connection, the
|
|
902
|
+
// ring is unchanged.
|
|
903
|
+
const supersededRows = db
|
|
904
|
+
.prepare(`SELECT l.source_id, l.target_id, l.created_at, m.source_agent AS agent
|
|
905
|
+
FROM memory_links l LEFT JOIN memories m ON m.id = l.source_id
|
|
906
|
+
WHERE l.relationship = 'superseded_by' AND l.created_at >= ?`)
|
|
907
|
+
.all(cutoffIso);
|
|
908
|
+
for (const r of supersededRows) {
|
|
909
|
+
const date = inWindow(r.created_at);
|
|
910
|
+
if (date === null)
|
|
911
|
+
continue;
|
|
912
|
+
const n = nightFor(date);
|
|
913
|
+
n.superseded.push({ old: r.source_id, by: r.target_id });
|
|
914
|
+
bumpAgent(n, r.agent, "superseded");
|
|
915
|
+
}
|
|
916
|
+
// retracted (#452) — corrected_by links whose SOURCE's CURRENT status is
|
|
917
|
+
// 'retracted'. INNER JOIN so the status filter applies: rewrite-path
|
|
918
|
+
// corrected_by links leave status 'corrected' (or NULL on legacy rows) and
|
|
919
|
+
// are NOT retractions. Marks write no memory_history row (grounding: the
|
|
920
|
+
// only two INSERT sites are the reconsolidation rewrite + rollback), so
|
|
921
|
+
// the link + current status IS the retraction record. Dedup per
|
|
922
|
+
// (date, source_id) — the enriched pattern — in case a pair's link row was
|
|
923
|
+
// re-created on the same night.
|
|
924
|
+
const retractedRows = db
|
|
925
|
+
.prepare(`SELECT l.source_id, l.target_id, l.created_at, m.source_agent AS agent
|
|
926
|
+
FROM memory_links l JOIN memories m ON m.id = l.source_id
|
|
927
|
+
WHERE l.relationship = 'corrected_by' AND m.status = 'retracted' AND l.created_at >= ?`)
|
|
928
|
+
.all(cutoffIso);
|
|
929
|
+
const retractedSeen = new Set(); // `${date}|${source_id}` dedup
|
|
930
|
+
for (const r of retractedRows) {
|
|
931
|
+
const date = inWindow(r.created_at);
|
|
932
|
+
if (date === null)
|
|
933
|
+
continue;
|
|
934
|
+
const key = `${date}|${r.source_id}`;
|
|
935
|
+
if (retractedSeen.has(key))
|
|
936
|
+
continue;
|
|
937
|
+
retractedSeen.add(key);
|
|
938
|
+
const n = nightFor(date);
|
|
939
|
+
n.retracted.push({ old: r.source_id, by: r.target_id });
|
|
940
|
+
bumpAgent(n, r.agent, "retracted");
|
|
941
|
+
}
|
|
942
|
+
// rewritten (#452) — memory_history rows from the reconsolidation rewrite
|
|
943
|
+
// (cause='reconsolidation'; rollback rows keep their own cause and stay
|
|
944
|
+
// enriched-shaped). DISTINCT memory per night, the enriched pattern.
|
|
945
|
+
const rewrittenRows = db
|
|
946
|
+
.prepare(`SELECT h.memory_id, h.created_at, m.source_agent AS agent
|
|
947
|
+
FROM memory_history h LEFT JOIN memories m ON m.id = h.memory_id
|
|
948
|
+
WHERE h.cause = 'reconsolidation' AND h.created_at >= ?`)
|
|
949
|
+
.all(cutoffIso);
|
|
950
|
+
const rewrittenSeen = new Set(); // `${date}|${memory_id}` dedup
|
|
951
|
+
for (const r of rewrittenRows) {
|
|
952
|
+
const date = inWindow(r.created_at);
|
|
953
|
+
if (date === null)
|
|
954
|
+
continue;
|
|
955
|
+
const key = `${date}|${r.memory_id}`;
|
|
956
|
+
if (rewrittenSeen.has(key))
|
|
957
|
+
continue;
|
|
958
|
+
rewrittenSeen.add(key);
|
|
959
|
+
const n = nightFor(date);
|
|
960
|
+
n.rewritten.push(r.memory_id);
|
|
961
|
+
bumpAgent(n, r.agent, "rewritten");
|
|
962
|
+
}
|
|
901
963
|
const sorted = Array.from(nights.values())
|
|
902
|
-
.filter((n) => n.added.length +
|
|
964
|
+
.filter((n) => n.added.length +
|
|
965
|
+
n.enriched.length +
|
|
966
|
+
n.merged.length +
|
|
967
|
+
n.linked.length +
|
|
968
|
+
n.superseded.length +
|
|
969
|
+
n.retracted.length +
|
|
970
|
+
n.rewritten.length >
|
|
971
|
+
0)
|
|
903
972
|
.sort((x, y) => (x.date < y.date ? -1 : x.date > y.date ? 1 : 0));
|
|
904
973
|
return { status: 200, body: { nights: sorted } };
|
|
905
974
|
}
|
package/dist/db.js
CHANGED
|
@@ -652,6 +652,22 @@ const MIGRATIONS = [
|
|
|
652
652
|
WHERE importance_scored_at IS NULL AND base_strength != 0.5`);
|
|
653
653
|
},
|
|
654
654
|
},
|
|
655
|
+
{
|
|
656
|
+
version: 20,
|
|
657
|
+
name: "add_promotion_last_count",
|
|
658
|
+
up: (db) => {
|
|
659
|
+
// #448 — promotion baseline: the access_count the nightly promotion
|
|
660
|
+
// stage (stagePromotion) last consumed. Backfilled to access_count so
|
|
661
|
+
// the first post-upgrade nightly does not replay lifetime counts as
|
|
662
|
+
// promotions (v19 backfill precedent). Written by the stage, this
|
|
663
|
+
// backfill, and dedup merges (the baseline moves with the summed
|
|
664
|
+
// counter). Idempotent via hasColumn (the v8 pattern).
|
|
665
|
+
if (!hasColumn(db, "memories", "promotion_last_count")) {
|
|
666
|
+
db.exec("ALTER TABLE memories ADD COLUMN promotion_last_count INTEGER");
|
|
667
|
+
}
|
|
668
|
+
db.exec("UPDATE memories SET promotion_last_count = access_count WHERE promotion_last_count IS NULL");
|
|
669
|
+
},
|
|
670
|
+
},
|
|
655
671
|
];
|
|
656
672
|
/**
|
|
657
673
|
* Run all pending migrations against the database.
|
package/dist/dedup.js
CHANGED
|
@@ -347,7 +347,9 @@ function mergeCluster(db, canonical, losers, injectFailure) {
|
|
|
347
347
|
clearLoserTags.run(loser.id);
|
|
348
348
|
storage.updateMemory(db, loser.id, { domain: null });
|
|
349
349
|
}
|
|
350
|
-
// 5. Merge counters onto the canonical.
|
|
350
|
+
// 5. Merge counters onto the canonical. promotion_last_count moves WITH the
|
|
351
|
+
// summed access_count (#448): a held baseline would replay the losers'
|
|
352
|
+
// lifetime accesses as fresh promotions on the next nightly run.
|
|
351
353
|
const accessCount = canonical.access_count + losers.reduce((s, l) => s + l.access_count, 0);
|
|
352
354
|
const shownCount = (canonical.shown_count ?? 0) + losers.reduce((s, l) => s + (l.shown_count ?? 0), 0);
|
|
353
355
|
const lastAccessed = [canonical, ...losers]
|
|
@@ -361,6 +363,7 @@ function mergeCluster(db, canonical, losers, injectFailure) {
|
|
|
361
363
|
shown_count: shownCount,
|
|
362
364
|
...(lastAccessed ? { last_accessed: lastAccessed } : {}),
|
|
363
365
|
base_strength: baseStrength,
|
|
366
|
+
promotion_last_count: accessCount,
|
|
364
367
|
});
|
|
365
368
|
injectFailure?.(canonical.id);
|
|
366
369
|
// 6. Audit trail (dedup_log is the merge record — and the only surviving
|
|
@@ -50,8 +50,7 @@ export declare function runPruneDryRun(db: Database.Database, decayHalfLifeDays?
|
|
|
50
50
|
* with `importance` defaulting to `base_strength` (retrieval.ts). A memory
|
|
51
51
|
* can EVER cross the prune floor (0.01, stageDecayPrune) only if that
|
|
52
52
|
* asymptote is itself below it: base_strength² * 0.1 < 0.01, i.e.
|
|
53
|
-
* base_strength < sqrt(0.1).
|
|
54
|
-
* only slow the approach, they never raise the floor.
|
|
53
|
+
* base_strength < sqrt(0.1).
|
|
55
54
|
*/
|
|
56
55
|
export declare const EVER_PRUNABLE_BASE_STRENGTH_CEILING: number;
|
|
57
56
|
export declare function histogram(values: number[], edges?: number[]): Record<string, number>;
|
|
@@ -67,7 +66,7 @@ export interface StructuralStrengthStats {
|
|
|
67
66
|
}
|
|
68
67
|
/**
|
|
69
68
|
* Compute effective-strength distributions now, and simulated further out
|
|
70
|
-
* (
|
|
69
|
+
* (decay continuing with no further access or promotion — the
|
|
71
70
|
* "if nothing changes" projection), plus the asymptotic floor per memory.
|
|
72
71
|
* Requires `configureDecay` to already reflect the desired half-life (call
|
|
73
72
|
* `runPruneDryRun` first, or `configureDecay` directly, in the same process).
|
package/dist/eval/decay-eval.js
CHANGED
|
@@ -11,39 +11,6 @@
|
|
|
11
11
|
* floor, now vs simulated further out
|
|
12
12
|
* (d) adoption — #192 shown_count/access_count signal quality
|
|
13
13
|
*/
|
|
14
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
-
}
|
|
20
|
-
Object.defineProperty(o, k2, desc);
|
|
21
|
-
}) : (function(o, m, k, k2) {
|
|
22
|
-
if (k2 === undefined) k2 = k;
|
|
23
|
-
o[k2] = m[k];
|
|
24
|
-
}));
|
|
25
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
-
}) : function(o, v) {
|
|
28
|
-
o["default"] = v;
|
|
29
|
-
});
|
|
30
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
-
var ownKeys = function(o) {
|
|
32
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
-
var ar = [];
|
|
34
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
-
return ar;
|
|
36
|
-
};
|
|
37
|
-
return ownKeys(o);
|
|
38
|
-
};
|
|
39
|
-
return function (mod) {
|
|
40
|
-
if (mod && mod.__esModule) return mod;
|
|
41
|
-
var result = {};
|
|
42
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
-
__setModuleDefault(result, mod);
|
|
44
|
-
return result;
|
|
45
|
-
};
|
|
46
|
-
})();
|
|
47
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
15
|
exports.EVER_PRUNABLE_BASE_STRENGTH_CEILING = void 0;
|
|
49
16
|
exports.runDomainBacklogAudit = runDomainBacklogAudit;
|
|
@@ -53,7 +20,6 @@ exports.runStructuralStrengthStats = runStructuralStrengthStats;
|
|
|
53
20
|
exports.runAdoptionStats = runAdoptionStats;
|
|
54
21
|
const retrieval_js_1 = require("../retrieval.js");
|
|
55
22
|
const consolidate_js_1 = require("../consolidate.js");
|
|
56
|
-
const storage = __importStar(require("../storage.js"));
|
|
57
23
|
const state_js_1 = require("../state.js");
|
|
58
24
|
/**
|
|
59
25
|
* Partition NULL-domain memories by the `domainCursor` rowid watermark.
|
|
@@ -106,8 +72,7 @@ function runPruneDryRun(db, decayHalfLifeDays = retrieval_js_1.DEFAULT_DECAY_HAL
|
|
|
106
72
|
* with `importance` defaulting to `base_strength` (retrieval.ts). A memory
|
|
107
73
|
* can EVER cross the prune floor (0.01, stageDecayPrune) only if that
|
|
108
74
|
* asymptote is itself below it: base_strength² * 0.1 < 0.01, i.e.
|
|
109
|
-
* base_strength < sqrt(0.1).
|
|
110
|
-
* only slow the approach, they never raise the floor.
|
|
75
|
+
* base_strength < sqrt(0.1).
|
|
111
76
|
*/
|
|
112
77
|
exports.EVER_PRUNABLE_BASE_STRENGTH_CEILING = Math.sqrt(0.1);
|
|
113
78
|
const STRENGTH_BUCKET_EDGES = [0, 0.01, 0.05, 0.1, 0.2, 0.3, 0.5, 0.7, 1.0];
|
|
@@ -128,16 +93,15 @@ function histogram(values, edges = STRENGTH_BUCKET_EDGES) {
|
|
|
128
93
|
}
|
|
129
94
|
/**
|
|
130
95
|
* Compute effective-strength distributions now, and simulated further out
|
|
131
|
-
* (
|
|
96
|
+
* (decay continuing with no further access or promotion — the
|
|
132
97
|
* "if nothing changes" projection), plus the asymptotic floor per memory.
|
|
133
98
|
* Requires `configureDecay` to already reflect the desired half-life (call
|
|
134
99
|
* `runPruneDryRun` first, or `configureDecay` directly, in the same process).
|
|
135
100
|
*/
|
|
136
101
|
function runStructuralStrengthStats(db) {
|
|
137
102
|
const rows = db
|
|
138
|
-
.prepare("SELECT id, base_strength, last_accessed
|
|
103
|
+
.prepare("SELECT id, base_strength, last_accessed FROM memories")
|
|
139
104
|
.all();
|
|
140
|
-
const linkCounts = storage.getAllLinkCounts(db);
|
|
141
105
|
const now = new Date();
|
|
142
106
|
const nowVals = [];
|
|
143
107
|
const at180 = [];
|
|
@@ -148,11 +112,9 @@ function runStructuralStrengthStats(db) {
|
|
|
148
112
|
const base = r.base_strength ?? 0.5;
|
|
149
113
|
if (base < exports.EVER_PRUNABLE_BASE_STRENGTH_CEILING)
|
|
150
114
|
everPrunable++;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
at180.push((0, retrieval_js_1.effectiveStrength)(base, r.last_accessed, new Date(now.getTime() + 180 * 86_400_000), opts));
|
|
155
|
-
at365.push((0, retrieval_js_1.effectiveStrength)(base, r.last_accessed, new Date(now.getTime() + 365 * 86_400_000), opts));
|
|
115
|
+
nowVals.push((0, retrieval_js_1.effectiveStrength)(base, r.last_accessed, now));
|
|
116
|
+
at180.push((0, retrieval_js_1.effectiveStrength)(base, r.last_accessed, new Date(now.getTime() + 180 * 86_400_000)));
|
|
117
|
+
at365.push((0, retrieval_js_1.effectiveStrength)(base, r.last_accessed, new Date(now.getTime() + 365 * 86_400_000)));
|
|
156
118
|
neverVals.push(base * base * 0.1);
|
|
157
119
|
}
|
|
158
120
|
return {
|
|
@@ -86,7 +86,7 @@ const ranking_battery_js_1 = require("./ranking-battery.js");
|
|
|
86
86
|
const DEFAULT_PHOTO_PATH = (0, node_path_1.join)(process.cwd(), "data", "ranking-eval-photo.json");
|
|
87
87
|
/** Battery gate thresholds (sweep protocol, issue #425 commit-3 gates). */
|
|
88
88
|
const BATTERY_TOP1_STABILITY_MIN = 0.9;
|
|
89
|
-
/** Plant one fixture corpus into a writable DB with its
|
|
89
|
+
/** Plant one fixture corpus into a writable DB with its seeded strength metadata. */
|
|
90
90
|
async function plantRankingFixture(db, fixture) {
|
|
91
91
|
const idByKey = new Map();
|
|
92
92
|
const now = Date.now();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* issue (the owner's live console test, 2026-09-13): a user searches a
|
|
6
6
|
* DISTINCTIVE PROPER NOUN they know exists; the one memory carrying that
|
|
7
7
|
* token has the best similarity of all candidates AND the literal FTS hit,
|
|
8
|
-
* yet ranks behind
|
|
8
|
+
* yet ranks behind high-strength but less-relevant memories that win on
|
|
9
9
|
* effective strength + graph connections.
|
|
10
10
|
*
|
|
11
11
|
* (a) sirnas_exact_match — the AC1 fixture: a low-strength, unlinked,
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
* - The texts are synthetic and generic (this package publishes to npm —
|
|
26
26
|
* no real infrastructure, people, or fleet detail), but SHAPED like the
|
|
27
27
|
* field failure: the proper noun is invented (it must appear nowhere
|
|
28
|
-
* else — verified by an invariant test), the rivals
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* else — verified by an invariant test), the rivals carry the metadata
|
|
29
|
+
* the production pipeline itself writes (base strength, access history,
|
|
30
|
+
* link counts).
|
|
31
31
|
* - Similarities are MEASURED at run time with the real bge-small-en-v1.5
|
|
32
32
|
* embedder (ranking-eval.ts) — never asserted into existence. The
|
|
33
33
|
* declared expectation is about ORDER under the shipped ranking, and
|
|
@@ -46,7 +46,7 @@ export interface RankingRow {
|
|
|
46
46
|
memoryType: string;
|
|
47
47
|
/** Birth base_strength — the value stageImportance would have written. */
|
|
48
48
|
baseStrength: number;
|
|
49
|
-
/** Access history (
|
|
49
|
+
/** Access history (the decay clock + the #448 promotion baseline). */
|
|
50
50
|
accessCount: number;
|
|
51
51
|
/** Days before the run's `now` of the last access (0 = touched today). */
|
|
52
52
|
lastAccessedDaysAgo: number;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* issue (the owner's live console test, 2026-09-13): a user searches a
|
|
7
7
|
* DISTINCTIVE PROPER NOUN they know exists; the one memory carrying that
|
|
8
8
|
* token has the best similarity of all candidates AND the literal FTS hit,
|
|
9
|
-
* yet ranks behind
|
|
9
|
+
* yet ranks behind high-strength but less-relevant memories that win on
|
|
10
10
|
* effective strength + graph connections.
|
|
11
11
|
*
|
|
12
12
|
* (a) sirnas_exact_match — the AC1 fixture: a low-strength, unlinked,
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* - The texts are synthetic and generic (this package publishes to npm —
|
|
27
27
|
* no real infrastructure, people, or fleet detail), but SHAPED like the
|
|
28
28
|
* field failure: the proper noun is invented (it must appear nowhere
|
|
29
|
-
* else — verified by an invariant test), the rivals
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* else — verified by an invariant test), the rivals carry the metadata
|
|
30
|
+
* the production pipeline itself writes (base strength, access history,
|
|
31
|
+
* link counts).
|
|
32
32
|
* - Similarities are MEASURED at run time with the real bge-small-en-v1.5
|
|
33
33
|
* embedder (ranking-eval.ts) — never asserted into existence. The
|
|
34
34
|
* declared expectation is about ORDER under the shipped ranking, and
|
|
@@ -50,13 +50,13 @@ exports.RANKING_SOURCE_AGENT = "ranking-eval";
|
|
|
50
50
|
// The target mirrors the field case's SHAPE with invented names: a home
|
|
51
51
|
// harbour memory carrying one distinctive proper noun, born weak (base 0.35,
|
|
52
52
|
// the "routine-but-curated" band), never accessed, unlinked, a few months
|
|
53
|
-
// old. The rivals are what the decay model produces for early-
|
|
53
|
+
// old. The rivals are what the decay model produces for early high-strength
|
|
54
54
|
// technical rows: base 0.90-0.95, mutual links, access history, created
|
|
55
55
|
// BEFORE the target. They share the harbour/boating domain vocabulary — so
|
|
56
56
|
// a bare proper-noun query lands in their neighborhood — but none carries
|
|
57
57
|
// the proper noun.
|
|
58
58
|
const SIRNAS_ROWS = [
|
|
59
|
-
// --- rivals:
|
|
59
|
+
// --- rivals: high-strength, connected, older, domain-adjacent ---
|
|
60
60
|
{
|
|
61
61
|
key: "rival_fees",
|
|
62
62
|
content: "Harbour fees and guest dock rules: pay at the machine by the fuel quay, electricity on the guest dock is metered, " +
|
|
@@ -158,7 +158,7 @@ const SIRNAS_ROWS = [
|
|
|
158
158
|
exports.SIRNAS_FIXTURE = {
|
|
159
159
|
key: "sirnas_exact_match",
|
|
160
160
|
description: "AC1: a distinctive proper-noun query must rank the exact-match (both-channel) memory first, " +
|
|
161
|
-
"ahead of
|
|
161
|
+
"ahead of high-strength domain-adjacent rivals that win on strength + connections.",
|
|
162
162
|
rows: SIRNAS_ROWS,
|
|
163
163
|
query: "Vindarsvik",
|
|
164
164
|
distinctiveToken: "vindarsvik",
|
package/dist/eval/run-eval.js
CHANGED
|
@@ -93,7 +93,7 @@ function renderDecay(backlog, prune, structural, adoption) {
|
|
|
93
93
|
lines.push("### Structural strength — who can EVER prune\n");
|
|
94
94
|
lines.push(`A memory can only ever cross the prune floor (effective strength < 0.01) if its asymptote is below that floor: ` +
|
|
95
95
|
`\`base_strength < sqrt(0.1) ≈ ${round(structural.everPrunableCeiling, 4)}\` (floor = base_strength × importance × 0.1, ` +
|
|
96
|
-
`importance defaults to base_strength
|
|
96
|
+
`importance defaults to base_strength).\n`);
|
|
97
97
|
lines.push(`**Ever-prunable: ${structural.everPrunableCount} / ${structural.totalMemories} (${pct(structural.totalMemories > 0 ? structural.everPrunableCount / structural.totalMemories : 0)})** — everyone else has a strength floor that never drops low enough to prune, no matter how long it goes unaccessed.\n`);
|
|
98
98
|
for (const [label, hist] of [
|
|
99
99
|
["Effective strength now", structural.effectiveStrengthNowHistogram],
|
package/dist/graph.d.ts
CHANGED
|
@@ -103,8 +103,7 @@ export interface ExportGraphOptions {
|
|
|
103
103
|
* Nodes are ranked by effective (decayed) strength and capped at `limit`.
|
|
104
104
|
* Edges include only links where BOTH endpoints made the cut.
|
|
105
105
|
* Link counts come from one aggregate query over memory_links (no per-row
|
|
106
|
-
* queries) and feed
|
|
107
|
-
* per-node linkCount field.
|
|
106
|
+
* queries) and feed the per-node linkCount field.
|
|
108
107
|
*/
|
|
109
108
|
export declare function exportGraph(db: Database.Database, options?: ExportGraphOptions): VizGraph;
|
|
110
109
|
export declare function shortestPath(db: Database.Database, fromId: string, toId: string, maxDepth?: number): string[] | null;
|
package/dist/graph.js
CHANGED
|
@@ -232,8 +232,7 @@ function makeLabel(content) {
|
|
|
232
232
|
* Nodes are ranked by effective (decayed) strength and capped at `limit`.
|
|
233
233
|
* Edges include only links where BOTH endpoints made the cut.
|
|
234
234
|
* Link counts come from one aggregate query over memory_links (no per-row
|
|
235
|
-
* queries) and feed
|
|
236
|
-
* per-node linkCount field.
|
|
235
|
+
* queries) and feed the per-node linkCount field.
|
|
237
236
|
*/
|
|
238
237
|
function exportGraph(db, options = {}) {
|
|
239
238
|
const limit = Math.min(Math.max(Math.floor(options.limit ?? exports.EXPORT_DEFAULT_LIMIT), 1), exports.EXPORT_MAX_LIMIT);
|
|
@@ -249,7 +248,7 @@ function exportGraph(db, options = {}) {
|
|
|
249
248
|
const linkCounts = new Map(linkCountRows.map((r) => [r.id, r.cnt]));
|
|
250
249
|
// Candidate memories — domain/type filters pushed into SQL
|
|
251
250
|
let sql = `SELECT id, content, memory_type, domain, project, base_strength,
|
|
252
|
-
last_accessed,
|
|
251
|
+
last_accessed, created_at
|
|
253
252
|
FROM memories`;
|
|
254
253
|
const where = [];
|
|
255
254
|
const params = [];
|
|
@@ -275,7 +274,7 @@ function exportGraph(db, options = {}) {
|
|
|
275
274
|
// Score, filter on effective strength, rank, cap
|
|
276
275
|
const scored = rows.map((row) => {
|
|
277
276
|
const linkCount = linkCounts.get(row.id) ?? 0;
|
|
278
|
-
const strength = (0, retrieval_js_1.effectiveStrength)(row.base_strength ?? 0.5, row.last_accessed, now
|
|
277
|
+
const strength = (0, retrieval_js_1.effectiveStrength)(row.base_strength ?? 0.5, row.last_accessed, now);
|
|
279
278
|
return { row, strength, linkCount };
|
|
280
279
|
});
|
|
281
280
|
const filtered = options.minStrength !== undefined
|
package/dist/mcp-server.js
CHANGED
|
@@ -1548,7 +1548,7 @@ async function startServer(options = {}) {
|
|
|
1548
1548
|
//
|
|
1549
1549
|
// An enrich is EVIDENCE ABOUT IMPORTANCE: it bumps corroboration_count and
|
|
1550
1550
|
// base_strength (+the calibration delta, capped at 1.0) — the same anchor
|
|
1551
|
-
// the nightly's LLM scoring
|
|
1551
|
+
// the nightly's LLM scoring writes. It must NOT touch
|
|
1552
1552
|
// access_count (reserved for real recall use) or shown_count (index
|
|
1553
1553
|
// exposure) — faking either corrupts the uses-per-showing adoption metric.
|
|
1554
1554
|
// Never a stage write: stages are derived presentation (the E-reframe).
|
package/dist/nofit.d.ts
CHANGED
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
* memory from pruning. Halving does not touch last_accessed/access_count.
|
|
39
39
|
* - Re-classification: a later run whose LLM tags it, or whose evolved
|
|
40
40
|
* prototypes clear the floor, gives it a (weak) primary — halving stops.
|
|
41
|
-
* Its strength is
|
|
41
|
+
* Its strength is restored only by the nightly promotion stage (#448),
|
|
42
|
+
* which converts each new access_count delta into a base_strength bump.
|
|
42
43
|
*
|
|
43
44
|
* PRUNE INTERACTION (verified against stageDecayPrune + effectiveStrength):
|
|
44
45
|
* prune fires when effectiveStrength < 0.01 for a >90-day-old, never-accessed
|
package/dist/nofit.js
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
* memory from pruning. Halving does not touch last_accessed/access_count.
|
|
40
40
|
* - Re-classification: a later run whose LLM tags it, or whose evolved
|
|
41
41
|
* prototypes clear the floor, gives it a (weak) primary — halving stops.
|
|
42
|
-
* Its strength is
|
|
42
|
+
* Its strength is restored only by the nightly promotion stage (#448),
|
|
43
|
+
* which converts each new access_count delta into a base_strength bump.
|
|
43
44
|
*
|
|
44
45
|
* PRUNE INTERACTION (verified against stageDecayPrune + effectiveStrength):
|
|
45
46
|
* prune fires when effectiveStrength < 0.01 for a >90-day-old, never-accessed
|
package/dist/recall-index.d.ts
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* Strengthening semantics (the recall/decay alignment):
|
|
11
11
|
* - Appearing in the index = exposure: shown_count + last_accessed refresh
|
|
12
12
|
* (mild, temporary strengthen — the decay clock resets) via
|
|
13
|
-
* storage.touchMemoriesShown. NO access_count bump:
|
|
14
|
-
* shield, and the adoption metric stay driven by real
|
|
13
|
+
* storage.touchMemoriesShown. NO access_count bump: the promotion signal
|
|
14
|
+
* (#448), the prune shield, and the adoption metric stay driven by real
|
|
15
|
+
* use.
|
|
15
16
|
* - hicortex_get = use: full strengthen (access_count + 1).
|
|
16
17
|
*
|
|
17
18
|
* Anti-bloat gates: relevance floor (measured cosine, or a real BM25 match),
|
package/dist/recall-index.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* Strengthening semantics (the recall/decay alignment):
|
|
12
12
|
* - Appearing in the index = exposure: shown_count + last_accessed refresh
|
|
13
13
|
* (mild, temporary strengthen — the decay clock resets) via
|
|
14
|
-
* storage.touchMemoriesShown. NO access_count bump:
|
|
15
|
-
* shield, and the adoption metric stay driven by real
|
|
14
|
+
* storage.touchMemoriesShown. NO access_count bump: the promotion signal
|
|
15
|
+
* (#448), the prune shield, and the adoption metric stay driven by real
|
|
16
|
+
* use.
|
|
16
17
|
* - hicortex_get = use: full strengthen (access_count + 1).
|
|
17
18
|
*
|
|
18
19
|
* Anti-bloat gates: relevance floor (measured cosine, or a real BM25 match),
|
package/dist/retrieval.d.ts
CHANGED
|
@@ -208,7 +208,7 @@ export declare function l2ToCosine(distance: number): number;
|
|
|
208
208
|
*/
|
|
209
209
|
export declare function cosineBetweenVectors(a: Float32Array, b: Float32Array): number;
|
|
210
210
|
/**
|
|
211
|
-
* Compute decayed strength with adaptive decay (B+
|
|
211
|
+
* Compute decayed strength with adaptive decay (B+D model).
|
|
212
212
|
* Exported for use by consolidation decay/prune stage.
|
|
213
213
|
*
|
|
214
214
|
* #425 read-side law: the decay-relevant importance is CLAMPED at the
|
|
@@ -216,11 +216,17 @@ export declare function cosineBetweenVectors(a: Float32Array, b: Float32Array):
|
|
|
216
216
|
* exactly 1.0 the decay rate is exactly 1.0 and the row never decays, so
|
|
217
217
|
* legacy base-1.0 rows (and any write site that predates the cap) decay
|
|
218
218
|
* again. The clamp applies to explicit importance passes too.
|
|
219
|
+
*
|
|
220
|
+
* #448: the access/connectivity HARDENING terms are REMOVED — the fade rate
|
|
221
|
+
* no longer depends on access or link history (a briefly-used memory was
|
|
222
|
+
* near-immortal: 10 accesses cut the decay rate to 3% of normal). Real use
|
|
223
|
+
* now raises the STORED score via the nightly promotion stage
|
|
224
|
+
* (consolidate.ts stagePromotion) instead of slowing future decay, so a
|
|
225
|
+
* promoted-then-abandoned memory fades on the same 365-day clock as any
|
|
226
|
+
* other, from a higher anchor.
|
|
219
227
|
*/
|
|
220
228
|
export declare function effectiveStrength(baseStrength: number, lastAccessed: string | null, now: Date, options?: {
|
|
221
229
|
importance?: number;
|
|
222
|
-
accessCount?: number;
|
|
223
|
-
linkCount?: number;
|
|
224
230
|
}): number;
|
|
225
231
|
/**
|
|
226
232
|
* Return a composite relevance score in [0, 1] for a candidate memory.
|