@soulcraft/cor 3.0.0-rc.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 (87) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +328 -0
  3. package/dist/aggregation/NativeAggregationEngine.d.ts +118 -0
  4. package/dist/aggregation/NativeAggregationEngine.js +186 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +236 -0
  7. package/dist/graph/GraphAccelerationAdapter.d.ts +153 -0
  8. package/dist/graph/GraphAccelerationAdapter.js +326 -0
  9. package/dist/graph/NativeGraphAdjacencyIndex.d.ts +286 -0
  10. package/dist/graph/NativeGraphAdjacencyIndex.js +944 -0
  11. package/dist/hnsw/AdaptiveDiskAnnModeSelector.d.ts +187 -0
  12. package/dist/hnsw/AdaptiveDiskAnnModeSelector.js +313 -0
  13. package/dist/hnsw/NativeDiskAnnWrapper.d.ts +489 -0
  14. package/dist/hnsw/NativeDiskAnnWrapper.js +1456 -0
  15. package/dist/index.d.ts +28 -0
  16. package/dist/index.js +29 -0
  17. package/dist/legacyLayoutGuard.d.ts +93 -0
  18. package/dist/legacyLayoutGuard.js +237 -0
  19. package/dist/license/constants.d.ts +30 -0
  20. package/dist/license/constants.js +30 -0
  21. package/dist/license/onlineValidator.d.ts +43 -0
  22. package/dist/license/onlineValidator.js +225 -0
  23. package/dist/license/types.d.ts +39 -0
  24. package/dist/license/types.js +5 -0
  25. package/dist/license.d.ts +64 -0
  26. package/dist/license.js +165 -0
  27. package/dist/migration/MigrationCoordinator.d.ts +171 -0
  28. package/dist/migration/MigrationCoordinator.js +249 -0
  29. package/dist/migration/indexEpochGuard.d.ts +163 -0
  30. package/dist/migration/indexEpochGuard.js +202 -0
  31. package/dist/native/NativeEmbeddingEngine.d.ts +79 -0
  32. package/dist/native/NativeEmbeddingEngine.js +318 -0
  33. package/dist/native/NativeRoaringBitmap32.d.ts +114 -0
  34. package/dist/native/NativeRoaringBitmap32.js +221 -0
  35. package/dist/native/ffi.d.ts +20 -0
  36. package/dist/native/ffi.js +48 -0
  37. package/dist/native/idMapperTestSupport.d.ts +67 -0
  38. package/dist/native/idMapperTestSupport.js +112 -0
  39. package/dist/native/index.d.ts +49 -0
  40. package/dist/native/index.js +87 -0
  41. package/dist/native/napi.d.ts +21 -0
  42. package/dist/native/napi.js +88 -0
  43. package/dist/native/types.d.ts +1298 -0
  44. package/dist/native/types.js +16 -0
  45. package/dist/plugin.d.ts +50 -0
  46. package/dist/plugin.js +388 -0
  47. package/dist/providerContracts.d.ts +277 -0
  48. package/dist/providerContracts.js +38 -0
  49. package/dist/resource/OsMemoryProbe.d.ts +175 -0
  50. package/dist/resource/OsMemoryProbe.js +206 -0
  51. package/dist/resource/ResourceManager.d.ts +491 -0
  52. package/dist/resource/ResourceManager.js +960 -0
  53. package/dist/utils/ColumnManifest.d.ts +97 -0
  54. package/dist/utils/ColumnManifest.js +129 -0
  55. package/dist/utils/NativeColumnStore.d.ts +284 -0
  56. package/dist/utils/NativeColumnStore.js +685 -0
  57. package/dist/utils/NativeMetadataIndex.d.ts +882 -0
  58. package/dist/utils/NativeMetadataIndex.js +2631 -0
  59. package/dist/utils/NativeUnifiedCache.d.ts +87 -0
  60. package/dist/utils/NativeUnifiedCache.js +273 -0
  61. package/dist/utils/binaryIdMapperFactory.d.ts +59 -0
  62. package/dist/utils/binaryIdMapperFactory.js +94 -0
  63. package/dist/utils/collation.d.ts +30 -0
  64. package/dist/utils/collation.js +40 -0
  65. package/dist/utils/columnStoreTypes.d.ts +161 -0
  66. package/dist/utils/columnStoreTypes.js +29 -0
  67. package/dist/utils/nativeBinaryEntityIdMapper.d.ts +211 -0
  68. package/dist/utils/nativeBinaryEntityIdMapper.js +381 -0
  69. package/dist/utils/nativeEntityIdMapper.d.ts +111 -0
  70. package/dist/utils/nativeEntityIdMapper.js +170 -0
  71. package/docs/ADR-002-diskann-100-percent-rust.md +294 -0
  72. package/docs/ADR-003-semantic-time-travel.md +100 -0
  73. package/docs/aggregation.md +251 -0
  74. package/docs/comparison.md +162 -0
  75. package/docs/deployment-limits.md +87 -0
  76. package/docs/diskann.md +184 -0
  77. package/docs/migration-3.0.md +396 -0
  78. package/docs/performance-budget.md +274 -0
  79. package/docs/performance.md +117 -0
  80. package/docs/scaling.md +439 -0
  81. package/docs/snapshot-safety.md +246 -0
  82. package/docs/u64-id-space.md +214 -0
  83. package/docs/verification-report.md +670 -0
  84. package/native/brainy-native.node +0 -0
  85. package/native/index.d.ts +3916 -0
  86. package/package.json +92 -0
  87. package/scripts/migrate-cortex-2x-to-3x.mjs +970 -0
@@ -0,0 +1,489 @@
1
+ /**
2
+ * @module hnsw/NativeDiskAnnWrapper
3
+ * @description TypeScript wrapper around cor's native **Adaptive
4
+ * DiskANN** engine. Satisfies brainy 8.0's `VectorIndexProvider`
5
+ * contract (see [`providerContracts.ts`](../providerContracts.ts))
6
+ * so brainy treats it as the standard vector index slot —
7
+ * `addItem` / `search` / `rebuild` / `flush` / ... Underneath,
8
+ * every operation routes through cor's pure-Rust DiskANN engine
9
+ * (`native/diskann/`).
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { Brainy } from '@soulcraft/brainy'
14
+ *
15
+ * // Cor is auto-detected by brainy's plugin system on init.
16
+ * const brain = new Brainy({
17
+ * storage: { type: 'filesystem', path: '/data/idx' }
18
+ * })
19
+ * await brain.init() // [cor] Adaptive DiskANN engaged (mode auto)
20
+ *
21
+ * await brain.add({ data: 'native rust acceleration', type: 'concept' })
22
+ * const hits = await brain.search('billion scale ann', 10)
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Default zero-config — the adaptive selector picks Mode 1 / 2 / 3
28
+ * // from observed memory pressure. Most users only set `recall`.
29
+ * const brain = new Brainy({
30
+ * storage: { type: 'filesystem', path: '/data/idx' },
31
+ * vector: {
32
+ * recall: 'balanced', // 'fast' | 'balanced' | 'accurate'
33
+ * }
34
+ * })
35
+ * ```
36
+ *
37
+ * ## Operating model
38
+ *
39
+ * Adaptive DiskANN is build-once, query-many: the on-disk file
40
+ * embeds the Vamana graph plus — depending on the build mode — an
41
+ * optional PQ codebook + codes section + the full vectors, all in
42
+ * a single contiguous mmap-able layout (see `native/diskann/src/format.rs`).
43
+ * Dynamic insertions go to a small in-memory **delta buffer** that
44
+ * brute-force-searches alongside the main index until the next
45
+ * `rebuild()` folds them in. This matches FreshDiskANN's published
46
+ * online-update model.
47
+ *
48
+ * The wrapper picks an operating mode (in-memory / hybrid /
49
+ * on-disk) at build time and open time via the
50
+ * {@link AdaptiveDiskAnnModeSelector}; see that module for the
51
+ * decision tree. Operators set nothing — observed memory drives
52
+ * the choice.
53
+ *
54
+ * ## Search path
55
+ *
56
+ * 1. Query the main index via the native DiskANN searcher:
57
+ * - **Mode 1**: exact-distance walk over full vectors (no PQ,
58
+ * no rerank — the walk's top-L is the exact top-L).
59
+ * - **Mode 2/3**: PQ-greedy walk in RAM (codes pinned or paged
60
+ * depending on mode), full-vector re-rank of the **whole walked
61
+ * candidate list** by default (`paddingFactor` is an optional cap
62
+ * for bounding cold-NVMe re-rank I/O — see {@link RECALL_PRESETS}).
63
+ * 2. Brute-force the delta buffer (typically <0.1% of total
64
+ * size after a recent rebuild).
65
+ * 3. Merge + sort + truncate to `k`.
66
+ *
67
+ * ## When this wrapper engages
68
+ *
69
+ * The cor plugin (`src/plugin.ts`) registers this wrapper under
70
+ * brainy 8.0's canonical `'vector'` provider key. Whenever cor is
71
+ * installed, brainy's vector index slot resolves to Adaptive DiskANN.
72
+ */
73
+ import type { Vector, VectorDocument, DistanceFunction, StorageAdapter } from '@soulcraft/brainy';
74
+ import type { VectorIndexProvider } from '../providerContracts.js';
75
+ import type { EntityIdMapperLike } from '../utils/columnStoreTypes.js';
76
+ import type { MigrationCoordinator, MigrationStatus } from '../migration/MigrationCoordinator.js';
77
+ import { type AdaptiveDiskAnnMode, type ModeSelection } from './AdaptiveDiskAnnModeSelector.js';
78
+ /**
79
+ * **Public configuration surface** for the Adaptive DiskANN wrapper.
80
+ *
81
+ * The zero-config principle: the wrapper picks every algorithm-level
82
+ * tuning value (PQ subspaces, max degree, search list size, alpha
83
+ * pruning, mmap-vs-RAM adjacency) from observed runtime signals.
84
+ * Operators never tune the algorithm. The only knobs users touch are:
85
+ *
86
+ * 1. `dimensions` — required; can't be auto-detected
87
+ * 2. `recall` — the single quality/latency dial
88
+ * 3. `indexPath` — optional override; derived from storage by default
89
+ * 4. `mode` — explicit escape hatch for benchmarking / pinned-profile
90
+ * deployments. The adaptive selector picks correctly for ~99% of
91
+ * workloads.
92
+ */
93
+ export interface DiskAnnIndexConfig {
94
+ /** Vector dimension (e.g. 384 for all-MiniLM-L6-v2). */
95
+ dimensions: number;
96
+ /**
97
+ * Output path for the on-disk DiskANN file. When omitted, the
98
+ * wrapper derives the path from `options.storage` using the
99
+ * canonical brainy 8.0 convention:
100
+ *
101
+ * `{storage-root}/_system/vector-index/{shard}.dkann`
102
+ *
103
+ * For a single-shard brain, `{shard}` is `main`. Living under
104
+ * `_system/` means brainy's `db.persist(path)` and operator
105
+ * backup tools (`gsutil` / `rclone`) sweep the DiskANN file for
106
+ * free — no special inclusion rule needed. Explicit `indexPath`
107
+ * always wins (sharded deployments that want custom paths).
108
+ */
109
+ indexPath?: string;
110
+ /**
111
+ * **Recall preset** — the single quality/latency dial. Brainy 8.0
112
+ * exposes the same names on its JS HNSW path, so the user-facing
113
+ * trade-off is identical regardless of which vector index runs
114
+ * underneath.
115
+ *
116
+ * - `"fast"` — ~92-95% recall@10; lowest latency.
117
+ * - `"balanced"` (default) — ~96-98% recall@10.
118
+ * - `"accurate"` — ~99%+ recall@10; highest latency.
119
+ */
120
+ recall?: 'fast' | 'balanced' | 'accurate';
121
+ /**
122
+ * **Adaptive DiskANN mode escape hatch** (Piece I). Defaults to
123
+ * `'auto'`, which lets the selector pick `in-memory` / `hybrid` /
124
+ * `on-disk` from observed memory pressure
125
+ * (`/proc/meminfo` + V8 heap + active instances). Set to an
126
+ * explicit mode only when you have a specific reason — benchmarking,
127
+ * a pinned-profile deployment, etc.
128
+ */
129
+ mode?: 'auto' | AdaptiveDiskAnnMode;
130
+ }
131
+ /**
132
+ * @description **Scale-aware `l_search` (E3 zero-config adaptation).** The walk
133
+ * width a fixed default serves degrades with corpus size: recall@10 at
134
+ * `l_search=100` MEASURED 0.9942 @ 1M → 0.9385 @ 10M → **0.8747 @ 100M**
135
+ * (SIFT sweeps, bxl9000 2026-07-01: sift10m-recall + sift100m-nvme runs;
136
+ * summaries in `docs/verification/`). Holding ≥0.95 needed `l_search≈150` at
137
+ * 10M (0.9647) and `≈256` at 100M (0.9559) — i.e. the required width grows
138
+ * ~×1.8 per decade of corpus size. This resolver widens the *default* walk to
139
+ * match: `base` up to 2M nodes (where the measured recall is ≥0.99 already),
140
+ * then `base × 1.8^log10(n/2e6)`, capped at 512 (the latency guard; the cap is
141
+ * also where the 1B PROJECTION ends — 1B ≈ 489 stays a projection until the
142
+ * SIFT1B run measures it).
143
+ *
144
+ * Zero-config contract: this scales the DEFAULT only. An explicit
145
+ * `defaultLSearch` in the construction config pins the width verbatim (the
146
+ * escape hatch), and a `recall` preset scales proportionally from its base
147
+ * (`fast`/`accurate` keep their relative latency/recall intent at every scale).
148
+ *
149
+ * @param base - The preset/base walk width (e.g. 100 for 'balanced').
150
+ * @param nodeCount - Current corpus size (main index + delta).
151
+ * @returns The scale-adjusted walk width, in `[base, 512]` (never below base).
152
+ * @example
153
+ * scaleAwareLSearch(100, 1e6) // 100 (measured 0.9942 — base is ample)
154
+ * scaleAwareLSearch(100, 1e7) // 151 (measured anchor: 150 → 0.9647)
155
+ * scaleAwareLSearch(100, 1e8) // 271 (measured anchor: 256 → 0.9559)
156
+ * scaleAwareLSearch(100, 1e9) // 489 (PROJECTED — SIFT1B measures it)
157
+ */
158
+ export declare function scaleAwareLSearch(base: number, nodeCount: number): number;
159
+ export declare class NativeDiskAnnWrapper implements VectorIndexProvider {
160
+ private config;
161
+ private distanceFunction;
162
+ private storage;
163
+ private persistMode;
164
+ /** Live searcher instance — null until the first build. */
165
+ private native;
166
+ /** Newly added entries since the last build. Brute-force searched. */
167
+ private delta;
168
+ /** Removed entries — filtered out at search time. */
169
+ private tombstones;
170
+ /**
171
+ * **#72 Phase A+B — lazy resolver to the canonical entity-id mapper**
172
+ * (the metadata index's mapper, the SAME one brainy assigns entity
173
+ * ints with at write time). Wired by `src/plugin.ts` exactly as the
174
+ * graph index's `mapperResolver` (#68). The wrapper resolves
175
+ * `uuid → int` (slotmap writes, pushdown, membership) and `int → uuid`
176
+ * (search-result hydration) through it, which is why there is no
177
+ * longer a resident `slotByUuid` / `uuidBySlot` pair on the JS heap —
178
+ * the off-heap mmap slotmaps (`main.slotmap` / `main.slotrev`) plus
179
+ * this mapper replace them entirely (the 10B-RAM gate).
180
+ */
181
+ private mapperResolver?;
182
+ /**
183
+ * Lazy resolver to the shared #18 {@link MigrationCoordinator} (the same getter
184
+ * the metadata + graph providers receive). Undefined when no migration path is
185
+ * wired (e.g. a raw-napi construction in tests).
186
+ */
187
+ private readonly migrationCoordinatorGetter?;
188
+ /**
189
+ * True when the construction config carried an EXPLICIT `defaultLSearch` —
190
+ * the operator pinned the walk width, so {@link resolveLSearch} uses it
191
+ * verbatim instead of the scale-aware default (see {@link scaleAwareLSearch}).
192
+ * A `recall` preset does NOT pin: presets set the BASE and still scale with
193
+ * corpus size, preserving their relative recall/latency intent at every scale.
194
+ */
195
+ private lSearchPinned;
196
+ /**
197
+ * The in-flight fold (async rebuild) promise, or `null`. Concurrent
198
+ * `rebuild()` calls coalesce onto it, `addItem()`'s hard-cap backpressure
199
+ * awaits it, and `removeItem()` consults it so a delta-only removal that
200
+ * may already be baked into the incoming index still gets a tombstone
201
+ * (no resurrection at swap).
202
+ */
203
+ private rebuildInFlight;
204
+ /** In-flight L0 flush, or null (see {@link flushDeltaToL0}). */
205
+ private flushInFlight;
206
+ /** Live L0 segments, ascending by id (oldest first; search iterates newest→oldest). */
207
+ private l0Segments;
208
+ /** Next segment id (persisted in the manifest). */
209
+ private manifestNextId;
210
+ /** Segment-manifest cold-load memo (see {@link ensureSegments}). */
211
+ private segmentsLoaded;
212
+ private segmentsLoading;
213
+ /** Serializes manifest writes: the storage adapter has no per-key write
214
+ * ordering, so two in-flight saves (a flush racing a consolidation) could
215
+ * physically land out of order and persist a STALE segment list — losing a
216
+ * just-flushed segment's only reference after a restart. Each save chains
217
+ * behind the previous and builds its payload AT EXECUTION TIME. */
218
+ private manifestSaveChain;
219
+ /**
220
+ * **Piece I — Adaptive DiskANN.** The selector's most recent
221
+ * decision, exposed via {@link getLastModeSelection} for telemetry
222
+ * and tests. `null` until the first build or open.
223
+ */
224
+ private lastSelection;
225
+ constructor(config: DiskAnnIndexConfig & {
226
+ distanceFunction?: DistanceFunction;
227
+ }, distanceFunction: DistanceFunction, options?: {
228
+ storage?: StorageAdapter | null;
229
+ persistMode?: 'immediate' | 'deferred';
230
+ /**
231
+ * **#72 Phase A+B** — lazy resolver to the canonical entity-id
232
+ * mapper (the metadata index's mapper). Required for any operation
233
+ * that touches the main index (rebuild / search / removeItem):
234
+ * the wrapper resolves `slot ↔ uuid` through the off-heap mmap
235
+ * slotmaps + this mapper instead of resident JS maps. `src/plugin.ts`
236
+ * passes `() => metadataIndexInstance?.getIdMapper()`, mirroring the
237
+ * graph index's #68 wiring; the resolver is lazy so factory-call
238
+ * order between the providers doesn't matter.
239
+ */
240
+ mapperResolver?: () => EntityIdMapperLike | undefined;
241
+ /**
242
+ * **#18** — lazy resolver to the shared {@link MigrationCoordinator}. The
243
+ * plugin passes the SAME getter to all three derived-index providers so
244
+ * Brainy's feature-detected lock methods answer consistently across them.
245
+ */
246
+ migrationCoordinator?: () => MigrationCoordinator | undefined;
247
+ });
248
+ /**
249
+ * Resolve the canonical entity-id mapper, or throw if it isn't wired.
250
+ * Every main-index operation (rebuild, search-result hydration,
251
+ * pushdown, membership) resolves `slot ↔ uuid` through this mapper +
252
+ * the off-heap slotmaps; without it the wrapper cannot translate the
253
+ * native engine's entity ints to UUIDs. In production `src/plugin.ts`
254
+ * always wires it; the throw surfaces a misconfiguration loudly rather
255
+ * than silently returning empty results.
256
+ */
257
+ private mapper;
258
+ /**
259
+ * Append an entry to the delta buffer. Persisted by the next
260
+ * `rebuild()` call, which folds the delta into the main index.
261
+ */
262
+ addItem(item: VectorDocument): Promise<string>;
263
+ /**
264
+ * Mark an entry as removed. Filtered out at search time; physically
265
+ * removed at the next `rebuild()`.
266
+ */
267
+ removeItem(id: string): Promise<boolean>;
268
+ /**
269
+ * Hydrate the delta buffer from the canonical nouns persisted in
270
+ * storage — the cold-rebuild path used by {@link rebuild} after a
271
+ * `restore()` / migration, when the wrapper has no in-memory state to
272
+ * fold but storage holds the authoritative records. Streams via
273
+ * paginated `getNouns()` rather than fetching every noun at once.
274
+ *
275
+ * NOTE: like the existing delta path this materializes the vectors it
276
+ * loads into the JS delta. A billion-noun cold rebuild should instead
277
+ * stream straight from a storage-backed vector source (the native
278
+ * `build_from_file` path used by the SIFT billion-scale builder); that
279
+ * scale optimization is tracked separately. For the migration sizes
280
+ * this serves today the delta route is correct, and it logs the count
281
+ * it loaded so an operator can see the reconstruction happen.
282
+ *
283
+ * @param dim - Index dimension; nouns whose vector width differs are skipped.
284
+ */
285
+ private hydrateDeltaFromStorage;
286
+ search(queryVector: Vector, k?: number, filter?: (id: string) => Promise<boolean>, options?: {
287
+ rerank?: {
288
+ multiplier: number;
289
+ };
290
+ candidateIds?: string[];
291
+ /**
292
+ * Metadata∩graph universe (entity UUIDs) from the `find()`
293
+ * composition. When supplied and selective, it is PUSHED INTO the
294
+ * native beam walk (predicate pushdown, #10) so the walk collects only
295
+ * allowed slots — far higher recall than post-filtering the unfiltered
296
+ * top-k. Above {@link PUSHDOWN_MAX_SELECTIVITY} it is folded into the
297
+ * post-filter predicate instead (cheaper, and post-filter suffices).
298
+ */
299
+ allowedIds?: ReadonlySet<string>;
300
+ }): Promise<Array<[string, number]>>;
301
+ /**
302
+ * Predicate-pushdown search (#10). Translates the metadata∩graph entity
303
+ * universe to main-index slots, pushes them into the native beam walk
304
+ * (which traverses *through* all nodes but only collects allowed slots),
305
+ * and inflates `l_search` by ~1/selectivity so a selective predicate still
306
+ * surfaces k allowed neighbours. Delta-buffer entries (no slot yet) are
307
+ * brute-forced. This is the high-recall path for selective filtered
308
+ * `find()` — post-filtering the unfiltered top-k loses recall when few of
309
+ * those candidates pass the predicate.
310
+ *
311
+ * @param queryVector - The query embedding.
312
+ * @param k - Number of nearest allowed neighbours to return.
313
+ * @param allowedIds - Entity UUIDs permitted in the result (the find() universe).
314
+ * @param filter - Optional additional async predicate, composed with allowedIds.
315
+ * @param options - Optional rerank multiplier override.
316
+ * @returns Up to k `[uuid, distance]` pairs, ascending by distance.
317
+ */
318
+ private searchPushdown;
319
+ /**
320
+ * @description **#18 migration lock — feature-detected by Brainy.** `true` while
321
+ * the brain is undergoing (or has failed) the coordinated 7.x → 8.0 rebuild, so
322
+ * Brainy holds reads+writes behind the lock (and skips its lazy first-query
323
+ * force-rebuild of the vector index). Delegates to the shared {@link
324
+ * MigrationCoordinator}; `false` when none is wired.
325
+ * @returns `true` when the brain must not be read from or written to.
326
+ */
327
+ isMigrating(): boolean;
328
+ /**
329
+ * @description **#18 migration lock — structured progress** Brainy relays into
330
+ * `getIndexStatus().migration`. `null` unless a migration is in flight.
331
+ * @returns The current {@link MigrationStatus}, or `null`.
332
+ */
333
+ migrationStatus(): MigrationStatus | null;
334
+ /**
335
+ * @description Resolve the search-time walk width for this query: the
336
+ * scale-aware default ({@link scaleAwareLSearch} over the current corpus size)
337
+ * unless the operator pinned `defaultLSearch` explicitly, floored at `k × 2`
338
+ * so a large `k` is never starved. This is the E3 fix for the measured
339
+ * fixed-default recall fade (0.9942 @ 1M → 0.8747 @ 100M at l_search=100).
340
+ * @param k - The requested result count.
341
+ * @returns The walk width to pass to the native searcher.
342
+ */
343
+ /**
344
+ * **Adaptive DiskANN mode selection (Piece I)** — build the native config for
345
+ * an index of `totalCount` vectors: the selector picks "in-memory" for brains
346
+ * that fit in RAM (no PQ, sub-ms search), "hybrid" for medium scale, "on-disk"
347
+ * when memory is tight or many brains share the box. Shared by the full
348
+ * consolidation ({@link runRebuild}) and the O(delta) L0 segment flush
349
+ * ({@link runFlushToL0}); records the selection on {@link lastSelection}.
350
+ */
351
+ private nativeBuildCfg;
352
+ private resolveLSearch;
353
+ /** Total entries across the base index + all L0 segments (no delta). */
354
+ private indexedSize;
355
+ size(): number;
356
+ clear(): void;
357
+ /**
358
+ * Rebuild the main index from scratch: concatenate (current main −
359
+ * tombstones) ∪ delta, run a full DiskANN build, swap the searcher
360
+ * atomically.
361
+ *
362
+ * At billion-scale this is the expensive operation (hours-to-days of
363
+ * build time) — but it is NON-BLOCKING: the build runs on a worker
364
+ * thread (`rebuildFromExistingAsync`), so the JS event loop keeps
365
+ * serving reads and accepting writes for the entire fold. Writes that
366
+ * land mid-fold stay in the live delta (reconciled against the fold's
367
+ * snapshot at swap) and roll into the next fold. Concurrent calls
368
+ * coalesce onto the in-flight fold. The delta is bounded: a soft
369
+ * threshold kicks a background fold automatically and a hard cap
370
+ * applies backpressure in `addItem` (see `DELTA_SOFT_FOLD_BYTES`).
371
+ *
372
+ * The optional `recall` argument overrides the wrapper's
373
+ * construction-time recall preset for this rebuild only — useful
374
+ * when an operator wants to ship a higher-quality index after a
375
+ * data-quality push but kept the brain on `'balanced'` originally.
376
+ * Omit to keep the wrapper's current preset.
377
+ */
378
+ rebuild(options?: {
379
+ recall?: 'fast' | 'balanced' | 'accurate';
380
+ }): Promise<void>;
381
+ /** The fold body — see {@link rebuild} for the public contract. */
382
+ private runRebuild;
383
+ /**
384
+ * Flush the delta buffer to disk. For DiskANN the delta is in-memory
385
+ * by design (a few MB at most between rebuilds); returns the buffer
386
+ * size for parity with HNSW's flush contract.
387
+ */
388
+ flush(): Promise<number>;
389
+ getPersistMode(): 'immediate' | 'deferred';
390
+ /** Absolute path of a segment file (lives next to the base indexPath). */
391
+ private segPath;
392
+ /** Membership across the base + every L0 segment (int → any slot). */
393
+ private hasIntAnywhere;
394
+ /**
395
+ * @description Provider init — brainy feature-detects and awaits it. Cold-loads
396
+ * the segment manifest so a reopened brain serves its L0 segments from the
397
+ * first query (the wrapper's own async paths also call {@link ensureSegments},
398
+ * so a caller that skips init() still gets the segments lazily).
399
+ */
400
+ init(): Promise<void>;
401
+ /** Memoized segment-manifest cold-load. */
402
+ private ensureSegments;
403
+ /**
404
+ * Read `__vector_index_manifest__` and open every live L0 segment. A missing
405
+ * manifest is the legacy single-index layout (the base at `indexPath` IS the
406
+ * whole vector index) — existing brains upgrade seamlessly; the first flush
407
+ * writes the first manifest. A segment file the manifest references but that
408
+ * fails to open is SKIPPED loudly (crash-orphan tolerance; its entries remain
409
+ * safe in canonical storage and the next consolidation heals the set).
410
+ */
411
+ private loadSegmentManifest;
412
+ /**
413
+ * Persist the live segment set (AFTER the referenced files exist on disk).
414
+ * Saves are SERIALIZED and each snapshot the segment list at write time, so
415
+ * concurrent flush/consolidation saves can never persist out of order.
416
+ */
417
+ private saveSegmentManifest;
418
+ private writeSegmentManifestNow;
419
+ /**
420
+ * @description **#73 MVP-C — flush the delta into a new immutable L0 segment.**
421
+ * O(new-data): builds a small self-contained `.dkann` from the delta snapshot
422
+ * alone (existing index untouched), registers it in the manifest, and
423
+ * reconciles the live delta. Concurrent calls coalesce. Crash ordering: the
424
+ * segment file is fully written (with its slotmap sidecars) BEFORE the
425
+ * manifest references it.
426
+ */
427
+ private flushDeltaToL0;
428
+ /** The flush body — see {@link flushDeltaToL0}. */
429
+ private runFlushToL0;
430
+ private tryOpenExisting;
431
+ /**
432
+ * Remove any leftover `main.slots.json` written by a pre-#72 RC build.
433
+ * The native `main.slotmap` is now authoritative; a stale JSON sidecar
434
+ * is never read, but we delete it (best-effort) so it can never shadow
435
+ * the off-heap slotmap and to reclaim its (potentially large) bytes.
436
+ */
437
+ private removeLegacySlotsSidecar;
438
+ /**
439
+ * **Piece I.** Resolve the open-time mode for an existing file.
440
+ * Respects an explicit `config.mode` override; otherwise consults
441
+ * the adaptive selector.
442
+ */
443
+ private resolveOpenMode;
444
+ /**
445
+ * **Piece I.** Resolve the build-time mode. Same override-respecting
446
+ * shape as {@link resolveOpenMode}; the build-time decision
447
+ * additionally lets the selector choose Mode 1 (no PQ) when the
448
+ * brain fits in RAM.
449
+ */
450
+ private resolveBuildMode;
451
+ /**
452
+ * **Piece I.** The selector's most recent decision (either at the
453
+ * last `tryOpenExisting` or `rebuild`). Returns `null` if no
454
+ * brain has been opened or built yet. Surface for telemetry and
455
+ * tests.
456
+ */
457
+ getLastModeSelection(): ModeSelection | null;
458
+ /**
459
+ * **Test-only hook** for exercising the build-adjacency resolver
460
+ * without spinning up a real build. Mirrors the
461
+ * `_testInjectOsMemoryProbe` pattern (Piece J); not part of the
462
+ * public contract.
463
+ * @internal
464
+ */
465
+ _testResolveAdjacencyBackend(nodeCount: number): {
466
+ kind: 'mmap';
467
+ mmapPath: string;
468
+ } | {
469
+ kind: 'ram';
470
+ };
471
+ /**
472
+ * Resolve the build-time adjacency backend from the config plus
473
+ * the actual node count. `useMmapAdjacency: 'auto'` (the default)
474
+ * picks file-backed once `nodeCount > MMAP_ADJACENCY_AUTO_THRESHOLD`;
475
+ * explicit `true` / `false` always wins. Per the zero-config
476
+ * principle, operators don't have to know the 100 M threshold —
477
+ * the wrapper observes `totalCount` and flips automatically.
478
+ */
479
+ private resolveAdjacencyBackend;
480
+ /**
481
+ * Count tombstoned entities that are present in the MAIN index (so
482
+ * `size()` can subtract them). Membership is the off-heap reverse
483
+ * slotmap: resolve uuid → int → `hasInt`. Returns 0 without touching
484
+ * the mapper when there are no tombstones or no main index, so the
485
+ * common `size()` call stays allocation- and FFI-free.
486
+ */
487
+ private countMainTombstones;
488
+ }
489
+ //# sourceMappingURL=NativeDiskAnnWrapper.d.ts.map