rag-memory-epf-mcp 6.4.0 → 6.4.2

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.
@@ -14,5 +14,6 @@ type Block = {
14
14
  heading: boolean;
15
15
  };
16
16
  export declare function buildBlocks(lines: string[]): Block[];
17
+ export declare function splitOversize(block: string, enc: Tiktoken, maxTokens: number): string[];
17
18
  export declare function chunkStructured(text: string, enc: Tiktoken, maxTokens?: number): CSegment[];
18
19
  export {};
@@ -8,13 +8,18 @@ export const LEGACY_SIGNATURE = 'legacy-unknown';
8
8
  export function mergeMinTokens(maxTokens) {
9
9
  return Math.floor(maxTokens / 2); // NOT >>1 — bitshift wraps at 2^31 (r12)
10
10
  }
11
+ // kg-relation-evidence D3: fallback=cp-window-N names the windowed oversize split below. Boundaries
12
+ // differ from cp-exact only for blocks that reach the fallback, so the signature says so.
11
13
  export function effectiveSignature(maxTokens) {
12
- return `c1:enc=cl100k_base:max=${maxTokens}:overlap=0:fence=on:merge=${mergeMinTokens(maxTokens)}:fallback=cp-exact-${maxTokens}`;
14
+ return `c1:enc=cl100k_base:max=${maxTokens}:overlap=0:fence=on:merge=${mergeMinTokens(maxTokens)}:fallback=cp-window-${maxTokens}`;
13
15
  }
14
16
  // Strict parse (r5-15): the regex alone classified impossible signatures
15
17
  // (max=0, max/fallback mismatch) as current. Cross-check the components.
18
+ // Both c1 fallbacks are the current FORMAT (stats "current" vs legacy/unknown, index.ts:4826-4831):
19
+ // a document stamped cp-exact by 6.4.0 is not re-chunked by an unchanged-content sync and must not be
20
+ // reported as an unknown format.
16
21
  export function isCurrentFormatSignature(sig) {
17
- const m = /^c1:enc=cl100k_base:max=(\d+):overlap=0:fence=on:merge=(\d+):fallback=cp-exact-(\d+)$/.exec(sig);
22
+ const m = /^c1:enc=cl100k_base:max=(\d+):overlap=0:fence=on:merge=(\d+):fallback=cp-(?:exact|window)-(\d+)$/.exec(sig);
18
23
  if (!m)
19
24
  return false;
20
25
  const max = Number(m[1]);
@@ -104,33 +109,75 @@ export function buildBlocks(lines) {
104
109
  }
105
110
  return blocks;
106
111
  }
107
- // cp-exact oversize split, non-monotonicity-safe (spec §4.3-6, r5-5).
108
- // BPE prefix token counts are NOT monotonic ('/sdkX' -> 1,1,2,1,2), so binary
109
- // search is invalid. Scan prefixes linearly, remember the last fitting one, and
110
- // keep probing LOOKAHEAD candidates past a miss to recover dips. If not even
111
- // one codepoint fits, throw — never emit an over-budget chunk.
112
+ // Oversize split for a block with no structural cut (spec §4.3-6; kg-relation-evidence D3).
113
+ // 6.4.0 scanned every codepoint prefix and re-encoded it (one encode per codepoint, quadratic per
114
+ // chunk; 20k chars 4.6 s, 575k 162 s measured). Now: encode a window once, map the first maxTokens
115
+ // window tokens back to a codepoint count, then VERIFY that candidate by encoding the real substring —
116
+ // the window tokenisation is only an estimate, because BPE re-tokenises a truncated final piece
117
+ // differently (prefix counts are not monotonic: '/sdkX' -> 1,1,2,1,2). Shrink the token count
118
+ // (1, 2, 4, ... fewer) until a candidate verifies; if none does, fall back to the 6.4.0 linear scan for
119
+ // this chunk only, which also keeps its throw. Contract (chunk-invariants, chunker-c): lossless,
120
+ // contiguous codepoint offsets, every chunk's ACTUAL encode length <= maxTokens, deterministic, always
121
+ // advances. Not a longest-fit guarantee — neither was LOOKAHEAD.
112
122
  const LOOKAHEAD = 16;
113
- function splitOversize(block, enc, maxTokens) {
123
+ const WINDOW_MIN_CP = 64;
124
+ const WINDOW_CP_PER_TOKEN = 8;
125
+ function utf8Bytes(cp) {
126
+ const c = cp.codePointAt(0);
127
+ return c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; // a lone surrogate encodes as U+FFFD = 3
128
+ }
129
+ function linearFit(cps, start, enc, maxTokens) {
130
+ let lastFit = 0;
131
+ let missesSinceFit = 0;
132
+ for (let probe = start + 1; probe <= cps.length && missesSinceFit < LOOKAHEAD; probe++) {
133
+ const t = enc.encode(cps.slice(start, probe).join('')).length;
134
+ if (t <= maxTokens) {
135
+ lastFit = probe - start;
136
+ missesSinceFit = 0;
137
+ }
138
+ else
139
+ missesSinceFit++;
140
+ }
141
+ return lastFit;
142
+ }
143
+ function windowFit(cps, start, enc, maxTokens) {
144
+ const rest = cps.length - start;
145
+ let w = Math.min(rest, Math.max(WINDOW_MIN_CP, maxTokens * WINDOW_CP_PER_TOKEN));
146
+ let toks = enc.encode(cps.slice(start, start + w).join(''));
147
+ while (toks.length <= maxTokens && w < rest) {
148
+ w = Math.min(rest, w * 2);
149
+ toks = enc.encode(cps.slice(start, start + w).join(''));
150
+ }
151
+ if (toks.length <= maxTokens)
152
+ return rest; // the whole remainder fits
153
+ for (let keep = maxTokens, step = 1; keep >= 1; keep -= step, step *= 2) {
154
+ const bytes = enc.decode(toks.slice(0, keep)).length;
155
+ let used = 0;
156
+ let c = 0;
157
+ while (c < w && used + utf8Bytes(cps[start + c]) <= bytes) {
158
+ used += utf8Bytes(cps[start + c]);
159
+ c++;
160
+ }
161
+ if (c === 0)
162
+ continue;
163
+ if (enc.encode(cps.slice(start, start + c).join('')).length <= maxTokens)
164
+ return c;
165
+ }
166
+ return 0;
167
+ }
168
+ export function splitOversize(block, enc, maxTokens) {
114
169
  const cps = [...block];
115
170
  const out = [];
116
171
  let start = 0;
117
172
  while (start < cps.length) {
118
- let lastFit = 0;
119
- let missesSinceFit = 0;
120
- for (let probe = start + 1; probe <= cps.length && missesSinceFit < LOOKAHEAD; probe++) {
121
- const t = enc.encode(cps.slice(start, probe).join('')).length;
122
- if (t <= maxTokens) {
123
- lastFit = probe - start;
124
- missesSinceFit = 0;
125
- }
126
- else
127
- missesSinceFit++;
128
- }
129
- if (lastFit === 0) {
173
+ let take = windowFit(cps, start, enc, maxTokens);
174
+ if (take === 0)
175
+ take = linearFit(cps, start, enc, maxTokens);
176
+ if (take === 0) {
130
177
  throw new Error(`chunker c1: codepoint at offset ${start} exceeds maxTokens=${maxTokens} on its own — cannot honor the token budget`);
131
178
  }
132
- out.push(cps.slice(start, start + lastFit).join(''));
133
- start += lastFit;
179
+ out.push(cps.slice(start, start + take).join(''));
180
+ start += take;
134
181
  }
135
182
  return out;
136
183
  }
@@ -0,0 +1,156 @@
1
+ import type Database from 'better-sqlite3';
2
+ export declare const EVIDENCE_FORMAT: "evidence-v1";
3
+ export declare const EVIDENCE_CAPS: Readonly<{
4
+ maxEntries: 8;
5
+ maxQuoteCodepoints: 4000;
6
+ maxBodyCodepoints: 8000;
7
+ }>;
8
+ export declare const FINGERPRINT_TAG = "kg-evidence-fp/1";
9
+ export declare const LEGACY_CMP_TAG = "kg-legacy-cmp/1";
10
+ export declare const EVIDENCE_SEMANTICS = "caller-selected evidence; the engine verified the references exist verbatim; not an independent proof of the relation";
11
+ export type WhyStatus = 'reason-stated' | 'relation-only' | 'unjudged';
12
+ export interface EvidenceSupportInput {
13
+ why_status?: WhyStatus;
14
+ valid_time?: string;
15
+ scope_note?: string;
16
+ }
17
+ export type EvidenceEntryInput = {
18
+ kind: 'document';
19
+ chunk_id: string;
20
+ start: number;
21
+ end: number;
22
+ quote_sha256: string;
23
+ } | {
24
+ kind: 'observation';
25
+ observation_id: string;
26
+ revision_no: number;
27
+ content_sha256: string;
28
+ };
29
+ export interface EvidenceItemInput {
30
+ relationship_id: string;
31
+ evidence: EvidenceEntryInput[];
32
+ support?: EvidenceSupportInput;
33
+ }
34
+ export interface SourceTuple {
35
+ source_kind: string;
36
+ source_ref: string;
37
+ source_hash: string | null;
38
+ }
39
+ export interface DocumentRefMeta {
40
+ kind: 'document';
41
+ document_id: string;
42
+ chunk_id: string;
43
+ start: number;
44
+ end: number;
45
+ chunk_text_sha256: string;
46
+ quote_sha256: string;
47
+ }
48
+ export interface ObservationRefMeta {
49
+ kind: 'observation';
50
+ observation_id: string;
51
+ entity_id: string;
52
+ revision_no: number;
53
+ content_sha256: string;
54
+ status_at_generation: 'active';
55
+ sources: SourceTuple[];
56
+ source_summary: 'recorded' | 'unrecorded' | 'import-only';
57
+ }
58
+ export type EvidenceRefMeta = DocumentRefMeta | ObservationRefMeta;
59
+ export interface EvidenceChunkMetadataV1 {
60
+ kg_format: typeof EVIDENCE_FORMAT;
61
+ relationship_id: string;
62
+ source_entity_id: string;
63
+ target_entity_id: string;
64
+ source_entity: string;
65
+ target_entity: string;
66
+ relation_type: string;
67
+ direction: 'source->target';
68
+ confidence: number;
69
+ evidence_refs: EvidenceRefMeta[];
70
+ support: {
71
+ why_status: WhyStatus;
72
+ valid_time: string | null;
73
+ scope_note: string | null;
74
+ };
75
+ generated_ref_status: 'refs-resolved';
76
+ input_fingerprint: string;
77
+ fingerprint_profile_id: number;
78
+ body_codepoints: number;
79
+ embedded_truncation: 'unchecked';
80
+ evidence_semantics: string;
81
+ }
82
+ export interface FingerprintInput {
83
+ relationship_id: string;
84
+ source_entity_id: string;
85
+ target_entity_id: string;
86
+ relation_type: string;
87
+ source_name: string;
88
+ target_name: string;
89
+ kg_format: typeof EVIDENCE_FORMAT;
90
+ refs: EvidenceRefMeta[];
91
+ profile_id: number;
92
+ }
93
+ export interface PreparedEvidenceChunk {
94
+ chunk_id: string;
95
+ relationship_id: string;
96
+ text: string;
97
+ body_codepoints: number;
98
+ metadata: EvidenceChunkMetadataV1;
99
+ }
100
+ export interface KgChunkRow {
101
+ rowid: number;
102
+ chunk_id: string;
103
+ chunk_type: string | null;
104
+ entity_id: string | null;
105
+ relationship_id: string | null;
106
+ text: string | null;
107
+ metadata: string | null;
108
+ }
109
+ export interface KgEmbedCapture {
110
+ rowid: number;
111
+ chunk_id: string;
112
+ text_sha256: string;
113
+ cmp: string | null;
114
+ profile_id: number;
115
+ }
116
+ export declare function cpLength(s: string): number;
117
+ export declare function cpSlice(s: string, start: number, end: number): string;
118
+ export declare function sha256Utf8(s: string): string;
119
+ export declare function compareSourceTuples(a: SourceTuple, b: SourceTuple): number;
120
+ export declare function fingerprintSerialization(f: FingerprintInput): string;
121
+ export declare function computeInputFingerprint(f: FingerprintInput): string;
122
+ export declare function fingerprintFromMetadata(m: EvidenceChunkMetadataV1, profileId: number): string;
123
+ export declare function buildEvidenceBody(sourceName: string, relationType: string, targetName: string, quotes: string[]): string;
124
+ export declare function evidenceChunkId(relationshipId: string): string;
125
+ export declare function parseEvidenceMetadata(raw: string | null): EvidenceChunkMetadataV1 | null;
126
+ export declare function kgComparisonValue(row: KgChunkRow): string | null;
127
+ export declare function captureKgEmbedInput(row: KgChunkRow, profileId: number): KgEmbedCapture;
128
+ export declare class EvidenceInputError extends Error {
129
+ readonly code: string;
130
+ readonly itemIndex: number;
131
+ constructor(code: string, itemIndex: number, detail: string);
132
+ }
133
+ export declare class EvidenceCommitConflictError extends Error {
134
+ readonly itemIndex: number;
135
+ readonly reason: string;
136
+ constructor(itemIndex: number, reason: string);
137
+ }
138
+ export declare function readSourceTuples(db: Database.Database, observationId: string): SourceTuple[];
139
+ export declare function resolveEvidenceItem(db: Database.Database, item: EvidenceItemInput, itemIndex: number, profileId: number): PreparedEvidenceChunk;
140
+ export type EvidenceStatus = 'claim_only' | 'refs_available' | 'refs_unavailable';
141
+ export interface EvidenceRefStatus {
142
+ index: number;
143
+ kind: string;
144
+ status: 'available' | 'missing' | 'mismatch';
145
+ ref: unknown;
146
+ text: string | null;
147
+ }
148
+ export declare function resolveEvidenceStatus(db: Database.Database, meta: Record<string, unknown> | null): {
149
+ status: EvidenceStatus;
150
+ refs: EvidenceRefStatus[];
151
+ };
152
+ export declare function isSourceTuple(v: unknown): v is SourceTuple;
153
+ export declare function isWellFormedRef(v: unknown): boolean;
154
+ export declare function collectEvidenceRowidsCitingObservations(db: Database.Database, observationIds: string[]): number[];
155
+ export declare function collectEvidenceRowidsCitingDocument(db: Database.Database, documentId: string): number[];
156
+ export declare function collectEvidenceRowidsCitingOwnerEntity(db: Database.Database, entityId: string): number[];