@gamaze/hicortex 0.23.1 → 0.24.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 +64 -13
- package/dist/calibration.d.ts +31 -0
- package/dist/calibration.js +38 -1
- package/dist/capture.d.ts +7 -0
- package/dist/capture.js +10 -1
- package/dist/dashboard.d.ts +32 -7
- package/dist/dashboard.js +50 -10
- package/dist/db.js +45 -0
- package/dist/dedup.js +2 -2
- package/dist/distill-queue.d.ts +203 -0
- package/dist/distill-queue.js +440 -0
- package/dist/health.d.ts +13 -1
- package/dist/health.js +6 -1
- package/dist/hosted-boot.d.ts +1 -1
- package/dist/index.js +17 -2
- package/dist/learnings-identity.js +20 -1
- package/dist/localhost-bypass.js +1 -1
- package/dist/mcp-server.js +92 -7
- package/dist/nightly.js +88 -1
- package/dist/nofit.d.ts +1 -1
- package/dist/nofit.js +1 -1
- package/dist/schema-prototypes.d.ts +1 -1
- package/dist/schema-prototypes.js +1 -1
- package/dist/status.d.ts +11 -0
- package/dist/status.js +25 -0
- package/dist/types.d.ts +20 -0
- package/hermes-plugin/hicortex/README.md +13 -6
- package/hermes-plugin/hicortex/__init__.py +7 -0
- package/hermes-plugin/hicortex/client.py +64 -2
- package/hermes-plugin/hicortex/plugin.yaml +1 -1
- package/hermes-plugin/hicortex/provider.py +24 -1
- package/opencode-plugin/hicortex/index.ts +24 -1
- package/package.json +2 -1
- package/pi-extension/hicortex/index.ts +24 -1
- package/server.json +2 -2
- package/dist/eval/decay-eval.d.ts +0 -111
- package/dist/eval/decay-eval.js +0 -214
- package/dist/eval/dups.d.ts +0 -100
- package/dist/eval/dups.js +0 -174
- package/dist/eval/eval-clock.d.ts +0 -32
- package/dist/eval/eval-clock.js +0 -47
- package/dist/eval/eval-db.d.ts +0 -25
- package/dist/eval/eval-db.js +0 -67
- package/dist/eval/graph-eval.d.ts +0 -89
- package/dist/eval/graph-eval.js +0 -246
- package/dist/eval/importance-eval.d.ts +0 -85
- package/dist/eval/importance-eval.js +0 -286
- package/dist/eval/planted-eval.d.ts +0 -30
- package/dist/eval/planted-eval.js +0 -122
- package/dist/eval/planted-fixtures.d.ts +0 -107
- package/dist/eval/planted-fixtures.js +0 -283
- package/dist/eval/planted-harness.d.ts +0 -183
- package/dist/eval/planted-harness.js +0 -651
- package/dist/eval/ranking-battery.d.ts +0 -125
- package/dist/eval/ranking-battery.js +0 -289
- package/dist/eval/ranking-eval.d.ts +0 -61
- package/dist/eval/ranking-eval.js +0 -554
- package/dist/eval/ranking-fixtures.d.ts +0 -117
- package/dist/eval/ranking-fixtures.js +0 -485
- package/dist/eval/recall-sweep.d.ts +0 -87
- package/dist/eval/recall-sweep.js +0 -1030
- package/dist/eval/reflection-census.d.ts +0 -19
- package/dist/eval/reflection-census.js +0 -25
- package/dist/eval/relevance-eval.d.ts +0 -178
- package/dist/eval/relevance-eval.js +0 -2240
- package/dist/eval/run-eval.d.ts +0 -20
- package/dist/eval/run-eval.js +0 -299
package/dist/eval/dups.d.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* D1 — duplicate-rate audit (#191 mechanical baseline).
|
|
3
|
-
*
|
|
4
|
-
* For each memory, finds its top-10 nearest neighbors by vector distance and
|
|
5
|
-
* keeps every pair whose cosine similarity clears the lowest of three report
|
|
6
|
-
* thresholds (0.90 / 0.92 / 0.95). Clusters are built per threshold with
|
|
7
|
-
* union-find; the headline number at each threshold is "excess" — how many
|
|
8
|
-
* rows would disappear if every cluster were merged down to one memory.
|
|
9
|
-
*
|
|
10
|
-
* Duplicate pairs are additionally attributed to either the #189 recovery
|
|
11
|
-
* re-ingest (a retried capture segment produced a second row for content
|
|
12
|
-
* already stored) or organic near-duplication (independent sessions that
|
|
13
|
-
* happened to cover the same ground), using two signals: a shared base
|
|
14
|
-
* `source_session` (the `#<segment>` suffix stripped), or ingestion runs that
|
|
15
|
-
* differ while `created_at` is near-identical (a re-ingest preserves the
|
|
16
|
-
* original session date but lands in a later ingestion run).
|
|
17
|
-
*
|
|
18
|
-
* The clustering primitives (union-find, KNN edge building, metadata-mismatch
|
|
19
|
-
* check) live in `../cluster.js` — extracted (#100/#191) so `hicortex dedup`
|
|
20
|
-
* reuses the exact same math instead of re-implementing it. Re-exported below
|
|
21
|
-
* for backward compatibility with existing importers of this module.
|
|
22
|
-
*/
|
|
23
|
-
import type Database from "better-sqlite3";
|
|
24
|
-
import { UnionFind, clusterEdges, clusterExcess, clusterMetadataMismatch, type Edge, type ClusterMetadataMismatch } from "../cluster.js";
|
|
25
|
-
export { UnionFind, clusterEdges, clusterExcess, clusterMetadataMismatch, type ClusterMetadataMismatch };
|
|
26
|
-
/** @deprecated import `Edge` from `../cluster.js` instead. */
|
|
27
|
-
export type DupEdge = Edge;
|
|
28
|
-
/** Cosine thresholds the report evaluates, low to high. */
|
|
29
|
-
export declare const DUP_THRESHOLDS: readonly [0.9, 0.92, 0.95];
|
|
30
|
-
export interface DupMemoryRow {
|
|
31
|
-
id: string;
|
|
32
|
-
content: string;
|
|
33
|
-
created_at: string;
|
|
34
|
-
ingested_at: string;
|
|
35
|
-
source_session: string | null;
|
|
36
|
-
project: string | null;
|
|
37
|
-
privacy: string;
|
|
38
|
-
source_agent: string;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Partition memories into ingestion "runs" by `ingested_at`: sorted
|
|
42
|
-
* ascending, a gap greater than `gapHours` starts a new run. Returns a map
|
|
43
|
-
* of memory id -> run id (0-based, monotonically increasing).
|
|
44
|
-
*/
|
|
45
|
-
export declare function sessionizeByIngestedAt(rows: Array<{
|
|
46
|
-
id: string;
|
|
47
|
-
ingested_at: string;
|
|
48
|
-
}>, gapHours?: number): Map<string, number>;
|
|
49
|
-
/** Strip the `#<segment>` suffix from a `source_session` value. Null-safe. */
|
|
50
|
-
export declare function baseSessionId(sourceSession: string | null): string | null;
|
|
51
|
-
/** Whether two ISO timestamps fall within `toleranceDays` of each other. */
|
|
52
|
-
export declare function nearIdenticalDate(a: string, b: string, toleranceDays?: number): boolean;
|
|
53
|
-
export type PairAttribution = "recovery_reingest" | "organic";
|
|
54
|
-
/**
|
|
55
|
-
* Classify a duplicate pair as a #189 recovery re-ingest or organic overlap.
|
|
56
|
-
* Recovery re-ingest when either: both sides share the same base
|
|
57
|
-
* `source_session`, or they landed in different ingestion runs but
|
|
58
|
-
* `created_at` is near-identical (the re-ingest preserves the original
|
|
59
|
-
* session date while landing in a later run).
|
|
60
|
-
*/
|
|
61
|
-
export declare function attributePair(a: DupMemoryRow, b: DupMemoryRow, runOf: Map<string, number>): PairAttribution;
|
|
62
|
-
export interface DupThresholdResult {
|
|
63
|
-
threshold: number;
|
|
64
|
-
clusterCount: number;
|
|
65
|
-
excess: number;
|
|
66
|
-
/** Cluster sizes, largest first — a quick histogram without dumping content. */
|
|
67
|
-
clusterSizes: number[];
|
|
68
|
-
}
|
|
69
|
-
export interface DupClusterDump {
|
|
70
|
-
size: number;
|
|
71
|
-
members: Array<{
|
|
72
|
-
id: string;
|
|
73
|
-
created_at: string;
|
|
74
|
-
preview: string;
|
|
75
|
-
}>;
|
|
76
|
-
attribution: {
|
|
77
|
-
recoveryReingest: number;
|
|
78
|
-
organic: number;
|
|
79
|
-
};
|
|
80
|
-
metadataMismatch: ClusterMetadataMismatch;
|
|
81
|
-
}
|
|
82
|
-
export interface DupReport {
|
|
83
|
-
totalMemories: number;
|
|
84
|
-
knnK: number;
|
|
85
|
-
thresholds: DupThresholdResult[];
|
|
86
|
-
/** Attribution over every pair at/above the lowest threshold (broadest view). */
|
|
87
|
-
pairAttribution: {
|
|
88
|
-
recoveryReingest: number;
|
|
89
|
-
organic: number;
|
|
90
|
-
totalPairs: number;
|
|
91
|
-
};
|
|
92
|
-
/** Top clusters at the lowest threshold, largest first, for manual review. */
|
|
93
|
-
topClusters: DupClusterDump[];
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Run the full D1 duplicate audit against an open (readonly) snapshot
|
|
97
|
-
* connection. Pure aside from the DB reads — safe against a readonly
|
|
98
|
-
* connection, no writes attempted.
|
|
99
|
-
*/
|
|
100
|
-
export declare function runDupAudit(db: Database.Database, topN?: number): DupReport;
|
package/dist/eval/dups.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* D1 — duplicate-rate audit (#191 mechanical baseline).
|
|
4
|
-
*
|
|
5
|
-
* For each memory, finds its top-10 nearest neighbors by vector distance and
|
|
6
|
-
* keeps every pair whose cosine similarity clears the lowest of three report
|
|
7
|
-
* thresholds (0.90 / 0.92 / 0.95). Clusters are built per threshold with
|
|
8
|
-
* union-find; the headline number at each threshold is "excess" — how many
|
|
9
|
-
* rows would disappear if every cluster were merged down to one memory.
|
|
10
|
-
*
|
|
11
|
-
* Duplicate pairs are additionally attributed to either the #189 recovery
|
|
12
|
-
* re-ingest (a retried capture segment produced a second row for content
|
|
13
|
-
* already stored) or organic near-duplication (independent sessions that
|
|
14
|
-
* happened to cover the same ground), using two signals: a shared base
|
|
15
|
-
* `source_session` (the `#<segment>` suffix stripped), or ingestion runs that
|
|
16
|
-
* differ while `created_at` is near-identical (a re-ingest preserves the
|
|
17
|
-
* original session date but lands in a later ingestion run).
|
|
18
|
-
*
|
|
19
|
-
* The clustering primitives (union-find, KNN edge building, metadata-mismatch
|
|
20
|
-
* check) live in `../cluster.js` — extracted (#100/#191) so `hicortex dedup`
|
|
21
|
-
* reuses the exact same math instead of re-implementing it. Re-exported below
|
|
22
|
-
* for backward compatibility with existing importers of this module.
|
|
23
|
-
*/
|
|
24
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.DUP_THRESHOLDS = exports.clusterMetadataMismatch = exports.clusterExcess = exports.clusterEdges = exports.UnionFind = void 0;
|
|
26
|
-
exports.sessionizeByIngestedAt = sessionizeByIngestedAt;
|
|
27
|
-
exports.baseSessionId = baseSessionId;
|
|
28
|
-
exports.nearIdenticalDate = nearIdenticalDate;
|
|
29
|
-
exports.attributePair = attributePair;
|
|
30
|
-
exports.runDupAudit = runDupAudit;
|
|
31
|
-
const cluster_js_1 = require("../cluster.js");
|
|
32
|
-
Object.defineProperty(exports, "UnionFind", { enumerable: true, get: function () { return cluster_js_1.UnionFind; } });
|
|
33
|
-
Object.defineProperty(exports, "clusterEdges", { enumerable: true, get: function () { return cluster_js_1.clusterEdges; } });
|
|
34
|
-
Object.defineProperty(exports, "clusterExcess", { enumerable: true, get: function () { return cluster_js_1.clusterExcess; } });
|
|
35
|
-
Object.defineProperty(exports, "clusterMetadataMismatch", { enumerable: true, get: function () { return cluster_js_1.clusterMetadataMismatch; } });
|
|
36
|
-
/** Cosine thresholds the report evaluates, low to high. */
|
|
37
|
-
exports.DUP_THRESHOLDS = [0.9, 0.92, 0.95];
|
|
38
|
-
/** Neighbors requested per memory (excluding the memory itself). */
|
|
39
|
-
const KNN_K = 10;
|
|
40
|
-
/** Ingestion-run gap: a pause longer than this starts a new "run". */
|
|
41
|
-
const DEFAULT_RUN_GAP_HOURS = 1;
|
|
42
|
-
/** created_at proximity treated as "near-identical" for cross-run pairs. */
|
|
43
|
-
const DEFAULT_SAME_DATE_TOLERANCE_DAYS = 1;
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Sessionization + attribution (pure — unit tested)
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
/**
|
|
48
|
-
* Partition memories into ingestion "runs" by `ingested_at`: sorted
|
|
49
|
-
* ascending, a gap greater than `gapHours` starts a new run. Returns a map
|
|
50
|
-
* of memory id -> run id (0-based, monotonically increasing).
|
|
51
|
-
*/
|
|
52
|
-
function sessionizeByIngestedAt(rows, gapHours = DEFAULT_RUN_GAP_HOURS) {
|
|
53
|
-
const sorted = [...rows].sort((a, b) => a.ingested_at.localeCompare(b.ingested_at));
|
|
54
|
-
const gapMs = gapHours * 3_600_000;
|
|
55
|
-
const runOf = new Map();
|
|
56
|
-
let runId = -1;
|
|
57
|
-
let prevTime = null;
|
|
58
|
-
for (const r of sorted) {
|
|
59
|
-
const t = Date.parse(r.ingested_at);
|
|
60
|
-
const valid = Number.isFinite(t);
|
|
61
|
-
if (prevTime === null || !valid || t - prevTime > gapMs) {
|
|
62
|
-
runId++;
|
|
63
|
-
}
|
|
64
|
-
runOf.set(r.id, runId);
|
|
65
|
-
if (valid)
|
|
66
|
-
prevTime = t;
|
|
67
|
-
}
|
|
68
|
-
return runOf;
|
|
69
|
-
}
|
|
70
|
-
/** Strip the `#<segment>` suffix from a `source_session` value. Null-safe. */
|
|
71
|
-
function baseSessionId(sourceSession) {
|
|
72
|
-
if (!sourceSession)
|
|
73
|
-
return null;
|
|
74
|
-
const idx = sourceSession.indexOf("#");
|
|
75
|
-
return idx === -1 ? sourceSession : sourceSession.slice(0, idx);
|
|
76
|
-
}
|
|
77
|
-
/** Whether two ISO timestamps fall within `toleranceDays` of each other. */
|
|
78
|
-
function nearIdenticalDate(a, b, toleranceDays = DEFAULT_SAME_DATE_TOLERANCE_DAYS) {
|
|
79
|
-
const ta = Date.parse(a);
|
|
80
|
-
const tb = Date.parse(b);
|
|
81
|
-
if (!Number.isFinite(ta) || !Number.isFinite(tb))
|
|
82
|
-
return false;
|
|
83
|
-
return Math.abs(ta - tb) / 86_400_000 <= toleranceDays;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Classify a duplicate pair as a #189 recovery re-ingest or organic overlap.
|
|
87
|
-
* Recovery re-ingest when either: both sides share the same base
|
|
88
|
-
* `source_session`, or they landed in different ingestion runs but
|
|
89
|
-
* `created_at` is near-identical (the re-ingest preserves the original
|
|
90
|
-
* session date while landing in a later run).
|
|
91
|
-
*/
|
|
92
|
-
function attributePair(a, b, runOf) {
|
|
93
|
-
const baseA = baseSessionId(a.source_session);
|
|
94
|
-
const baseB = baseSessionId(b.source_session);
|
|
95
|
-
if (baseA !== null && baseA === baseB)
|
|
96
|
-
return "recovery_reingest";
|
|
97
|
-
const runA = runOf.get(a.id);
|
|
98
|
-
const runB = runOf.get(b.id);
|
|
99
|
-
if (runA !== undefined &&
|
|
100
|
-
runB !== undefined &&
|
|
101
|
-
runA !== runB &&
|
|
102
|
-
nearIdenticalDate(a.created_at, b.created_at)) {
|
|
103
|
-
return "recovery_reingest";
|
|
104
|
-
}
|
|
105
|
-
return "organic";
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Run the full D1 duplicate audit against an open (readonly) snapshot
|
|
109
|
-
* connection. Pure aside from the DB reads — safe against a readonly
|
|
110
|
-
* connection, no writes attempted.
|
|
111
|
-
*/
|
|
112
|
-
function runDupAudit(db, topN = 15) {
|
|
113
|
-
const rows = db
|
|
114
|
-
.prepare(`SELECT id, content, created_at, ingested_at, source_session, project, privacy, source_agent
|
|
115
|
-
FROM memories`)
|
|
116
|
-
.all();
|
|
117
|
-
const byId = new Map(rows.map((r) => [r.id, r]));
|
|
118
|
-
const runOf = sessionizeByIngestedAt(rows.map((r) => ({ id: r.id, ingested_at: r.ingested_at })));
|
|
119
|
-
const lowestThreshold = Math.min(...exports.DUP_THRESHOLDS);
|
|
120
|
-
const edges = (0, cluster_js_1.buildKnnEdges)(db, { k: KNN_K, minCosine: lowestThreshold });
|
|
121
|
-
const thresholds = exports.DUP_THRESHOLDS.map((threshold) => {
|
|
122
|
-
const clusters = (0, cluster_js_1.clusterEdges)(edges, threshold);
|
|
123
|
-
return {
|
|
124
|
-
threshold,
|
|
125
|
-
clusterCount: clusters.length,
|
|
126
|
-
excess: (0, cluster_js_1.clusterExcess)(clusters),
|
|
127
|
-
clusterSizes: clusters.map((c) => c.length).sort((a, b) => b - a),
|
|
128
|
-
};
|
|
129
|
-
});
|
|
130
|
-
let recoveryReingest = 0;
|
|
131
|
-
let organic = 0;
|
|
132
|
-
for (const e of edges) {
|
|
133
|
-
const a = byId.get(e.a);
|
|
134
|
-
const b = byId.get(e.b);
|
|
135
|
-
if (!a || !b)
|
|
136
|
-
continue;
|
|
137
|
-
if (attributePair(a, b, runOf) === "recovery_reingest")
|
|
138
|
-
recoveryReingest++;
|
|
139
|
-
else
|
|
140
|
-
organic++;
|
|
141
|
-
}
|
|
142
|
-
const broadestClusters = (0, cluster_js_1.clusterEdges)(edges, lowestThreshold).sort((a, b) => b.length - a.length);
|
|
143
|
-
const topClusters = broadestClusters.slice(0, topN).map((memberIds) => {
|
|
144
|
-
const members = memberIds.map((id) => byId.get(id)).filter((m) => !!m);
|
|
145
|
-
let recovery = 0;
|
|
146
|
-
let organicCount = 0;
|
|
147
|
-
for (let i = 0; i < members.length; i++) {
|
|
148
|
-
for (let j = i + 1; j < members.length; j++) {
|
|
149
|
-
if (attributePair(members[i], members[j], runOf) === "recovery_reingest")
|
|
150
|
-
recovery++;
|
|
151
|
-
else
|
|
152
|
-
organicCount++;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const sortedMembers = [...members].sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
156
|
-
return {
|
|
157
|
-
size: members.length,
|
|
158
|
-
members: sortedMembers.map((m) => ({
|
|
159
|
-
id: m.id,
|
|
160
|
-
created_at: m.created_at,
|
|
161
|
-
preview: m.content.slice(0, 80),
|
|
162
|
-
})),
|
|
163
|
-
attribution: { recoveryReingest: recovery, organic: organicCount },
|
|
164
|
-
metadataMismatch: (0, cluster_js_1.clusterMetadataMismatch)(members),
|
|
165
|
-
};
|
|
166
|
-
});
|
|
167
|
-
return {
|
|
168
|
-
totalMemories: rows.length,
|
|
169
|
-
knnK: KNN_K,
|
|
170
|
-
thresholds,
|
|
171
|
-
pairAttribution: { recoveryReingest, organic, totalPairs: edges.length },
|
|
172
|
-
topClusters,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* #458 — the shared pinned-clock helper for eval harnesses.
|
|
3
|
-
*
|
|
4
|
-
* The recall evals score against wall-clock time (recency + decay terms in
|
|
5
|
-
* computeScore/effectiveStrength), so a pre-photo and a post-run hours apart
|
|
6
|
-
* drift even on identical code — PR D evidenced mode-OFF units moving with no
|
|
7
|
-
* changed code in the path. The fix is a harness-visible pin: every eval that
|
|
8
|
-
* calls retrieve() (or the decay/adoption stats) accepts a `--now <ISO>` flag
|
|
9
|
-
* and threads the SAME instant into the retrieve() `now` seam (retrieval.ts —
|
|
10
|
-
* the injectable-clock idiom of run-deadline.ts), making before/after runs
|
|
11
|
-
* wall-clock-independent. Default stays the live clock — pinning is opt-in,
|
|
12
|
-
* for comparability.
|
|
13
|
-
*
|
|
14
|
-
* Fail-explicit by design (the repo convention): an invalid pin THROWS with a
|
|
15
|
-
* clear message rather than silently falling back to the live clock — a run
|
|
16
|
-
* that believed it was pinned but wasn't would silently reintroduce the drift
|
|
17
|
-
* class this helper exists to remove.
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Parse a `--now` flag value into the pinned instant.
|
|
21
|
-
*
|
|
22
|
-
* @param raw the raw flag value. undefined or empty/whitespace → null (the
|
|
23
|
-
* live clock — the flag was not passed). Anything else must parse as a
|
|
24
|
-
* Date; garbage or an invalid instant (e.g. month 13) throws.
|
|
25
|
-
* @returns the pinned Date, or null for the live clock.
|
|
26
|
-
*/
|
|
27
|
-
export declare function parsePinnedNow(raw: string | undefined): Date | null;
|
|
28
|
-
/**
|
|
29
|
-
* Human-readable clock mode for report headers / photo JSON: "pinned <ISO>"
|
|
30
|
-
* or "live" — so artifacts are self-describing about which clock produced them.
|
|
31
|
-
*/
|
|
32
|
-
export declare function clockLabel(now: Date | null): string;
|
package/dist/eval/eval-clock.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* #458 — the shared pinned-clock helper for eval harnesses.
|
|
4
|
-
*
|
|
5
|
-
* The recall evals score against wall-clock time (recency + decay terms in
|
|
6
|
-
* computeScore/effectiveStrength), so a pre-photo and a post-run hours apart
|
|
7
|
-
* drift even on identical code — PR D evidenced mode-OFF units moving with no
|
|
8
|
-
* changed code in the path. The fix is a harness-visible pin: every eval that
|
|
9
|
-
* calls retrieve() (or the decay/adoption stats) accepts a `--now <ISO>` flag
|
|
10
|
-
* and threads the SAME instant into the retrieve() `now` seam (retrieval.ts —
|
|
11
|
-
* the injectable-clock idiom of run-deadline.ts), making before/after runs
|
|
12
|
-
* wall-clock-independent. Default stays the live clock — pinning is opt-in,
|
|
13
|
-
* for comparability.
|
|
14
|
-
*
|
|
15
|
-
* Fail-explicit by design (the repo convention): an invalid pin THROWS with a
|
|
16
|
-
* clear message rather than silently falling back to the live clock — a run
|
|
17
|
-
* that believed it was pinned but wasn't would silently reintroduce the drift
|
|
18
|
-
* class this helper exists to remove.
|
|
19
|
-
*/
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.parsePinnedNow = parsePinnedNow;
|
|
22
|
-
exports.clockLabel = clockLabel;
|
|
23
|
-
/**
|
|
24
|
-
* Parse a `--now` flag value into the pinned instant.
|
|
25
|
-
*
|
|
26
|
-
* @param raw the raw flag value. undefined or empty/whitespace → null (the
|
|
27
|
-
* live clock — the flag was not passed). Anything else must parse as a
|
|
28
|
-
* Date; garbage or an invalid instant (e.g. month 13) throws.
|
|
29
|
-
* @returns the pinned Date, or null for the live clock.
|
|
30
|
-
*/
|
|
31
|
-
function parsePinnedNow(raw) {
|
|
32
|
-
if (raw === undefined || raw.trim() === "")
|
|
33
|
-
return null;
|
|
34
|
-
const parsed = new Date(raw);
|
|
35
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
36
|
-
throw new Error(`eval clock: invalid --now value "${raw}" — pass a full ISO 8601 instant ` +
|
|
37
|
-
`(e.g. 2026-09-17T09:00:00.000Z) or omit the flag to run on the live clock`);
|
|
38
|
-
}
|
|
39
|
-
return parsed;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Human-readable clock mode for report headers / photo JSON: "pinned <ISO>"
|
|
43
|
-
* or "live" — so artifacts are self-describing about which clock produced them.
|
|
44
|
-
*/
|
|
45
|
-
function clockLabel(now) {
|
|
46
|
-
return now === null ? "live" : `pinned ${now.toISOString()}`;
|
|
47
|
-
}
|
package/dist/eval/eval-db.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only snapshot access for the #191 mechanical audit baseline.
|
|
3
|
-
*
|
|
4
|
-
* The eval NEVER touches a live database — it runs against a checkpointed
|
|
5
|
-
* copy (`data/audit-<date>/snapshot.db`, gitignored). This module opens that
|
|
6
|
-
* copy in better-sqlite3's `readonly` mode and loads the sqlite-vec
|
|
7
|
-
* extension the same way `db.ts#initDb` does, WITHOUT calling `initDb()`
|
|
8
|
-
* itself: `initDb` runs schema migrations, which write to the file. A
|
|
9
|
-
* snapshot is assumed to already be at the current schema version (verified
|
|
10
|
-
* by `assertReadonly`, which also proves no migration silently ran).
|
|
11
|
-
*/
|
|
12
|
-
import Database from "better-sqlite3";
|
|
13
|
-
/**
|
|
14
|
-
* Open a DB snapshot for read-only analysis.
|
|
15
|
-
*
|
|
16
|
-
* Throws if the path does not exist, or if a write attempt against the
|
|
17
|
-
* returned connection would (surprisingly) succeed — the second check is a
|
|
18
|
-
* belt-and-suspenders guard against a future better-sqlite3/OS combination
|
|
19
|
-
* where `readonly: true` is silently ignored (e.g. a non-standard
|
|
20
|
-
* filesystem), so a bug can never turn the audit into a mutation of
|
|
21
|
-
* production data.
|
|
22
|
-
*/
|
|
23
|
-
export declare function openSnapshot(dbPath: string): Database.Database;
|
|
24
|
-
/** Convert a sqlite-vec embedding BLOB (as read back from `memory_vectors`) to a Float32Array. */
|
|
25
|
-
export declare function blobToEmbedding(blob: Buffer): Float32Array;
|
package/dist/eval/eval-db.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Read-only snapshot access for the #191 mechanical audit baseline.
|
|
4
|
-
*
|
|
5
|
-
* The eval NEVER touches a live database — it runs against a checkpointed
|
|
6
|
-
* copy (`data/audit-<date>/snapshot.db`, gitignored). This module opens that
|
|
7
|
-
* copy in better-sqlite3's `readonly` mode and loads the sqlite-vec
|
|
8
|
-
* extension the same way `db.ts#initDb` does, WITHOUT calling `initDb()`
|
|
9
|
-
* itself: `initDb` runs schema migrations, which write to the file. A
|
|
10
|
-
* snapshot is assumed to already be at the current schema version (verified
|
|
11
|
-
* by `assertReadonly`, which also proves no migration silently ran).
|
|
12
|
-
*/
|
|
13
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.openSnapshot = openSnapshot;
|
|
18
|
-
exports.blobToEmbedding = blobToEmbedding;
|
|
19
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
20
|
-
const node_fs_1 = require("node:fs");
|
|
21
|
-
/**
|
|
22
|
-
* Open a DB snapshot for read-only analysis.
|
|
23
|
-
*
|
|
24
|
-
* Throws if the path does not exist, or if a write attempt against the
|
|
25
|
-
* returned connection would (surprisingly) succeed — the second check is a
|
|
26
|
-
* belt-and-suspenders guard against a future better-sqlite3/OS combination
|
|
27
|
-
* where `readonly: true` is silently ignored (e.g. a non-standard
|
|
28
|
-
* filesystem), so a bug can never turn the audit into a mutation of
|
|
29
|
-
* production data.
|
|
30
|
-
*/
|
|
31
|
-
function openSnapshot(dbPath) {
|
|
32
|
-
if (!(0, node_fs_1.existsSync)(dbPath)) {
|
|
33
|
-
throw new Error(`Snapshot DB not found at ${dbPath}`);
|
|
34
|
-
}
|
|
35
|
-
const db = new better_sqlite3_1.default(dbPath, { readonly: true });
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
37
|
-
const sqliteVec = require("sqlite-vec");
|
|
38
|
-
sqliteVec.load(db);
|
|
39
|
-
assertReadonly(db);
|
|
40
|
-
return db;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Verify the connection truly refuses writes. Uses a table guaranteed to
|
|
44
|
-
* exist in any migrated Hicortex DB (`schema_version`) and a no-op-shaped
|
|
45
|
-
* statement (touches a version number that cannot exist) so that even if the
|
|
46
|
-
* guard somehow failed open, the blast radius is a single junk row rather
|
|
47
|
-
* than corruption of real data.
|
|
48
|
-
*/
|
|
49
|
-
function assertReadonly(db) {
|
|
50
|
-
let wroteSuccessfully = false;
|
|
51
|
-
try {
|
|
52
|
-
db.prepare("INSERT INTO schema_version (version, name, applied_at) VALUES (-1, '__eval_readonly_probe__', '')").run();
|
|
53
|
-
wroteSuccessfully = true;
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Expected: SQLITE_READONLY. The connection is safe to use.
|
|
57
|
-
}
|
|
58
|
-
if (wroteSuccessfully) {
|
|
59
|
-
throw new Error("Snapshot DB accepted a write — refusing to run the eval against a " +
|
|
60
|
-
"connection that is not truly read-only. Check the better-sqlite3 " +
|
|
61
|
-
"readonly option and filesystem permissions.");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/** Convert a sqlite-vec embedding BLOB (as read back from `memory_vectors`) to a Float32Array. */
|
|
65
|
-
function blobToEmbedding(blob) {
|
|
66
|
-
return new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
|
|
67
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* D6 — link-graph health audit (#191 mechanical baseline).
|
|
3
|
-
*
|
|
4
|
-
* Of the ~6.5k links: how many are meaningful vs near-duplicate noise, does
|
|
5
|
-
* the stored `memory_links.strength` still match a fresh cosine recompute
|
|
6
|
-
* (drift), and how do all these stats look before vs after the
|
|
7
|
-
* `relinkCursor` watermark (the resumable `hicortex relink` migration to the
|
|
8
|
-
* corrected cosine formula, #145).
|
|
9
|
-
*/
|
|
10
|
-
import type Database from "better-sqlite3";
|
|
11
|
-
/**
|
|
12
|
-
* Deterministic sample of `rows` (#460): a seeded partial Fisher–Yates
|
|
13
|
-
* shuffles the LAST `size` slots from the back, and that shuffled tail is
|
|
14
|
-
* the sample. Rows must already be in a stable order (the caller sorts by
|
|
15
|
-
* PK) so the draw does not depend on SQLite's scan order.
|
|
16
|
-
*/
|
|
17
|
-
export declare function seededSample<T>(rows: T[], size: number, seed: number): T[];
|
|
18
|
-
export interface LinkRow {
|
|
19
|
-
source_id: string;
|
|
20
|
-
target_id: string;
|
|
21
|
-
relationship: string;
|
|
22
|
-
strength: number;
|
|
23
|
-
}
|
|
24
|
-
/** Cosine similarity between two L2-normalized embeddings (dot product). */
|
|
25
|
-
export declare function cosineBetween(a: Float32Array, b: Float32Array): number;
|
|
26
|
-
export declare function byRelationshipCounts(links: LinkRow[]): Record<string, number>;
|
|
27
|
-
/**
|
|
28
|
-
* Partition links by whether their SOURCE memory's rowid has been covered by
|
|
29
|
-
* the `hicortex relink` watermark. `relinkCursor` is the last fully
|
|
30
|
-
* committed rowid (relink.ts) — relink iterates memories by rowid and
|
|
31
|
-
* discovers/refreshes links FROM each one, so a link's source rowid <=
|
|
32
|
-
* cursor means a relink pass has already run for that source (current
|
|
33
|
-
* formula); null cursor (never run) puts everything in "notYetRelinked".
|
|
34
|
-
*/
|
|
35
|
-
export declare function partitionByRelinkCursor(links: LinkRow[], sourceRowid: Map<string, number>, relinkCursor: number | null): {
|
|
36
|
-
relinked: LinkRow[];
|
|
37
|
-
notYetRelinked: LinkRow[];
|
|
38
|
-
};
|
|
39
|
-
export interface HubEntry {
|
|
40
|
-
id: string;
|
|
41
|
-
degree: number;
|
|
42
|
-
project: string | null;
|
|
43
|
-
domain: string | null;
|
|
44
|
-
preview: string;
|
|
45
|
-
}
|
|
46
|
-
export interface DegreeReport {
|
|
47
|
-
memoriesWithLinks: number;
|
|
48
|
-
totalMemories: number;
|
|
49
|
-
degreeHistogram: Record<string, number>;
|
|
50
|
-
topHubs: HubEntry[];
|
|
51
|
-
}
|
|
52
|
-
export declare function runDegreeAudit(db: Database.Database, topN?: number): DegreeReport;
|
|
53
|
-
export interface DriftReport {
|
|
54
|
-
sampleSize: number;
|
|
55
|
-
driftHistogram: Record<string, number>;
|
|
56
|
-
meanAbsDrift: number;
|
|
57
|
-
maxAbsDrift: number;
|
|
58
|
-
skippedMissingEmbedding: number;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Recompute cosine for a link sample and compare to stored `strength` (post
|
|
62
|
-
* migration-5 rescale, should track closely). The sample is deterministic
|
|
63
|
-
* (#460): all link rows in stable PK order, drawn by a seeded Fisher–Yates —
|
|
64
|
-
* two runs on the same build + snapshot produce an identical report, so
|
|
65
|
-
* before/after comparisons can require full-output identity.
|
|
66
|
-
*/
|
|
67
|
-
export declare function runDriftSample(db: Database.Database, embeddings: Map<string, Float32Array>, sampleSize?: number, seed?: number): DriftReport;
|
|
68
|
-
export interface PartitionStats {
|
|
69
|
-
linkCount: number;
|
|
70
|
-
byRelationship: Record<string, number>;
|
|
71
|
-
dupNoiseLinks: number;
|
|
72
|
-
dupNoiseShare: number;
|
|
73
|
-
}
|
|
74
|
-
export interface GraphReport {
|
|
75
|
-
totalLinks: number;
|
|
76
|
-
byRelationship: Record<string, number>;
|
|
77
|
-
degree: DegreeReport;
|
|
78
|
-
drift: DriftReport;
|
|
79
|
-
dupNoise: {
|
|
80
|
-
threshold: number;
|
|
81
|
-
count: number;
|
|
82
|
-
share: number;
|
|
83
|
-
measured: number;
|
|
84
|
-
};
|
|
85
|
-
relinkCursor: number | null;
|
|
86
|
-
relinkedPartition: PartitionStats;
|
|
87
|
-
notYetRelinkedPartition: PartitionStats;
|
|
88
|
-
}
|
|
89
|
-
export declare function runGraphAudit(db: Database.Database, stateDir?: string): GraphReport;
|