@gamaze/hicortex 0.20.3 → 0.20.5
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/README.md +20 -1
- package/assets/dashboard.html +318 -8
- package/assets/identity.html +349 -8
- package/assets/viz.html +352 -8
- package/dist/backup.d.ts +12 -8
- package/dist/backup.js +13 -9
- package/dist/claude-desktop.d.ts +138 -0
- package/dist/claude-desktop.js +251 -0
- package/dist/cli.d.ts +7 -2
- package/dist/cli.js +84 -3
- package/dist/consolidate.d.ts +8 -1
- package/dist/consolidate.js +82 -2
- package/dist/dashboard.d.ts +17 -0
- package/dist/dashboard.js +32 -0
- package/dist/db.js +36 -0
- package/dist/dedup.d.ts +157 -25
- package/dist/dedup.js +376 -83
- package/dist/domain-classify.js +4 -2
- package/dist/index.js +7 -7
- package/dist/init.d.ts +4 -1
- package/dist/init.js +103 -1
- package/dist/llm.d.ts +19 -13
- package/dist/llm.js +25 -14
- package/dist/mcp-server.d.ts +6 -0
- package/dist/mcp-server.js +94 -13
- package/dist/mcp-stdio.d.ts +138 -0
- package/dist/mcp-stdio.js +313 -0
- package/dist/memory-instructions.d.ts +18 -0
- package/dist/memory-instructions.js +39 -2
- package/dist/nightly.js +14 -1
- package/dist/reconsolidation.d.ts +323 -0
- package/dist/reconsolidation.js +1226 -0
- package/dist/retrieval.d.ts +14 -0
- package/dist/retrieval.js +41 -3
- package/dist/state.d.ts +23 -1
- package/dist/storage.d.ts +25 -0
- package/dist/storage.js +49 -7
- package/dist/type-classify.js +4 -2
- package/dist/types.d.ts +188 -0
- package/hermes-plugin/hicortex/provider.py +29 -17
- package/opencode-plugin/hicortex/index.ts +7 -7
- package/package.json +4 -2
- package/pi-extension/hicortex/index.ts +7 -7
- package/server.json +44 -0
package/dist/dedup.d.ts
CHANGED
|
@@ -1,52 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `hicortex dedup`
|
|
2
|
+
* `hicortex dedup` + the nightly deterministic merge zone (issues #100, #392).
|
|
3
3
|
*
|
|
4
4
|
* Corpus-quality companion to `hicortex relink`/`classify-domains`: instead of
|
|
5
|
-
* discovering NEW structure, this
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* executes the merge.
|
|
5
|
+
* discovering NEW structure, this collapses memories that are near-identical
|
|
6
|
+
* (top-10 KNN cosine >= threshold, default 0.92, union-find clustered — same
|
|
7
|
+
* math as the #191 D1 duplicate-rate audit; see cluster.ts). Two surfaces,
|
|
8
|
+
* ONE core:
|
|
10
9
|
*
|
|
11
|
-
*
|
|
10
|
+
* - `runDedup` — the manual CLI. Default is a DRY RUN: report only, zero
|
|
11
|
+
* writes. `--apply` executes the merge. Threshold resolution:
|
|
12
|
+
* `--threshold` > config `dedupAutoMergeThreshold` > legacy config
|
|
13
|
+
* `dedupMergeThreshold` > 0.92.
|
|
14
|
+
* - `runDeterministicMergeZone` (#392) — the nightly's LLM-free merge zone:
|
|
15
|
+
* pairs at/above the ceiling merge deterministically, ZERO LLM calls, under
|
|
16
|
+
* its own pacing cap (`dedupNightlyMaxMerges`). Called from the
|
|
17
|
+
* reconsolidation stage (and from the quiet-night skip path in
|
|
18
|
+
* consolidate.ts) so one stage report covers all resolution work.
|
|
19
|
+
*
|
|
20
|
+
* Per cluster (shared `planDedup`/`mergeCluster` core — no forks):
|
|
12
21
|
* - Canonical = highest access_count (tie: oldest created_at, then
|
|
13
22
|
* lexicographically smallest id — fully deterministic for audit).
|
|
14
23
|
* - Losers' links are re-pointed onto the canonical (a link that would
|
|
15
24
|
* become a self-link, or one whose (canonical, target) ordered pair
|
|
16
25
|
* ALREADY holds an edge, is skipped rather than overwritten — see
|
|
17
|
-
* planLinkRepoints for why `relationship` cannot be part of that guard)
|
|
26
|
+
* planLinkRepoints for why `relationship` cannot be part of that guard);
|
|
27
|
+
* the losers' own link rows are then deleted (previously cascade-deleted
|
|
28
|
+
* with the row).
|
|
18
29
|
* - canonical.access_count/shown_count = summed across the cluster;
|
|
19
30
|
* last_accessed = max; base_strength = max.
|
|
20
31
|
* - Tags are UNIONED onto the canonical (weights NULL — the next nightly's
|
|
21
32
|
* reconsolidation pass recomputes weights and the derived primary from
|
|
22
|
-
* the merged tag set)
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
33
|
+
* the merged tag set); the losers' tag rows are cleared and their domain
|
|
34
|
+
* set NULL — a loser must not count in moduleIndex/tag recomputes.
|
|
35
|
+
* - A `dedup_log` row is written per loser — audit trail AND the safety net
|
|
36
|
+
* /distill consults (mcp-server.ts) so an absorbed loser's
|
|
37
|
+
* `source_session` marker still blocks a re-ingest.
|
|
38
|
+
* - Losers are ABSORBED (storage.absorbMemory), not deleted (#392): status
|
|
39
|
+
* 'absorbed', vector + FTS rows dropped, plain row retained — invisible
|
|
40
|
+
* to recall, fetchable by id as evidence. Same vocabulary as the
|
|
41
|
+
* reconsolidation rewrite path. Merges are NOT history-rollback-able —
|
|
42
|
+
* dedup_log (loser_id → canonical_id) + the retained loser row is the
|
|
43
|
+
* record.
|
|
28
44
|
*
|
|
29
|
-
* A cluster whose members disagree on project
|
|
30
|
-
*
|
|
45
|
+
* A cluster whose members disagree on project or source_agent is SKIPPED
|
|
46
|
+
* entirely and listed for manual review — no --force in this release.
|
|
31
47
|
*
|
|
32
|
-
* Safety rails
|
|
48
|
+
* Safety rails when applying (CLI and zone alike):
|
|
33
49
|
* - A full DB backup (SQLite backup API) is taken FIRST, to
|
|
34
|
-
*
|
|
35
|
-
*
|
|
50
|
+
* <state>/backups/pre-dedup-<ISO>.db, pruned to `backupRetention` newest
|
|
51
|
+
* (pattern-scoped: full `hicortex-*.tar.gz` artifacts keep their own
|
|
52
|
+
* count). The CLI aborts (no merges attempted) if the backup fails; the
|
|
53
|
+
* nightly zone is fail-soft (backup_failed flag, zero merges).
|
|
36
54
|
* - The existing single-flight capture lock (capture.ts) is held for the
|
|
37
55
|
* duration of the merge so a concurrent nightly/capture run can't race
|
|
38
|
-
* the dedup_log bookkeeping the merge relies on.
|
|
56
|
+
* the dedup_log bookkeeping the merge relies on. The CLI fails fast on a
|
|
57
|
+
* busy lock; the zone reports lock_busy and merges nothing.
|
|
39
58
|
*
|
|
40
59
|
* Server-mode only (needs the local DB), like relink/classify-domains.
|
|
41
60
|
*/
|
|
42
61
|
import type Database from "better-sqlite3";
|
|
43
62
|
import { type ClusterMetadataMismatch } from "./cluster.js";
|
|
44
63
|
import { acquireCaptureLock } from "./capture.js";
|
|
64
|
+
import type { DeterministicMergeZoneReport } from "./types.js";
|
|
45
65
|
/**
|
|
46
66
|
* Default merge threshold. Measured on the #191 mechanical audit corpus:
|
|
47
67
|
* 89 clusters / 110 excess rows at 0.92 (data/audit-20260729/eval-report.md).
|
|
68
|
+
* #392: also the default `dedupAutoMergeThreshold` — the deterministic/LLM
|
|
69
|
+
* boundary of the unified resolution pass.
|
|
48
70
|
*/
|
|
49
71
|
export declare const DEFAULT_DEDUP_MERGE_THRESHOLD = 0.92;
|
|
72
|
+
/**
|
|
73
|
+
* Default pacing cap on merge OPERATIONS per nightly run (#392): the zone's
|
|
74
|
+
* clusters and the stage's judged pair merges count against ONE cap. Bounds a
|
|
75
|
+
* misbehaving-distiller burst; a large backlog drains over a few nights.
|
|
76
|
+
* `0` disables the merge machinery entirely.
|
|
77
|
+
*/
|
|
78
|
+
export declare const DEFAULT_DEDUP_NIGHTLY_MAX_MERGES = 250;
|
|
79
|
+
/**
|
|
80
|
+
* Row shape read from `memories` for merge decisions — a superset of the
|
|
81
|
+
* fields the Memory type declares (shown_count isn't on that interface yet).
|
|
82
|
+
* `status` rides along so judged-pair merges can defensively drop rows that
|
|
83
|
+
* were absorbed between verdict and apply.
|
|
84
|
+
*/
|
|
85
|
+
interface DedupMemberRow {
|
|
86
|
+
id: string;
|
|
87
|
+
content: string;
|
|
88
|
+
access_count: number;
|
|
89
|
+
shown_count: number | null;
|
|
90
|
+
last_accessed: string | null;
|
|
91
|
+
base_strength: number;
|
|
92
|
+
created_at: string;
|
|
93
|
+
project: string | null;
|
|
94
|
+
privacy: string | null;
|
|
95
|
+
source_agent: string;
|
|
96
|
+
source_session: string | null;
|
|
97
|
+
status: string | null;
|
|
98
|
+
}
|
|
50
99
|
export interface DedupClusterPlan {
|
|
51
100
|
size: number;
|
|
52
101
|
canonicalId: string;
|
|
@@ -94,8 +143,8 @@ export interface DedupReport {
|
|
|
94
143
|
linksSkippedExisting: number;
|
|
95
144
|
/** --apply only: clusters actually merged. */
|
|
96
145
|
merged?: number;
|
|
97
|
-
/** --apply only: loser rows
|
|
98
|
-
|
|
146
|
+
/** --apply only: loser rows absorbed (hidden from recall, kept as evidence). */
|
|
147
|
+
losersAbsorbed?: number;
|
|
99
148
|
/** --apply only: clusters that errored mid-merge (rolled back; left for a re-run). */
|
|
100
149
|
failedClusters?: number;
|
|
101
150
|
/** --apply only: path to the pre-merge backup. */
|
|
@@ -104,7 +153,7 @@ export interface DedupReport {
|
|
|
104
153
|
export interface DedupOptions {
|
|
105
154
|
/** Execute the merge. Default false = dry run (report only, zero writes). */
|
|
106
155
|
apply?: boolean;
|
|
107
|
-
/** Override
|
|
156
|
+
/** Override the configured threshold for one run. */
|
|
108
157
|
threshold?: number;
|
|
109
158
|
/** DB path override (tests / manual snapshot verification). Defaults to resolveDbPath(). */
|
|
110
159
|
dbPath?: string;
|
|
@@ -116,7 +165,7 @@ export interface DedupOptions {
|
|
|
116
165
|
acquireLock?: typeof acquireCaptureLock;
|
|
117
166
|
/**
|
|
118
167
|
* Test-only failure injection: called once per cluster merge, after the
|
|
119
|
-
* link/tag/counter writes but before the audit-log +
|
|
168
|
+
* link/tag/counter writes but before the audit-log + absorb step. Throwing
|
|
120
169
|
* here proves a mid-merge error rolls the WHOLE cluster's writes back
|
|
121
170
|
* (better-sqlite3 transaction semantics) rather than leaving a half-merged
|
|
122
171
|
* cluster. Never set in production.
|
|
@@ -135,10 +184,93 @@ export interface LinkRepointPlan {
|
|
|
135
184
|
skippedSelfLink: number;
|
|
136
185
|
skippedExisting: number;
|
|
137
186
|
}
|
|
187
|
+
/** One cluster's execution plan from `planDedup` — canonical, losers, members. */
|
|
188
|
+
export interface DedupMergePlan {
|
|
189
|
+
canonical: DedupMemberRow;
|
|
190
|
+
losers: DedupMemberRow[];
|
|
191
|
+
/** All member rows, oldest first (CLI preview lines derive from this). */
|
|
192
|
+
membersOldestFirst: DedupMemberRow[];
|
|
193
|
+
}
|
|
194
|
+
export interface PlanDedupResult {
|
|
195
|
+
/** Every cluster found at the threshold (mergeable + mismatch-skipped). */
|
|
196
|
+
clusterCount: number;
|
|
197
|
+
/** Clusters that passed the metadata rails, in discovery order. */
|
|
198
|
+
mergePlans: DedupMergePlan[];
|
|
199
|
+
mismatchSkipped: DedupMismatchCluster[];
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Discovery + merge planning at a cosine threshold (read-only — no writes).
|
|
203
|
+
* The ONE clustering core shared by the manual CLI (`runDedup`) and the
|
|
204
|
+
* nightly deterministic merge zone (`runDeterministicMergeZone`): KNN edges
|
|
205
|
+
* (k=10) → union-find clusters → member load → metadata-rail classification →
|
|
206
|
+
* canonical pick. Never forked.
|
|
207
|
+
*/
|
|
208
|
+
export declare function planDedup(db: Database.Database, threshold: number): PlanDedupResult;
|
|
209
|
+
export type MergeMemoryIdsResult = {
|
|
210
|
+
ok: true;
|
|
211
|
+
canonicalId: string;
|
|
212
|
+
loserIds: string[];
|
|
213
|
+
linksRepointed: number;
|
|
214
|
+
} | {
|
|
215
|
+
ok: false;
|
|
216
|
+
reason: "metadata_mismatch" | "no_members";
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Merge an explicit set of memories (the judged-pair phase of #392: the
|
|
220
|
+
* reconsolidation stage queues verdict-confirmed pairs and applies them
|
|
221
|
+
* through THIS function so the merge math stays single-definition). Loads the
|
|
222
|
+
* LIVE rows at apply time — members that vanished or were absorbed between
|
|
223
|
+
* verdict and apply are dropped defensively; a metadata disagreement refuses
|
|
224
|
+
* the merge (both memories stay live). One transaction for the whole set.
|
|
225
|
+
*/
|
|
226
|
+
export declare function mergeMemoryIds(db: Database.Database, ids: string[]): MergeMemoryIdsResult;
|
|
227
|
+
/**
|
|
228
|
+
* Take a pre-merge DB backup to <stateDir>/backups/pre-dedup-<ISO>.db and
|
|
229
|
+
* prune older pre-dedup backups to `backupRetention` (config, default 7;
|
|
230
|
+
* pattern-scoped so full `hicortex-*.tar.gz` artifacts keep their own,
|
|
231
|
+
* independent retention count). THROWS on failure — the callers own the
|
|
232
|
+
* policy: the CLI aborts, the nightly zone is fail-soft. Returns the path.
|
|
233
|
+
*/
|
|
234
|
+
export declare function takePreDedupBackup(db: Database.Database, stateDir: string, config?: Record<string, unknown> | null): Promise<string>;
|
|
235
|
+
export interface DeterministicMergeZoneOptions {
|
|
236
|
+
/** State dir (lock + backup + band-stats persistence). Defaults to ~/.hicortex. */
|
|
237
|
+
stateDir?: string;
|
|
238
|
+
/** Cosine ceiling; validated (0,1] → DEFAULT_DEDUP_MERGE_THRESHOLD. */
|
|
239
|
+
threshold?: number;
|
|
240
|
+
/**
|
|
241
|
+
* Pacing cap on merge operations this run; validated >= 0 →
|
|
242
|
+
* DEFAULT_DEDUP_NIGHTLY_MAX_MERGES. `0` disables the machinery entirely
|
|
243
|
+
* (discovery skipped, zeroed report).
|
|
244
|
+
*/
|
|
245
|
+
maxMerges?: number;
|
|
246
|
+
/** Discovery + bounded preview only — zero writes, no lock, no backup. */
|
|
247
|
+
dryRun?: boolean;
|
|
248
|
+
/** Config override (backupRetention) — defaults to reading stateDir/config.json. */
|
|
249
|
+
config?: Record<string, unknown> | null;
|
|
250
|
+
/** Capture-lock acquirer override (tests). Defaults to the real capture.ts lock. */
|
|
251
|
+
acquireLock?: typeof acquireCaptureLock;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The >= dedupAutoMergeThreshold band of the unified resolution pass (#392):
|
|
255
|
+
* planDedup discovery + per-cluster mergeCluster — LLM-free, budget-free, so
|
|
256
|
+
* an LLM-less night still drains duplicates. Its own short capture-lock
|
|
257
|
+
* window and pre-merge backup; fail-soft on a busy lock (lock_busy) and on a
|
|
258
|
+
* backup failure (backup_failed) — zero merges either way, never a throw.
|
|
259
|
+
*
|
|
260
|
+
* Also persists the deterministic band's cumulative statistics to state.json
|
|
261
|
+
* `resolutionBandStats` (label `>=threshold`; losers count as merge verdicts
|
|
262
|
+
* at confidence 1.0, mismatch clusters as metadata_skipped) — skipped
|
|
263
|
+
* entirely on dry-run. Called from the reconsolidation stage (main path) and
|
|
264
|
+
* from runConsolidation's quiet-night skip path — exactly one of the two per
|
|
265
|
+
* run.
|
|
266
|
+
*/
|
|
267
|
+
export declare function runDeterministicMergeZone(db: Database.Database, opts?: DeterministicMergeZoneOptions): Promise<DeterministicMergeZoneReport>;
|
|
138
268
|
/**
|
|
139
269
|
* Run `hicortex dedup`. Dry run by default (options.apply falsy) — discovery
|
|
140
270
|
* + merge planning only, zero writes. `options.apply` executes: backup, then
|
|
141
|
-
* one transaction per cluster.
|
|
271
|
+
* one transaction per cluster. Fails fast (throws) on a busy capture lock or
|
|
272
|
+
* a failed backup — a deliberate manual command should be retried by the
|
|
273
|
+
* operator, not silently deferred (the nightly zone is the fail-soft twin).
|
|
142
274
|
*/
|
|
143
275
|
export declare function runDedup(options?: DedupOptions): Promise<DedupReport>;
|
|
144
276
|
/** Escape SQL LIKE wildcards — session ids (e.g. Hermes) can contain "_"/"%". */
|