@gamaze/hicortex 0.20.4 → 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 +4 -1
- 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 +6 -2
- package/dist/cli.js +62 -3
- package/dist/consolidate.d.ts +8 -1
- package/dist/consolidate.js +82 -2
- 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 +84 -13
- package/dist/mcp-stdio.js +6 -1
- 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 +1 -1
- package/pi-extension/hicortex/index.ts +7 -7
- package/server.json +2 -2
|
@@ -19,13 +19,25 @@
|
|
|
19
19
|
* The section name is RESERVED: PUT /identity rejects it, and the synthetic
|
|
20
20
|
* text overrides any user file of the same name (enforced means enforced).
|
|
21
21
|
* Off-switch: config `memoryInstructions: false`.
|
|
22
|
+
*
|
|
23
|
+
* Since #383 the file is also the source for the MCP standing instructions
|
|
24
|
+
* (the initialize-result `instructions` field) — the same policy, shaped for
|
|
25
|
+
* passive MCP clients, behind the same off-switch.
|
|
22
26
|
*/
|
|
23
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
28
|
exports.MEMORY_SECTION_NAME = void 0;
|
|
25
29
|
exports.renderMemoryInstructions = renderMemoryInstructions;
|
|
30
|
+
exports.renderMcpInstructions = renderMcpInstructions;
|
|
31
|
+
exports.resolveMcpInstructions = resolveMcpInstructions;
|
|
26
32
|
exports.isReservedSectionName = isReservedSectionName;
|
|
27
33
|
exports.injectMemorySection = injectMemorySection;
|
|
28
34
|
exports.MEMORY_SECTION_NAME = "memory";
|
|
35
|
+
/** Capture policy, shared by BOTH instruction surfaces (identity section +
|
|
36
|
+
* MCP initialize result) so they can never disagree. Sentence only — each
|
|
37
|
+
* renderer prefixes its own bullet marker. */
|
|
38
|
+
const CAPTURE_POLICY = "Capture is automatic (nightly). Do not manually ingest routine content — `hicortex_ingest` is for explicitly requested learnings only.";
|
|
39
|
+
/** Infrastructure policy, shared by BOTH instruction surfaces (same rule). */
|
|
40
|
+
const INFRA_POLICY = "Never inspect, test, or modify memory/plugin/gateway infrastructure (configs, services, tokens). If a memory tool seems missing or broken, say so and stop.";
|
|
29
41
|
/** The product-authored instruction text. Keep compact (~120 tokens): it is
|
|
30
42
|
* injected once per session into every agent on the fleet. */
|
|
31
43
|
function renderMemoryInstructions() {
|
|
@@ -34,10 +46,35 @@ function renderMemoryInstructions() {
|
|
|
34
46
|
"- A `## Memory recall (auto)` index may arrive with prompts: it is a MENU, not content. Fetch a full memory with `hicortex_get(id)` when the entry could change how you handle the current task.",
|
|
35
47
|
"- Recall before assuming: `hicortex_search` for prior decisions/facts/preferences, `hicortex_recent` to catch up on a project.",
|
|
36
48
|
"- Cite any memory you rely on by id + date, and mark it `FETCHED` (you read the full memory via `hicortex_get`) or `SNIPPET` (the one-line entry only). Don't present a SNIPPET citation as established. On conflicts, newer memories supersede older.",
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
`- ${CAPTURE_POLICY}`,
|
|
50
|
+
`- ${INFRA_POLICY}`,
|
|
39
51
|
].join("\n");
|
|
40
52
|
}
|
|
53
|
+
/** The MCP-client-shaped sibling (#383): standing instructions for the MCP
|
|
54
|
+
* `instructions` field in the initialize result — the MCP-native
|
|
55
|
+
* SessionStart. Passive MCP clients (Claude Desktop etc.) run no hooks and
|
|
56
|
+
* see no injected sections, so this field is the ONLY product-owned
|
|
57
|
+
* guidance their model ever receives; the 2026-09-10 field test showed the
|
|
58
|
+
* memory going entirely unused without it. Compact by construction (same
|
|
59
|
+
* ~120-token budget as the identity sibling) and shares the two policy
|
|
60
|
+
* sentences verbatim — one source, two surfaces. Drops the hook-only
|
|
61
|
+
* surfaces (the `## Memory recall (auto)` index) those clients never see. */
|
|
62
|
+
function renderMcpInstructions() {
|
|
63
|
+
return [
|
|
64
|
+
"This server is the user's persistent long-term memory, shared by all their agents and sessions: what was learned, decided, or corrected survives.",
|
|
65
|
+
"CALL `hicortex_search` BEFORE answering questions about the user's history, projects, or preferences — before assuming, guessing, or asking the user something that may already be known.",
|
|
66
|
+
"Call `hicortex_recent` at the start of substantive work on a project to catch up on its latest state.",
|
|
67
|
+
"Search results are one-line summaries — fetch the full memory with `hicortex_get` when it could change how you handle the task, and cite what you rely on (id + date). On conflicts, newer memories supersede older.",
|
|
68
|
+
`- ${CAPTURE_POLICY}`,
|
|
69
|
+
`- ${INFRA_POLICY}`,
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
72
|
+
/** The config gate as a pure function (#383): enabled → the rendered text,
|
|
73
|
+
* disabled → undefined (the SDK omits the field from the initialize result,
|
|
74
|
+
* so `memoryInstructions: false` silences BOTH surfaces with one switch). */
|
|
75
|
+
function resolveMcpInstructions(enabled) {
|
|
76
|
+
return enabled ? renderMcpInstructions() : undefined;
|
|
77
|
+
}
|
|
41
78
|
/** True for the reserved product section name (case-insensitive guard —
|
|
42
79
|
* section names are lowercase by allowlist, but be safe). */
|
|
43
80
|
function isReservedSectionName(name) {
|
package/dist/nightly.js
CHANGED
|
@@ -735,7 +735,20 @@ async function runNightly(options = {}) {
|
|
|
735
735
|
// #241: config-driven total LLM-call ceiling (default 5000, was 200).
|
|
736
736
|
(0, config_read_js_1.readPositiveConfig)(savedConfig ?? {}, "consolidateMaxLlmCalls", consolidate_js_1.CONSOLIDATE_MAX_LLM_CALLS),
|
|
737
737
|
// #245: soft cap on the corpus (default 10000; 0 disables eviction).
|
|
738
|
-
memorySoftCapResolved
|
|
738
|
+
memorySoftCapResolved, {
|
|
739
|
+
// #384 reconsolidation knobs — threaded exactly like the
|
|
740
|
+
// supersession pair above; the stage validates and falls back
|
|
741
|
+
// to its defaults (0.75 / 0.80) on invalid/absent values.
|
|
742
|
+
minSimilarity: savedConfig?.correctionMinSimilarity,
|
|
743
|
+
rewriteMinConfidence: savedConfig?.correctionRewriteMinConfidence,
|
|
744
|
+
// #392 unified-resolution knobs: the deterministic-merge
|
|
745
|
+
// ceiling (legacy dedupMergeThreshold honored when the new
|
|
746
|
+
// key is absent) and the pacing cap. Same validation posture
|
|
747
|
+
// — the stage defaults to 0.92 / 250.
|
|
748
|
+
autoMergeThreshold: (savedConfig?.dedupAutoMergeThreshold ??
|
|
749
|
+
savedConfig?.dedupMergeThreshold),
|
|
750
|
+
maxMerges: savedConfig?.dedupNightlyMaxMerges,
|
|
751
|
+
});
|
|
739
752
|
console.log(`[hicortex] Consolidation ${report.status} in ${report.elapsed_seconds}s` +
|
|
740
753
|
(report.stages.reflection ? ` (${report.stages.reflection.lessons_generated} lessons)` : ""));
|
|
741
754
|
consolidationStatus = report.status;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconsolidation (#384, #392) — the store resolves its own corrections, and
|
|
3
|
+
* THE unified resolution stage.
|
|
4
|
+
*
|
|
5
|
+
* Nightly consolidation stage (runs as Stage 3.8, after supersession, before
|
|
6
|
+
* decay/prune) that detects memories which correct, retract, supersede, or
|
|
7
|
+
* DUPLICATE older ones; REWRITES corrected facts in place (absorbing
|
|
8
|
+
* transition-only trigger memories), MERGES confirmed duplicates via the
|
|
9
|
+
* dedup core (absorbing the loser), and marks everything else. Companions to
|
|
10
|
+
* the stage:
|
|
11
|
+
* - explicit write-time marking (`corrects`/`supersedes` at /ingest +
|
|
12
|
+
* `hicortex_ingest`) — deterministic link + status, zero LLM;
|
|
13
|
+
* - `memory_history` audit + the `hicortex history` CLI (listing + rollback).
|
|
14
|
+
*
|
|
15
|
+
* #392 — one zone system, ONE verdict per pair: below `correctionMinSimilarity`
|
|
16
|
+
* (floor, 0.75) pairs are not candidates; in [floor, `dedupAutoMergeThreshold`)
|
|
17
|
+
* (ceiling, 0.92) each unlinked pair gets ONE verdict call whose action is
|
|
18
|
+
* `merge` | `corrects` | `supersedes` | `none`; at/above the ceiling the
|
|
19
|
+
* deterministic merge zone (dedup.ts runDeterministicMergeZone — LLM-free,
|
|
20
|
+
* budget-free) owns the pair. The merge disposition reuses the dedup core's
|
|
21
|
+
* execution (canonical pick, link re-point, dedup_log, metadata rails); a
|
|
22
|
+
* merge verdict below `correctionRewriteMinConfidence` keeps both memories.
|
|
23
|
+
*
|
|
24
|
+
* Status vocabulary (code-defined, extensible — deliberately NOT config):
|
|
25
|
+
* NULL/'active' default | 'superseded' + 'retracted' demote in ranking |
|
|
26
|
+
* 'corrected' = rewritten, never demotes (demoting it would bury the
|
|
27
|
+
* correction — the exact failure this stage fixes) | 'absorbed' = invisible
|
|
28
|
+
* to recall (no vector row, no FTS row; plain row + link kept as evidence,
|
|
29
|
+
* session lineage, rollback reference). Merge losers share 'absorbed'
|
|
30
|
+
* (storage.absorbMemory is the one primitive) — the only difference is the
|
|
31
|
+
* audit trail: rewrites roll back via memory_history; merges recover via
|
|
32
|
+
* dedup_log + the retained loser row (NOT history-rollback-able).
|
|
33
|
+
*
|
|
34
|
+
* This module deliberately does NOT import consolidate.ts (which imports this
|
|
35
|
+
* module to wire the stage) — the budget is consumed through the structural
|
|
36
|
+
* StageBudget interface below, which BudgetTracker satisfies. dedup.ts is
|
|
37
|
+
* imported (never the reverse) for the merge core.
|
|
38
|
+
*/
|
|
39
|
+
import type Database from "better-sqlite3";
|
|
40
|
+
import type { LlmClient, LlmUsage } from "./llm.js";
|
|
41
|
+
import type { ConsolidationReport } from "./types.js";
|
|
42
|
+
import type { EmbedFn } from "./retrieval.js";
|
|
43
|
+
import { acquireCaptureLock } from "./capture.js";
|
|
44
|
+
/** Stage label used for every budget.use()/recordUsage() call (#384). */
|
|
45
|
+
export declare const RECONSOLIDATION_STAGE_LABEL = "reconsolidation";
|
|
46
|
+
/**
|
|
47
|
+
* Default minimum COSINE similarity for a correction candidate pair. Lower
|
|
48
|
+
* than the supersession stage's 0.80 on purpose: a retraction often rides
|
|
49
|
+
* inside an otherwise unrelated memory (the field failure that opened this
|
|
50
|
+
* issue), so the neighborhood gate must be a touch wider while the LLM
|
|
51
|
+
* verdict + confidence gate carry the precision load.
|
|
52
|
+
*/
|
|
53
|
+
export declare const DEFAULT_CORRECTION_MIN_SIMILARITY = 0.75;
|
|
54
|
+
/**
|
|
55
|
+
* Default minimum verdict confidence for the REWRITE fork. Below this a
|
|
56
|
+
* `corrects` verdict degrades to mark-only — a weak mark is recoverable, a
|
|
57
|
+
* weak rewrite is corruption.
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEFAULT_CORRECTION_REWRITE_MIN_CONFIDENCE = 0.8;
|
|
60
|
+
/** Head of the old content quoted in the provenance footer. */
|
|
61
|
+
export declare const FOOTER_HEAD_MAX_CHARS = 160;
|
|
62
|
+
/** The code-defined status vocabulary (see module doc). Not user-configurable. */
|
|
63
|
+
export type MemoryStatus = "superseded" | "retracted" | "corrected" | "absorbed";
|
|
64
|
+
/**
|
|
65
|
+
* Statuses that demote a memory's ranking score (retrieval.ts findDemotedIds).
|
|
66
|
+
* 'corrected' is deliberately absent — see module doc.
|
|
67
|
+
*/
|
|
68
|
+
export declare const DEMOTED_STATUSES: readonly [MemoryStatus, MemoryStatus];
|
|
69
|
+
/** structural subset of consolidate.BudgetTracker (avoids an import cycle). */
|
|
70
|
+
export interface StageBudget {
|
|
71
|
+
readonly exhausted: boolean;
|
|
72
|
+
use(stage: string, count?: number): boolean;
|
|
73
|
+
recordUsage(stage: string, usage: LlmUsage | undefined): void;
|
|
74
|
+
}
|
|
75
|
+
export interface ReconsolidationOptions {
|
|
76
|
+
/** correctionMinSimilarity (config; default 0.75). Invalid → default. */
|
|
77
|
+
minSimilarity?: number;
|
|
78
|
+
/** correctionRewriteMinConfidence (config; default 0.80). Invalid → default. */
|
|
79
|
+
rewriteMinConfidence?: number;
|
|
80
|
+
/**
|
|
81
|
+
* dedupAutoMergeThreshold (config; default 0.92; legacy dedupMergeThreshold
|
|
82
|
+
* honored by nightly.ts when the new key is absent). The deterministic/LLM
|
|
83
|
+
* boundary of the unified resolution pass (#392): pairs at/above it merge
|
|
84
|
+
* via the LLM-free zone, pairs in [floor, ceiling) get the verdict.
|
|
85
|
+
* Invalid → default.
|
|
86
|
+
*/
|
|
87
|
+
autoMergeThreshold?: number;
|
|
88
|
+
/**
|
|
89
|
+
* dedupNightlyMaxMerges (config; default 250; 0 disables the merge
|
|
90
|
+
* machinery). Counts merge OPERATIONS per run — zone clusters + judged
|
|
91
|
+
* pair merges against ONE cap. Invalid → default.
|
|
92
|
+
*/
|
|
93
|
+
maxMerges?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Capture-lock acquirer override (tests) — the deterministic zone and the
|
|
96
|
+
* judged-merge phase each hold a short lock window. Defaults to the real
|
|
97
|
+
* capture.ts lock. DedupOptions.acquireLock pattern.
|
|
98
|
+
*/
|
|
99
|
+
acquireLock?: typeof acquireCaptureLock;
|
|
100
|
+
}
|
|
101
|
+
/** The 14-field stage report (typed once, in ConsolidationReport). */
|
|
102
|
+
export type ReconsolidationStageResult = NonNullable<ConsolidationReport["stages"]["reconsolidation"]>;
|
|
103
|
+
/**
|
|
104
|
+
* True when a memory is REWRITE-ELIGIBLE — a fact-shaped target. The fork is
|
|
105
|
+
* keyed on the existing taxonomy the code already trusts (facts are
|
|
106
|
+
* rewritten; decisions/plans/experiences are history, marked only).
|
|
107
|
+
*/
|
|
108
|
+
export declare function isFactShapedTarget(mem: {
|
|
109
|
+
memory_type: string;
|
|
110
|
+
content: string;
|
|
111
|
+
}): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The unified resolution verdict (#392): ONE call per unlinked pair decides
|
|
114
|
+
* how the newer memory relates to the older — merge (same underlying
|
|
115
|
+
* fact/verdict, differing in wording/qualifiers), corrects, supersedes, or
|
|
116
|
+
* none (related but distinct).
|
|
117
|
+
*/
|
|
118
|
+
export type ResolutionAction = "merge" | "corrects" | "supersedes" | "none";
|
|
119
|
+
/** Build the constrained pair-verdict prompt (1500-char truncation, supersession precedent). */
|
|
120
|
+
export declare function buildCorrectionVerdictPrompt(oldContent: string, newContent: string): string;
|
|
121
|
+
export interface CorrectionVerdict {
|
|
122
|
+
action: ResolutionAction;
|
|
123
|
+
confidence: number;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Parse the pair verdict. Null on anything unparseable, unknown action, or an
|
|
127
|
+
* out-of-range/missing confidence — the caller counts skipped_infra and moves
|
|
128
|
+
* on (same discipline as parseSupersessionReply: never mis-judge on ambiguity).
|
|
129
|
+
*/
|
|
130
|
+
export declare function parseCorrectionVerdict(reply: string): CorrectionVerdict | null;
|
|
131
|
+
export interface RewriteTriggerDisposition {
|
|
132
|
+
id: string;
|
|
133
|
+
disposition: "absorb" | "keep";
|
|
134
|
+
}
|
|
135
|
+
export interface RewriteContract {
|
|
136
|
+
rewritten: string;
|
|
137
|
+
triggers: RewriteTriggerDisposition[];
|
|
138
|
+
}
|
|
139
|
+
/** Build the constrained rewrite prompt: old content + N trigger contents, nothing else. */
|
|
140
|
+
export declare function buildRewritePrompt(oldContent: string, triggers: Array<{
|
|
141
|
+
id: string;
|
|
142
|
+
content: string;
|
|
143
|
+
}>): string;
|
|
144
|
+
/**
|
|
145
|
+
* Parse + validate the rewrite contract (AC4). Null on ANY failure:
|
|
146
|
+
* - unparseable JSON / wrong shape;
|
|
147
|
+
* - rewritten empty, identical to the old content, or longer than
|
|
148
|
+
* old + 2000 + 500 per additional trigger;
|
|
149
|
+
* - the triggers array not covering every input trigger id exactly once
|
|
150
|
+
* (missing, unknown, or duplicated) or carrying an invalid disposition.
|
|
151
|
+
* A null return degrades the WHOLE group to mark-only (never a partial apply).
|
|
152
|
+
*/
|
|
153
|
+
export declare function parseRewriteReply(reply: string, expectedTriggerIds: string[], oldContent: string): RewriteContract | null;
|
|
154
|
+
/**
|
|
155
|
+
* The provenance footer appended to every rewritten memory:
|
|
156
|
+
* `previously believed "<≤160-char head of old content>" until <date>`.
|
|
157
|
+
* date = the ISO DATE (YYYY-MM-DD) derived from the latest trigger's
|
|
158
|
+
* created_at — the same trigger recorded as the history row's evidence_id.
|
|
159
|
+
*/
|
|
160
|
+
export declare function buildCorrectionFooter(oldContent: string, dateISO: string): string;
|
|
161
|
+
export type ExplicitMarkKind = "corrects" | "supersedes";
|
|
162
|
+
export interface ExplicitMarkInput {
|
|
163
|
+
kind: ExplicitMarkKind;
|
|
164
|
+
/** Raw id reference (8-char prefix or full UUID) as the client sent it. */
|
|
165
|
+
target: string;
|
|
166
|
+
}
|
|
167
|
+
export type ExplicitMarkCheck = {
|
|
168
|
+
ok: true;
|
|
169
|
+
targetId: string;
|
|
170
|
+
} | {
|
|
171
|
+
ok: false;
|
|
172
|
+
httpStatus: number;
|
|
173
|
+
error: string;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Validate an explicit mark target BEFORE anything is written (AC1: on an
|
|
177
|
+
* unknown/ambiguous id the WHOLE request fails and nothing is stored).
|
|
178
|
+
* httpStatus is the REST code; the MCP tool reuses the message verbatim.
|
|
179
|
+
*/
|
|
180
|
+
export declare function checkExplicitMarkTarget(db: Database.Database, input: ExplicitMarkInput): ExplicitMarkCheck;
|
|
181
|
+
/**
|
|
182
|
+
* Apply a validated explicit mark: link old → new + status on the old memory.
|
|
183
|
+
* Deterministic — no LLM. Link strength 1.0: an operator-declared mark, not a
|
|
184
|
+
* measured cosine. `corrects` → `corrected_by` + status `retracted`;
|
|
185
|
+
* `supersedes` → `superseded_by` + status `superseded` (AC1).
|
|
186
|
+
*/
|
|
187
|
+
export declare function applyExplicitMark(db: Database.Database, newMemoryId: string, input: ExplicitMarkInput): void;
|
|
188
|
+
/**
|
|
189
|
+
* Drop a trigger's retrieval candidacy: status `absorbed`, vector row deleted,
|
|
190
|
+
* FTS row deleted (direct DELETE — the AFTER UPDATE trigger's `UPDATE … WHERE
|
|
191
|
+
* rowid` is a silent no-op on the missing row, so later column edits cannot
|
|
192
|
+
* resurrect it). Row + links are KEPT (evidence, session lineage, rollback).
|
|
193
|
+
* Must run inside a transaction. Tags/domain deliberately untouched (only the
|
|
194
|
+
* rewritten TARGET gets its tags cleared).
|
|
195
|
+
*
|
|
196
|
+
* #392: the implementation moved to storage.ts (`absorbMemory`) so the dedup
|
|
197
|
+
* merge core shares the ONE primitive without an import cycle; re-exported
|
|
198
|
+
* here under its historical name for the rewrite/rollback paths (nothing
|
|
199
|
+
* external imports it today, but it is the module's documented surface).
|
|
200
|
+
*/
|
|
201
|
+
export declare const absorbTrigger: (db: Database.Database, triggerId: string) => void;
|
|
202
|
+
/**
|
|
203
|
+
* Replace a memory's vector (delete + insert) — the /update re-embed pattern.
|
|
204
|
+
* Must run inside a transaction (the caller pre-computes the embedding
|
|
205
|
+
* asynchronously, outside the sync transaction).
|
|
206
|
+
*/
|
|
207
|
+
export declare function replaceMemoryVector(db: Database.Database, memoryId: string, embedding: Float32Array): void;
|
|
208
|
+
/**
|
|
209
|
+
* Reverse an absorb: status back to active (NULL), vector re-embedded from the
|
|
210
|
+
* (untouched) content, FTS row re-inserted explicitly (migration v10's rebuild
|
|
211
|
+
* pattern — the AFTER UPDATE trigger cannot recreate a deleted FTS row).
|
|
212
|
+
* Must run inside a transaction; no-op on a memory that is not currently
|
|
213
|
+
* absorbed (never resurrects an already-live row, never duplicates an FTS row).
|
|
214
|
+
*/
|
|
215
|
+
export declare function unabsorbTrigger(db: Database.Database, triggerId: string, embedding: Float32Array): void;
|
|
216
|
+
export interface MemoryHistoryRow {
|
|
217
|
+
id: number;
|
|
218
|
+
memory_id: string;
|
|
219
|
+
old_content: string;
|
|
220
|
+
new_content: string;
|
|
221
|
+
prev_status: string | null;
|
|
222
|
+
new_status: string | null;
|
|
223
|
+
triggers_json: string | null;
|
|
224
|
+
evidence_id: string | null;
|
|
225
|
+
confidence: number | null;
|
|
226
|
+
cause: string;
|
|
227
|
+
created_at: string;
|
|
228
|
+
}
|
|
229
|
+
/** History rows for one memory, oldest first. */
|
|
230
|
+
export declare function getMemoryHistory(db: Database.Database, memoryId: string): MemoryHistoryRow[];
|
|
231
|
+
export declare function getHistoryRow(db: Database.Database, historyRowId: number): MemoryHistoryRow | null;
|
|
232
|
+
export interface ResolutionBand {
|
|
233
|
+
/** Band label, e.g. "0.75-0.8" or ">=0.92". */
|
|
234
|
+
label: string;
|
|
235
|
+
/** Inclusive lower edge. */
|
|
236
|
+
lo: number;
|
|
237
|
+
/** Exclusive upper edge (Infinity for the deterministic >= band). */
|
|
238
|
+
hi: number;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Build the verdict-statistic bands from the LIVE floor/ceiling (#392): edges
|
|
242
|
+
* = sorted unique [floor, 0.80, 0.85, 0.90, ceiling]; bands are [e0,e1) …
|
|
243
|
+
* [e(n-1),en) plus the deterministic ">=en" band. Intermediate edges outside
|
|
244
|
+
* (floor, ceiling) are dropped — a band below the floor can never receive a
|
|
245
|
+
* verdict (candidates are >= floor), so a raised floor collapses the lower
|
|
246
|
+
* bands away instead of seeding dead labels. Labels use the numbers as
|
|
247
|
+
* configured ("0.75-0.8" … "0.9-0.92", ">=0.92").
|
|
248
|
+
*/
|
|
249
|
+
export declare function buildResolutionBands(floor: number, ceiling: number): ResolutionBand[];
|
|
250
|
+
/**
|
|
251
|
+
* The band a pair's cosine falls into: the [lo, hi) band that contains it,
|
|
252
|
+
* falling through to the final >= band for cosines at/above the last edge.
|
|
253
|
+
* Null only for a cosine below the floor (never a candidate).
|
|
254
|
+
*/
|
|
255
|
+
export declare function bandForCosine(bands: ResolutionBand[], cosine: number): ResolutionBand | null;
|
|
256
|
+
/**
|
|
257
|
+
* Nightly reconsolidation stage (#384, #392 — THE unified resolution stage).
|
|
258
|
+
*
|
|
259
|
+
* Phase 0 (#392): the deterministic merge zone (pairs >= the ceiling) runs
|
|
260
|
+
* first — LLM-free, budget-free, own lock/backup/cap.
|
|
261
|
+
*
|
|
262
|
+
* Scan: every memory with rowid > reconsolidationCursor (no shape filter;
|
|
263
|
+
* absorbed candidates are skipped — invisible memories are not re-judged).
|
|
264
|
+
* Each candidate's pairs: incoming explicit marks (verified once, AC7) then
|
|
265
|
+
* up-to-5 older KNN neighbors in [floor, ceiling) (verdict call per unlinked
|
|
266
|
+
* pair, AC2 — pairs at/above the ceiling are counted, never judged). Confirmed
|
|
267
|
+
* `corrects` pairs above the confidence gate on fact-shaped targets group by
|
|
268
|
+
* target into ONE rewrite call each (AC3); confirmed `merge` pairs queue for
|
|
269
|
+
* the merge phase; everything else is mark-only.
|
|
270
|
+
*
|
|
271
|
+
* Merge phase (#392): queued pairs merge through the dedup core under one
|
|
272
|
+
* lock/backup window, capped with the zone by dedupNightlyMaxMerges. A pair
|
|
273
|
+
* that cannot apply keeps both memories and holds the cursor.
|
|
274
|
+
*
|
|
275
|
+
* Cursor discipline mirrors stageSupersession: the cursor advances past a
|
|
276
|
+
* candidate once its neighbor set has been considered, regardless of infra
|
|
277
|
+
* skips — EXCEPT when rewrite groups or confirmed merges could not be applied
|
|
278
|
+
* (budget exhausted / rewrite-call infra error / merge cap or lock): the
|
|
279
|
+
* cursor then holds BELOW the earliest candidate contributing to the
|
|
280
|
+
* un-applied work, so those pairs are re-detected next run (dup-over-loss —
|
|
281
|
+
* an un-marked, un-rewritten, un-merged confirmed resolution must never be
|
|
282
|
+
* silently dropped by the cursor passing it).
|
|
283
|
+
*
|
|
284
|
+
* Dry-run: the zone's discovery + the free idempotency check only — zero LLM
|
|
285
|
+
* calls, zero writes, no cursor or band-stats persistence.
|
|
286
|
+
*/
|
|
287
|
+
export declare function stageReconsolidation(db: Database.Database, llm: LlmClient, budget: StageBudget, embedFn: EmbedFn, dryRun: boolean, stateDir: string | undefined, options?: ReconsolidationOptions): Promise<ReconsolidationStageResult>;
|
|
288
|
+
export interface RollbackResult {
|
|
289
|
+
historyRowId: number;
|
|
290
|
+
memoryId: string;
|
|
291
|
+
restoredStatus: string | null;
|
|
292
|
+
unabsorbed: string[];
|
|
293
|
+
newHistoryRowId: number;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Roll back ONE rewrite history row: restore the recorded prior content and
|
|
297
|
+
* prior status via the same mechanics as the rewrite (re-embed, clear tags +
|
|
298
|
+
* domain NULL), reverse every absorb recorded in triggers_json (status
|
|
299
|
+
* restored, vector re-embedded, FTS row re-inserted), and write the rollback's
|
|
300
|
+
* own history row (cause `rollback`).
|
|
301
|
+
*
|
|
302
|
+
* Newest-first discipline: the row must be the NEWEST history entry for its
|
|
303
|
+
* memory (rolling back an older entry under a newer one would clobber — undo
|
|
304
|
+
* the newest first). The rollback row itself can be rolled back (undo the
|
|
305
|
+
* undo), which is what makes the chain navigable.
|
|
306
|
+
*/
|
|
307
|
+
export declare function rollbackHistoryRow(db: Database.Database, historyRowId: number, embedFn: EmbedFn): Promise<RollbackResult>;
|
|
308
|
+
export interface HistoryCliOptions {
|
|
309
|
+
/** DB path override (tests / snapshot verification). Defaults to resolveDbPath(). */
|
|
310
|
+
dbPath?: string;
|
|
311
|
+
/** Memory id (8-char prefix or full UUID) whose history to list. */
|
|
312
|
+
memoryId?: string;
|
|
313
|
+
/** memory_history.id to roll back. */
|
|
314
|
+
rollbackId?: number;
|
|
315
|
+
/** Embed fn injection for tests; production lazy-loads the ONNX embedder. */
|
|
316
|
+
embedFn?: EmbedFn;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Runner for `hicortex history <id>` / `hicortex history --rollback <n>`.
|
|
320
|
+
* Returns a process exit code (0 success, 1 failure); throws only on
|
|
321
|
+
* unexpected infra errors (cli.ts prints those).
|
|
322
|
+
*/
|
|
323
|
+
export declare function runHistoryCommand(options: HistoryCliOptions): Promise<number>;
|