memwarden 0.0.1

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.
Files changed (119) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +402 -0
  3. package/dist/bundle/bundle.d.ts +28 -0
  4. package/dist/bundle/bundle.js +85 -0
  5. package/dist/cli/bin.d.ts +2 -0
  6. package/dist/cli/bin.js +593 -0
  7. package/dist/cli/connect.d.ts +63 -0
  8. package/dist/cli/connect.js +121 -0
  9. package/dist/cli/hook.d.ts +24 -0
  10. package/dist/cli/hook.js +186 -0
  11. package/dist/cli/tools.d.ts +47 -0
  12. package/dist/cli/tools.js +246 -0
  13. package/dist/daemon/ensure.d.ts +12 -0
  14. package/dist/daemon/ensure.js +54 -0
  15. package/dist/daemon/service.d.ts +15 -0
  16. package/dist/daemon/service.js +210 -0
  17. package/dist/embedding/index.d.ts +10 -0
  18. package/dist/embedding/index.js +33 -0
  19. package/dist/embedding/local-embedding.d.ts +14 -0
  20. package/dist/embedding/local-embedding.js +80 -0
  21. package/dist/functions/access-tracker.d.ts +13 -0
  22. package/dist/functions/access-tracker.js +92 -0
  23. package/dist/functions/audit.d.ts +46 -0
  24. package/dist/functions/audit.js +0 -0
  25. package/dist/functions/cjk-segmenter.d.ts +6 -0
  26. package/dist/functions/cjk-segmenter.js +120 -0
  27. package/dist/functions/compress-synthetic.d.ts +2 -0
  28. package/dist/functions/compress-synthetic.js +104 -0
  29. package/dist/functions/config.d.ts +68 -0
  30. package/dist/functions/config.js +231 -0
  31. package/dist/functions/conflicts.d.ts +19 -0
  32. package/dist/functions/conflicts.js +328 -0
  33. package/dist/functions/context.d.ts +3 -0
  34. package/dist/functions/context.js +155 -0
  35. package/dist/functions/dedup.d.ts +11 -0
  36. package/dist/functions/dedup.js +51 -0
  37. package/dist/functions/dejafix.d.ts +96 -0
  38. package/dist/functions/dejafix.js +356 -0
  39. package/dist/functions/doctor.d.ts +29 -0
  40. package/dist/functions/doctor.js +137 -0
  41. package/dist/functions/forget.d.ts +3 -0
  42. package/dist/functions/forget.js +87 -0
  43. package/dist/functions/hybrid-search.d.ts +17 -0
  44. package/dist/functions/hybrid-search.js +205 -0
  45. package/dist/functions/index.d.ts +32 -0
  46. package/dist/functions/index.js +44 -0
  47. package/dist/functions/keyed-mutex.d.ts +1 -0
  48. package/dist/functions/keyed-mutex.js +21 -0
  49. package/dist/functions/logger.d.ts +6 -0
  50. package/dist/functions/logger.js +37 -0
  51. package/dist/functions/memory-utils.d.ts +2 -0
  52. package/dist/functions/memory-utils.js +29 -0
  53. package/dist/functions/observe.d.ts +5 -0
  54. package/dist/functions/observe.js +326 -0
  55. package/dist/functions/paths.d.ts +1 -0
  56. package/dist/functions/paths.js +38 -0
  57. package/dist/functions/privacy.d.ts +1 -0
  58. package/dist/functions/privacy.js +30 -0
  59. package/dist/functions/provenance.d.ts +9 -0
  60. package/dist/functions/provenance.js +57 -0
  61. package/dist/functions/quantized-vector-index.d.ts +60 -0
  62. package/dist/functions/quantized-vector-index.js +275 -0
  63. package/dist/functions/receipt.d.ts +31 -0
  64. package/dist/functions/receipt.js +95 -0
  65. package/dist/functions/search-index.d.ts +27 -0
  66. package/dist/functions/search-index.js +217 -0
  67. package/dist/functions/search.d.ts +25 -0
  68. package/dist/functions/search.js +523 -0
  69. package/dist/functions/stemmer.d.ts +1 -0
  70. package/dist/functions/stemmer.js +110 -0
  71. package/dist/functions/synonyms.d.ts +1 -0
  72. package/dist/functions/synonyms.js +69 -0
  73. package/dist/functions/turboquant.d.ts +53 -0
  74. package/dist/functions/turboquant.js +278 -0
  75. package/dist/functions/types.d.ts +217 -0
  76. package/dist/functions/types.js +8 -0
  77. package/dist/functions/vector-index.d.ts +25 -0
  78. package/dist/functions/vector-index.js +125 -0
  79. package/dist/functions/vector-persistence.d.ts +14 -0
  80. package/dist/functions/vector-persistence.js +75 -0
  81. package/dist/functions/verify.d.ts +13 -0
  82. package/dist/functions/verify.js +104 -0
  83. package/dist/index.d.ts +1 -0
  84. package/dist/index.js +219 -0
  85. package/dist/kernel/http.d.ts +24 -0
  86. package/dist/kernel/http.js +261 -0
  87. package/dist/kernel/index.d.ts +19 -0
  88. package/dist/kernel/index.js +21 -0
  89. package/dist/kernel/kernel.d.ts +80 -0
  90. package/dist/kernel/kernel.js +297 -0
  91. package/dist/kernel/pubsub.d.ts +21 -0
  92. package/dist/kernel/pubsub.js +38 -0
  93. package/dist/kernel/types.d.ts +139 -0
  94. package/dist/kernel/types.js +20 -0
  95. package/dist/mcp/bin.d.ts +2 -0
  96. package/dist/mcp/bin.js +27 -0
  97. package/dist/mcp/server.d.ts +34 -0
  98. package/dist/mcp/server.js +377 -0
  99. package/dist/observability/metrics.d.ts +26 -0
  100. package/dist/observability/metrics.js +104 -0
  101. package/dist/proxy/server.d.ts +30 -0
  102. package/dist/proxy/server.js +331 -0
  103. package/dist/state/kv.d.ts +41 -0
  104. package/dist/state/kv.js +50 -0
  105. package/dist/state/oplog.d.ts +25 -0
  106. package/dist/state/oplog.js +57 -0
  107. package/dist/state/schema.d.ts +60 -0
  108. package/dist/state/schema.js +88 -0
  109. package/dist/state/store-libsql.d.ts +46 -0
  110. package/dist/state/store-libsql.js +263 -0
  111. package/dist/state/store-memory.d.ts +23 -0
  112. package/dist/state/store-memory.js +121 -0
  113. package/dist/state/store.d.ts +87 -0
  114. package/dist/state/store.js +58 -0
  115. package/dist/triggers/api.d.ts +14 -0
  116. package/dist/triggers/api.js +510 -0
  117. package/dist/triggers/auth.d.ts +1 -0
  118. package/dist/triggers/auth.js +13 -0
  119. package/package.json +58 -0
@@ -0,0 +1,69 @@
1
+ //
2
+ // Query-time synonym expansion for BM25. Each group below is a set of common
3
+ // developer terms that should match one another (abbreviation, full word,
4
+ // inflections). Groups are stemmed once at load so lookups key off the same
5
+ // stems the tokenizer emits; the BM25 scorer weights expanded hits below
6
+ // exact ones.
7
+ import { stem } from "./stemmer.js";
8
+ // Factual term associations (dev abbreviations and their expansions).
9
+ const GROUPS = [
10
+ ["auth", "authentication", "authn", "authenticating"],
11
+ ["authz", "authorization", "authorizing"],
12
+ ["db", "database", "datastore"],
13
+ ["perf", "performance", "latency", "throughput", "slow", "bottleneck"],
14
+ ["optim", "optimization", "optimizing", "optimise", "query-optimization"],
15
+ ["k8s", "kubernetes", "kube"],
16
+ ["config", "configuration", "configuring", "setup"],
17
+ ["deps", "dependencies", "dependency"],
18
+ ["env", "environment"],
19
+ ["fn", "function"],
20
+ ["impl", "implementation", "implementing"],
21
+ ["msg", "message", "messaging"],
22
+ ["repo", "repository"],
23
+ ["req", "request"],
24
+ ["res", "response"],
25
+ ["ts", "typescript"],
26
+ ["js", "javascript"],
27
+ ["pg", "postgres", "postgresql"],
28
+ ["err", "error", "errors"],
29
+ ["api", "endpoint", "endpoints"],
30
+ ["ci", "continuous-integration"],
31
+ ["cd", "continuous-deployment"],
32
+ ["test", "testing", "tests"],
33
+ ["doc", "documentation", "docs"],
34
+ ["infra", "infrastructure"],
35
+ ["deploy", "deployment", "deploying"],
36
+ ["cache", "caching", "cached"],
37
+ ["log", "logging", "logs"],
38
+ ["monitor", "monitoring"],
39
+ ["observe", "observability"],
40
+ ["sec", "security", "secure"],
41
+ ["validate", "validation", "validating"],
42
+ ["migrate", "migration", "migrations"],
43
+ ["debug", "debugging"],
44
+ ["container", "containerization", "docker"],
45
+ ["crash", "crashloop", "crashloopbackoff"],
46
+ ["webhook", "webhooks", "callback"],
47
+ ["middleware", "mw"],
48
+ ["paginate", "pagination"],
49
+ ["serialize", "serialization"],
50
+ ["encrypt", "encryption"],
51
+ ["hash", "hashing"],
52
+ ];
53
+ // stem -> the other stems in its group
54
+ const byStem = new Map();
55
+ for (const group of GROUPS) {
56
+ const stems = [...new Set(group.map((term) => stem(term.toLowerCase())))];
57
+ for (const s of stems) {
58
+ let bucket = byStem.get(s);
59
+ if (!bucket)
60
+ byStem.set(s, (bucket = new Set()));
61
+ for (const other of stems)
62
+ if (other !== s)
63
+ bucket.add(other);
64
+ }
65
+ }
66
+ export function getSynonyms(stemmedTerm) {
67
+ const bucket = byStem.get(stemmedTerm);
68
+ return bucket ? [...bucket] : [];
69
+ }
@@ -0,0 +1,53 @@
1
+ export declare const TURBOQUANT_VERSION = 1;
2
+ export declare const ROTATION_ROUNDS = 3;
3
+ export type QuantBits = 2 | 4;
4
+ export declare function nextPow2(n: number): number;
5
+ export declare function seedFromString(seed: string): number;
6
+ export declare function mulberry32(seed: number): () => number;
7
+ /**
8
+ * One Int8Array of ±1 per rotation round, length D each. Generated by
9
+ * draining the PRNG in a fixed order, so (seed, D, rounds) fully determines
10
+ * the rotation.
11
+ */
12
+ export declare function buildSignFlips(prng: () => number, D: number, rounds: number): Int8Array[];
13
+ export declare function fwht(buf: Float32Array): void;
14
+ /**
15
+ * Rotate `src` (length d <= D) into a length-D buffer: zero-pad, then
16
+ * `signFlips.length` rounds of (sign flip, FWHT, 1/sqrt(D) rescale).
17
+ * Norm-preserving up to f32 rounding. Non-finite inputs are sanitized to 0.
18
+ * Pass `scratch` (length D) to avoid per-call allocation.
19
+ */
20
+ export declare function rotate(src: Float32Array, D: number, signFlips: Int8Array[], scratch?: Float32Array): Float32Array;
21
+ export interface LevelTable {
22
+ levels: Float32Array;
23
+ boundaries: Float32Array;
24
+ }
25
+ /**
26
+ * Lloyd-Max iteration for N(0,1): boundaries are midpoints of adjacent
27
+ * levels; each level is the conditional mean of the cell. Converges in a
28
+ * few dozen iterations; deterministic, so the table never needs persisting
29
+ * (a short hash travels in index params to invalidate on algorithm change).
30
+ */
31
+ export declare function lloydMaxLevels(bits: QuantBits): LevelTable;
32
+ export declare function levelTableHash(bits: QuantBits): string;
33
+ export declare function packCodes(codeIndices: Uint8Array, bits: QuantBits): Uint8Array;
34
+ export declare function unpackCodes(packed: Uint8Array, D: number, bits: QuantBits): Uint8Array;
35
+ /**
36
+ * Quantize an already-rotated length-D buffer. Returns the packed codes and
37
+ * the original vector norm (rotation is norm-preserving, so this is also
38
+ * the pre-rotation norm). A zero/degenerate vector encodes with norm 0 and
39
+ * the code of the level nearest zero.
40
+ */
41
+ export declare function encodeRotated(rotated: Float32Array, D: number, bits: QuantBits): {
42
+ codes: Uint8Array;
43
+ norm: number;
44
+ };
45
+ /**
46
+ * Asymmetric partial inner product: Σ rotatedQuery[i] * LEVELS[code_i].
47
+ * The query stays full precision (rotated once per search); codes are read
48
+ * nibble/crumb-wise without unpacking allocations. f64 accumulation.
49
+ *
50
+ * Cosine estimate for a stored vector v: asymmetricDot / (sqrt(D) * |q|),
51
+ * because stored codes approximate sqrt(D) * v_i / ||v||.
52
+ */
53
+ export declare function asymmetricDot(rotatedQuery: Float32Array, codes: Uint8Array, D: number, bits: QuantBits, table?: LevelTable): number;
@@ -0,0 +1,278 @@
1
+ //
2
+ // TurboQuant-style data-oblivious vector quantization. Pure math, no I/O.
3
+ //
4
+ // Implements the core of "TurboQuant: Online Vector Quantization with
5
+ // Near-optimal Distortion Rate" (Google Research, arXiv:2504.19874, ICLR
6
+ // 2026): randomly rotate input vectors so coordinates become
7
+ // near-independent with a known concentrated distribution, then apply a
8
+ // FIXED precomputed optimal (Lloyd-Max) scalar quantizer per coordinate.
9
+ // No codebook training, no data-dependent state: codes are computable on
10
+ // the first add(). The MIT-licensed Rust reference is
11
+ // https://github.com/RyanCodrai/turbovec.
12
+ //
13
+ // Approximation note: after `ROTATION_ROUNDS` randomized Hadamard rounds,
14
+ // the coordinates of a unit vector are treated as i.i.d. N(0, 1/D). This is
15
+ // exact in the D→infinity limit and close for D >= 128. Real embeddings are
16
+ // anisotropic; the rotation Gaussianizes them and the optional rescore pass
17
+ // (see quantized-vector-index.ts) absorbs the residual error.
18
+ //
19
+ // We quantize the scaled coordinate sqrt(D) * x_i / ||x||, whose reference
20
+ // distribution is N(0, 1), so a single dimension-independent level table
21
+ // serves every index.
22
+ import { createHash } from "node:crypto";
23
+ export const TURBOQUANT_VERSION = 1;
24
+ export const ROTATION_ROUNDS = 3;
25
+ export function nextPow2(n) {
26
+ if (n <= 1)
27
+ return 1;
28
+ return 2 ** Math.ceil(Math.log2(n));
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Deterministic PRNG. The rotation must be reconstructible from params alone
32
+ // across processes, so Math.random is banned. mulberry32 is a tiny 32-bit
33
+ // generator with stable output for a given seed.
34
+ export function seedFromString(seed) {
35
+ // FNV-1a over UTF-16 code units, folded to uint32.
36
+ let h = 0x811c9dc5;
37
+ for (let i = 0; i < seed.length; i++) {
38
+ h ^= seed.charCodeAt(i);
39
+ h = Math.imul(h, 0x01000193);
40
+ }
41
+ return h >>> 0;
42
+ }
43
+ export function mulberry32(seed) {
44
+ let a = seed >>> 0;
45
+ return () => {
46
+ a = (a + 0x6d2b79f5) | 0;
47
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
48
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
49
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
50
+ };
51
+ }
52
+ /**
53
+ * One Int8Array of ±1 per rotation round, length D each. Generated by
54
+ * draining the PRNG in a fixed order, so (seed, D, rounds) fully determines
55
+ * the rotation.
56
+ */
57
+ export function buildSignFlips(prng, D, rounds) {
58
+ const flips = [];
59
+ for (let r = 0; r < rounds; r++) {
60
+ const s = new Int8Array(D);
61
+ for (let i = 0; i < D; i++) {
62
+ s[i] = prng() < 0.5 ? -1 : 1;
63
+ }
64
+ flips.push(s);
65
+ }
66
+ return flips;
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Fast Walsh-Hadamard Transform, in place, unnormalized. Callers rescale by
70
+ // 1/sqrt(D) per application to keep the transform orthonormal.
71
+ export function fwht(buf) {
72
+ const n = buf.length;
73
+ for (let len = 1; len < n; len <<= 1) {
74
+ for (let i = 0; i < n; i += len << 1) {
75
+ for (let j = i; j < i + len; j++) {
76
+ const a = buf[j];
77
+ const b = buf[j + len];
78
+ buf[j] = a + b;
79
+ buf[j + len] = a - b;
80
+ }
81
+ }
82
+ }
83
+ }
84
+ /**
85
+ * Rotate `src` (length d <= D) into a length-D buffer: zero-pad, then
86
+ * `signFlips.length` rounds of (sign flip, FWHT, 1/sqrt(D) rescale).
87
+ * Norm-preserving up to f32 rounding. Non-finite inputs are sanitized to 0.
88
+ * Pass `scratch` (length D) to avoid per-call allocation.
89
+ */
90
+ export function rotate(src, D, signFlips, scratch) {
91
+ const out = scratch && scratch.length === D ? scratch : new Float32Array(D);
92
+ for (let i = 0; i < src.length; i++) {
93
+ const v = src[i];
94
+ out[i] = Number.isFinite(v) ? v : 0;
95
+ }
96
+ out.fill(0, src.length);
97
+ const invSqrtD = 1 / Math.sqrt(D);
98
+ for (const flips of signFlips) {
99
+ for (let i = 0; i < D; i++) {
100
+ out[i] = out[i] * flips[i];
101
+ }
102
+ fwht(out);
103
+ for (let i = 0; i < D; i++) {
104
+ out[i] = out[i] * invSqrtD;
105
+ }
106
+ }
107
+ return out;
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Lloyd-Max scalar quantizer for the standard normal, computed analytically
111
+ // at init (no data involved) and cached per bit width.
112
+ function normalPdf(x) {
113
+ return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI);
114
+ }
115
+ // Abramowitz & Stegun 7.1.26 rational erf approximation (~1e-7 abs error).
116
+ function erf(z) {
117
+ const sign = z < 0 ? -1 : 1;
118
+ const x = Math.abs(z);
119
+ const t = 1 / (1 + 0.3275911 * x);
120
+ const y = 1 -
121
+ (((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t -
122
+ 0.284496736) *
123
+ t +
124
+ 0.254829592) *
125
+ t) *
126
+ Math.exp(-x * x);
127
+ return sign * y;
128
+ }
129
+ function normalCdf(x) {
130
+ return 0.5 * (1 + erf(x / Math.SQRT2));
131
+ }
132
+ const levelTableCache = new Map();
133
+ /**
134
+ * Lloyd-Max iteration for N(0,1): boundaries are midpoints of adjacent
135
+ * levels; each level is the conditional mean of the cell. Converges in a
136
+ * few dozen iterations; deterministic, so the table never needs persisting
137
+ * (a short hash travels in index params to invalidate on algorithm change).
138
+ */
139
+ export function lloydMaxLevels(bits) {
140
+ const cached = levelTableCache.get(bits);
141
+ if (cached)
142
+ return cached;
143
+ const L = 2 ** bits;
144
+ const INF = 8; // φ(8) ≈ 5e-15: numerically infinite for our purposes
145
+ const levels = new Float64Array(L);
146
+ for (let k = 0; k < L; k++) {
147
+ levels[k] = -3 + (6 * (k + 0.5)) / L;
148
+ }
149
+ const bounds = new Float64Array(L - 1);
150
+ for (let iter = 0; iter < 200; iter++) {
151
+ for (let k = 0; k < L - 1; k++) {
152
+ bounds[k] = (levels[k] + levels[k + 1]) / 2;
153
+ }
154
+ let moved = 0;
155
+ for (let k = 0; k < L; k++) {
156
+ const a = k === 0 ? -INF : bounds[k - 1];
157
+ const b = k === L - 1 ? INF : bounds[k];
158
+ const mass = normalCdf(b) - normalCdf(a);
159
+ if (mass <= 0)
160
+ continue;
161
+ const centroid = (normalPdf(a) - normalPdf(b)) / mass;
162
+ moved = Math.max(moved, Math.abs(centroid - levels[k]));
163
+ levels[k] = centroid;
164
+ }
165
+ if (moved < 1e-10)
166
+ break;
167
+ }
168
+ const table = {
169
+ levels: Float32Array.from(levels),
170
+ boundaries: Float32Array.from(bounds),
171
+ };
172
+ levelTableCache.set(bits, table);
173
+ return table;
174
+ }
175
+ export function levelTableHash(bits) {
176
+ const { levels } = lloydMaxLevels(bits);
177
+ const repr = Array.from(levels, (v) => v.toFixed(6)).join(",");
178
+ return createHash("sha256").update(repr).digest("hex").slice(0, 16);
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // Code packing. 4-bit: two codes per byte (even index in the low nibble);
182
+ // 2-bit: four codes per byte (index i in bits 2*(i % 4)).
183
+ export function packCodes(codeIndices, bits) {
184
+ const D = codeIndices.length;
185
+ if (bits === 4) {
186
+ const out = new Uint8Array(Math.ceil(D / 2));
187
+ for (let i = 0; i < D; i++) {
188
+ const c = codeIndices[i] & 0xf;
189
+ if (i % 2 === 0)
190
+ out[i >> 1] = c;
191
+ else
192
+ out[i >> 1] = out[i >> 1] | (c << 4);
193
+ }
194
+ return out;
195
+ }
196
+ const out = new Uint8Array(Math.ceil(D / 4));
197
+ for (let i = 0; i < D; i++) {
198
+ const c = codeIndices[i] & 0x3;
199
+ out[i >> 2] = out[i >> 2] | (c << ((i % 4) * 2));
200
+ }
201
+ return out;
202
+ }
203
+ export function unpackCodes(packed, D, bits) {
204
+ const out = new Uint8Array(D);
205
+ if (bits === 4) {
206
+ for (let i = 0; i < D; i++) {
207
+ const byte = packed[i >> 1];
208
+ out[i] = i % 2 === 0 ? byte & 0xf : (byte >> 4) & 0xf;
209
+ }
210
+ return out;
211
+ }
212
+ for (let i = 0; i < D; i++) {
213
+ const byte = packed[i >> 2];
214
+ out[i] = (byte >> ((i % 4) * 2)) & 0x3;
215
+ }
216
+ return out;
217
+ }
218
+ // ---------------------------------------------------------------------------
219
+ // Encode / score.
220
+ /**
221
+ * Quantize an already-rotated length-D buffer. Returns the packed codes and
222
+ * the original vector norm (rotation is norm-preserving, so this is also
223
+ * the pre-rotation norm). A zero/degenerate vector encodes with norm 0 and
224
+ * the code of the level nearest zero.
225
+ */
226
+ export function encodeRotated(rotated, D, bits) {
227
+ const { boundaries } = lloydMaxLevels(bits);
228
+ let normSq = 0;
229
+ for (let i = 0; i < D; i++) {
230
+ const v = rotated[i];
231
+ normSq += v * v;
232
+ }
233
+ const norm = Math.sqrt(normSq);
234
+ const scale = norm === 0 ? 0 : Math.sqrt(D) / norm;
235
+ const nB = boundaries.length;
236
+ const idx = new Uint8Array(D);
237
+ for (let i = 0; i < D; i++) {
238
+ const z = rotated[i] * scale;
239
+ // Binary search: count of boundaries < z is the code.
240
+ let lo = 0;
241
+ let hi = nB;
242
+ while (lo < hi) {
243
+ const mid = (lo + hi) >> 1;
244
+ if (boundaries[mid] < z)
245
+ lo = mid + 1;
246
+ else
247
+ hi = mid;
248
+ }
249
+ idx[i] = lo;
250
+ }
251
+ return { codes: packCodes(idx, bits), norm };
252
+ }
253
+ /**
254
+ * Asymmetric partial inner product: Σ rotatedQuery[i] * LEVELS[code_i].
255
+ * The query stays full precision (rotated once per search); codes are read
256
+ * nibble/crumb-wise without unpacking allocations. f64 accumulation.
257
+ *
258
+ * Cosine estimate for a stored vector v: asymmetricDot / (sqrt(D) * |q|),
259
+ * because stored codes approximate sqrt(D) * v_i / ||v||.
260
+ */
261
+ export function asymmetricDot(rotatedQuery, codes, D, bits, table) {
262
+ const { levels } = table ?? lloydMaxLevels(bits);
263
+ let sum = 0;
264
+ if (bits === 4) {
265
+ for (let i = 0; i < D; i++) {
266
+ const byte = codes[i >> 1];
267
+ const code = i % 2 === 0 ? byte & 0xf : (byte >> 4) & 0xf;
268
+ sum += rotatedQuery[i] * levels[code];
269
+ }
270
+ return sum;
271
+ }
272
+ for (let i = 0; i < D; i++) {
273
+ const byte = codes[i >> 2];
274
+ const code = (byte >> ((i % 4) * 2)) & 0x3;
275
+ sum += rotatedQuery[i] * levels[code];
276
+ }
277
+ return sum;
278
+ }
@@ -0,0 +1,217 @@
1
+ export type HookType = "session_start" | "prompt_submit" | "pre_tool_use" | "post_tool_use" | "post_tool_failure" | "pre_compact" | "subagent_start" | "subagent_stop" | "notification" | "task_completed" | "stop" | "session_end";
2
+ export interface HookPayload {
3
+ hookType: HookType;
4
+ sessionId: string;
5
+ project: string;
6
+ cwd: string;
7
+ timestamp: string;
8
+ data: unknown;
9
+ /** Which agent captured this (claude, codex, cursor, …). Optional. */
10
+ agent?: string;
11
+ }
12
+ export interface Session {
13
+ id: string;
14
+ project: string;
15
+ cwd: string;
16
+ startedAt: string;
17
+ endedAt?: string;
18
+ status: "active" | "completed" | "abandoned";
19
+ observationCount: number;
20
+ model?: string;
21
+ tags?: string[];
22
+ firstPrompt?: string;
23
+ summary?: string;
24
+ commitShas?: string[];
25
+ agentId?: string;
26
+ }
27
+ export interface RawObservation {
28
+ id: string;
29
+ sessionId: string;
30
+ timestamp: string;
31
+ hookType: HookType;
32
+ toolName?: string;
33
+ toolInput?: unknown;
34
+ toolOutput?: unknown;
35
+ userPrompt?: string;
36
+ assistantResponse?: string;
37
+ raw: unknown;
38
+ modality?: "text" | "image" | "mixed";
39
+ imageData?: string;
40
+ agentId?: string;
41
+ }
42
+ export type ObservationType = "file_read" | "file_write" | "file_edit" | "command_run" | "search" | "web_fetch" | "conversation" | "error" | "decision" | "discovery" | "subagent" | "notification" | "task" | "image" | "other";
43
+ /**
44
+ * Where a memory came from — the evidence the doctor audits. A memory with
45
+ * provenance can be checked for staleness (do its files still exist?) and
46
+ * sourcing (is there any evidence at all?); one without is "unsourced".
47
+ */
48
+ export interface Provenance {
49
+ cwd?: string;
50
+ files?: string[];
51
+ fileHashes?: Record<string, string>;
52
+ command?: string;
53
+ agent?: string;
54
+ capturedAt?: string;
55
+ userConfirmed?: boolean;
56
+ }
57
+ export interface CompressedObservation {
58
+ id: string;
59
+ sessionId: string;
60
+ timestamp: string;
61
+ type: ObservationType;
62
+ title: string;
63
+ subtitle?: string;
64
+ facts: string[];
65
+ narrative: string;
66
+ concepts: string[];
67
+ files: string[];
68
+ importance: number;
69
+ confidence?: number;
70
+ imageRef?: string;
71
+ imageData?: string;
72
+ imageDescription?: string;
73
+ modality?: "text" | "image" | "mixed";
74
+ agentId?: string;
75
+ provenance?: Provenance;
76
+ }
77
+ export interface Memory {
78
+ id: string;
79
+ createdAt: string;
80
+ updatedAt: string;
81
+ type: "pattern" | "preference" | "architecture" | "bug" | "workflow" | "fact";
82
+ title: string;
83
+ content: string;
84
+ concepts: string[];
85
+ files: string[];
86
+ sessionIds: string[];
87
+ strength: number;
88
+ version: number;
89
+ parentId?: string;
90
+ supersedes?: string[];
91
+ relatedIds?: string[];
92
+ sourceObservationIds?: string[];
93
+ isLatest: boolean;
94
+ forgetAfter?: string;
95
+ imageRef?: string;
96
+ imageData?: string;
97
+ agentId?: string;
98
+ project?: string;
99
+ provenance?: Provenance;
100
+ }
101
+ export interface SessionSummary {
102
+ sessionId: string;
103
+ project: string;
104
+ createdAt: string;
105
+ title: string;
106
+ narrative: string;
107
+ keyDecisions: string[];
108
+ filesModified: string[];
109
+ concepts: string[];
110
+ observationCount: number;
111
+ }
112
+ export interface ProjectProfile {
113
+ project: string;
114
+ updatedAt: string;
115
+ topConcepts: Array<{
116
+ concept: string;
117
+ frequency: number;
118
+ }>;
119
+ topFiles: Array<{
120
+ file: string;
121
+ frequency: number;
122
+ }>;
123
+ conventions: string[];
124
+ commonErrors: string[];
125
+ recentActivity: string[];
126
+ sessionCount: number;
127
+ totalObservations: number;
128
+ summary?: string;
129
+ }
130
+ export interface ContextBlock {
131
+ type: "summary" | "observation" | "memory";
132
+ content: string;
133
+ tokens: number;
134
+ recency: number;
135
+ sourceIds?: string[];
136
+ }
137
+ export interface SearchResult {
138
+ observation: CompressedObservation;
139
+ score: number;
140
+ sessionId: string;
141
+ }
142
+ export interface CompactSearchResult {
143
+ obsId: string;
144
+ sessionId: string;
145
+ title: string;
146
+ type: ObservationType;
147
+ score: number;
148
+ timestamp: string;
149
+ }
150
+ export interface Lesson {
151
+ id: string;
152
+ content: string;
153
+ context: string;
154
+ confidence: number;
155
+ reinforcements: number;
156
+ source: "crystal" | "manual" | "consolidation";
157
+ sourceIds: string[];
158
+ project?: string;
159
+ tags: string[];
160
+ createdAt: string;
161
+ updatedAt: string;
162
+ lastReinforcedAt?: string;
163
+ lastDecayedAt?: string;
164
+ decayRate: number;
165
+ deleted?: boolean;
166
+ }
167
+ /**
168
+ * Embedding provider abstraction. The the core vector stream is stubbed to
169
+ * empty (no provider wired), so this only needs to exist for the
170
+ * VectorIndex / hybrid-fusion plumbing to typecheck; an actual provider
171
+ * lands in a later phase.
172
+ */
173
+ export interface EmbeddingProvider {
174
+ name: string;
175
+ dimensions: number;
176
+ embed(text: string): Promise<Float32Array>;
177
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
178
+ embedImage?(src: string): Promise<Float32Array>;
179
+ }
180
+ /** A single vector-stream hit, shared by every vector index implementation. */
181
+ export interface VectorSearchHit {
182
+ obsId: string;
183
+ sessionId: string;
184
+ score: number;
185
+ }
186
+ /**
187
+ * The vector-index surface consumed by search.ts and hybrid-search.ts.
188
+ * Satisfied structurally by both VectorIndex (full-precision, the default)
189
+ * and QuantizedVectorIndex (TurboQuant codes, behind MEMWARDEN_QUANT_VECTOR).
190
+ */
191
+ export interface VectorIndexLike {
192
+ add(obsId: string, sessionId: string, embedding: Float32Array): void;
193
+ remove(obsId: string): void;
194
+ has(obsId: string): boolean;
195
+ /** Snapshot of the stored obsIds; used for restore reconciliation. */
196
+ ids(): string[];
197
+ search(query: Float32Array, limit?: number): VectorSearchHit[];
198
+ readonly size: number;
199
+ clear(): void;
200
+ validateDimensions(expected: number): {
201
+ mismatches: Array<{
202
+ obsId: string;
203
+ dim: number;
204
+ }>;
205
+ seenDimensions: Set<number>;
206
+ };
207
+ serialize(): string;
208
+ }
209
+ export interface HybridSearchResult {
210
+ observation: CompressedObservation;
211
+ bm25Score: number;
212
+ vectorScore: number;
213
+ graphScore: number;
214
+ combinedScore: number;
215
+ sessionId: string;
216
+ graphContext?: string;
217
+ }
@@ -0,0 +1,8 @@
1
+ //
2
+ // The subset of the shared data model that the wired core functions
3
+ // (observe / context / search) and their supporting modules depend on.
4
+ // Trimmed to only the shapes the core surface touches; the wire shapes
5
+ // (HookPayload, RawObservation, CompressedObservation, Session, ...) are
6
+ // kept the stable wire contract so existing connectors can
7
+ // talk to memwarden unchanged.
8
+ export {};
@@ -0,0 +1,25 @@
1
+ export interface VectorHit {
2
+ obsId: string;
3
+ sessionId: string;
4
+ score: number;
5
+ }
6
+ export declare class VectorIndex {
7
+ private vectors;
8
+ add(obsId: string, sessionId: string, embedding: Float32Array): void;
9
+ remove(obsId: string): void;
10
+ has(obsId: string): boolean;
11
+ ids(): string[];
12
+ get size(): number;
13
+ search(query: Float32Array, limit?: number): VectorHit[];
14
+ validateDimensions(expected: number): {
15
+ mismatches: Array<{
16
+ obsId: string;
17
+ dim: number;
18
+ }>;
19
+ seenDimensions: Set<number>;
20
+ };
21
+ clear(): void;
22
+ restoreFrom(other: VectorIndex): void;
23
+ serialize(): string;
24
+ static deserialize(json: string): VectorIndex;
25
+ }