@kb-labs/mind-core 2.93.0 → 2.96.0

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/dist/index.d.ts CHANGED
@@ -1,402 +1,467 @@
1
- export * from '@kb-labs/mind-types';
2
- import { IStorage } from '@kb-labs/sdk';
1
+ import { AgentSourceKind, IndexRequest, IndexResponse, SearchRequest, SearchResponse, QueryRequest, AgentResponse, ExploreRequest, ExploreResponse, ReindexRequest, DropRequest, DropResponse, SyncResponse, SyncListResponse, SyncStatusResponse, StatusResponse, HealthResponse, MindConfig, StageTrace, Trace, AgentWarning, AgentQueryMode, SnippetMode, AgentSource, SearchResult } from '@kb-labs/mind-contracts';
2
+ import { M as MindServices } from './services-CdtgGHNc.js';
3
+ import { IStorage, ILLM, ICache } from '@kb-labs/sdk';
3
4
 
4
5
  /**
5
- * @module @kb-labs/mind-core/error
6
- * Standardized error class for KB Labs Mind
7
- */
8
- declare class MindError extends Error {
9
- code: string;
10
- hint?: string | undefined;
11
- meta?: unknown | undefined;
12
- constructor(code: string, message: string, hint?: string | undefined, meta?: unknown | undefined);
6
+ * Adaptive source-file discovery via `globby` over the real filesystem.
7
+ *
8
+ * Reads the repo relative to `cwd` — NOT the platform `storage` adapter, which
9
+ * is a blob store that recurses into node_modules and chokes on pnpm symlink
10
+ * chains. Symlinks are not followed and dependency/build dirs are excluded.
11
+ *
12
+ * No language allowlist: we glob every file and KEEP whatever looks like source
13
+ * text, excluding only binaries, media, archives, data blobs, lockfiles and
14
+ * oversized/generated files. A new language (`.cs`, `.vue`, `.kt`, …) is indexed
15
+ * automatically — discovery adapts to the repo instead of being hand-curated.
16
+ *
17
+ * Returns paths relative to `cwd`.
18
+ */
19
+ /** Scope the engine discovers against — config-driven include/exclude globs. */
20
+ interface DiscoverScope {
21
+ /** Repo-relative roots/globs to index. Empty/undefined ⇒ whole repo. */
22
+ include?: string[];
23
+ /** Globs subtracted from the include set (added to the ignore list). */
24
+ exclude?: string[];
13
25
  }
14
- /**
15
- * Maps MindError codes to CLI exit codes
16
- */
17
- declare function getExitCode(err: MindError): number;
18
- /**
19
- * Error codes with their standard hints
20
- */
21
- declare const ERROR_HINTS: {
22
- readonly MIND_NO_GIT: "Initialize git repository or run from a git repository";
23
- readonly MIND_FS_TIMEOUT: "File system operation timed out - try increasing time budget";
24
- readonly MIND_PARSE_ERROR: "Failed to parse file - check syntax and try again";
25
- readonly MIND_PACK_BUDGET_EXCEEDED: "Context pack exceeds token budget - reduce content or increase budget";
26
- readonly MIND_FORBIDDEN: "Operation not permitted - check file permissions";
27
- readonly MIND_TIME_BUDGET: "Time budget exceeded - operation completed partially";
28
- readonly MIND_BAD_FLAGS: "Invalid command line flags - check values and try again";
29
- readonly MIND_INVALID_FLAG: "Invalid flag value - check format and try again";
30
- readonly MIND_BUNDLE_TIMEOUT: "Bundle operation timed out - skipped bundle information";
31
- readonly MIND_FEED_ERROR: "Mind feed operation failed - check logs for details";
32
- readonly MIND_INIT_ERROR: "Mind initialization failed - check permissions and try again";
33
- readonly MIND_UPDATE_ERROR: "Mind update operation failed - check logs for details";
34
- readonly MIND_PACK_ERROR: "Mind pack operation failed - check logs for details";
35
- readonly MIND_GIT_ERROR: "Git operation failed - check git repository status";
36
- readonly MIND_INDEX_NOT_FOUND: "Mind indexes not found - run \"kb mind init\" first";
37
- readonly MIND_INVALID_PATH: "Invalid file or directory path - check path exists and is accessible";
38
- readonly MIND_DEPENDENCY_ERROR: "Dependency resolution failed - check package configuration";
39
- readonly MIND_BUILD_ERROR: "Build operation failed - check configuration and try again";
40
- };
41
- type ErrorCode = keyof typeof ERROR_HINTS;
42
- /**
43
- * Create a MindError with standardized code and hint
44
- */
45
- declare function createMindError(code: ErrorCode, message: string, meta?: unknown): MindError;
46
- /**
47
- * Create a MindError from a generic error
48
- */
49
- declare function wrapError(error: unknown, code?: ErrorCode): MindError;
50
- /**
51
- * Check if an error is a MindError
52
- */
53
- declare function isMindError(error: unknown): error is MindError;
54
26
 
55
27
  /**
56
- * Pack and context types for KB Labs Mind
28
+ * Internal engine types (not wire contracts).
29
+ *
30
+ * A `Chunk` is the unit of indexing/retrieval. Chunks are stored two ways:
31
+ * - in the vector store (one `VectorRecord` per chunk, scoped by namespace=indexId)
32
+ * for semantic search;
33
+ * - in a per-index `chunks.json` via `IStorage` as the corpus for BM25 and
34
+ * listing/status.
57
35
  */
58
36
 
59
- type ContextSection = "intent_summary" | "product_overview" | "project_meta" | "api_signatures" | "recent_diffs" | "docs_overview" | "impl_snippets" | "configs_profiles";
60
- interface ContextBudget {
61
- totalTokens: number;
62
- caps: Partial<Record<ContextSection, number>>;
63
- truncation: "start" | "middle" | "end";
37
+ interface Chunk {
38
+ /** Stable id: `${path}#${startLine}-${endLine}`. */
39
+ id: string;
40
+ path: string;
41
+ startLine: number;
42
+ endLine: number;
43
+ text: string;
44
+ kind: AgentSourceKind;
64
45
  }
65
- type ContextSlice = "overview" | "api" | "diffs" | "snippets" | "configs" | "meta" | "docs";
66
- interface ContextPreset {
67
- name: string;
68
- weight: Partial<Record<ContextSlice, number>>;
46
+ /** Metadata stored alongside each vector record. */
47
+ interface ChunkMeta extends Record<string, unknown> {
48
+ path: string;
49
+ startLine: number;
50
+ endLine: number;
51
+ text: string;
52
+ kind: AgentSourceKind;
69
53
  }
70
- interface ITokenEstimator {
71
- estimate(text: string): number;
72
- truncate(text: string, maxTokens: number, mode: "start" | "middle" | "end"): string;
54
+ /** Per-file bookkeeping. `hash` enables incremental (delta) re-indexing. */
55
+ interface FileEntry {
56
+ chunks: number;
57
+ indexedAt: string;
58
+ /** Content hash; unchanged hash ⇒ file skipped on re-index. */
59
+ hash: string;
73
60
  }
61
+ /** Persisted per-index corpus (source of truth for BM25 + listing). */
62
+ interface IndexManifest {
63
+ indexId: string;
64
+ /** Chunks keyed by id. */
65
+ chunks: Chunk[];
66
+ /** Per-path bookkeeping for incremental sync. */
67
+ files: Record<string, FileEntry>;
68
+ updatedAt: string | null;
69
+ }
70
+ declare function chunkId(path: string, startLine: number, endLine: number): string;
71
+ /** Derive a source kind from a file path. */
72
+ declare function kindFromPath(path: string): AgentSourceKind;
74
73
 
75
74
  /**
76
- * Token estimation utilities for KB Labs Mind
75
+ * Sliding-window chunker (Phase 2 baseline; AST chunking is layered in Phase 3).
76
+ *
77
+ * Splits a file into line-aligned chunks of roughly `maxTokens` tokens with
78
+ * `overlapTokens` of overlap. Token count is approximated by whitespace words —
79
+ * good enough for windowing; real tokenization is not needed here.
77
80
  */
78
81
 
79
- /**
80
- * Default whitespace-aware token estimator
81
- * Algorithm: ~3.8-4.2 chars/token based on whitespace and code patterns
82
- */
83
- declare class DefaultTokenEstimator implements ITokenEstimator {
84
- private readonly charsPerToken;
85
- private readonly codeBonus;
86
- private readonly punctuationWeight;
87
- estimate(text: string): number;
88
- truncate(text: string, maxTokens: number, mode: "start" | "middle" | "end"): string;
82
+ interface ChunkOptions {
83
+ maxTokens: number;
84
+ overlapTokens: number;
89
85
  }
90
- /**
91
- * Default token estimator instance
92
- */
93
- declare const defaultTokenEstimator: DefaultTokenEstimator;
94
- /**
95
- * Estimate tokens using default strategy
96
- */
97
- declare function estimateTokens(text: string): number;
98
- /**
99
- * Truncate text to token limit using default strategy
100
- */
101
- declare function truncateToTokens(text: string, maxTokens: number, mode?: "start" | "middle" | "end"): string;
86
+ declare function slidingWindowChunks(path: string, content: string, opts: ChunkOptions): Chunk[];
102
87
 
103
88
  /**
104
- * Hashing utilities for KB Labs Mind
105
- */
106
- /**
107
- * Compute SHA256 hash for string content
108
- */
109
- declare function sha256(content: string): string;
110
- /**
111
- * Compute SHA256 hash for Buffer content
112
- */
113
- declare function sha256Buffer(buffer: Buffer): string;
114
- /**
115
- * Compute SHA256 hash for file content (streaming for large files)
89
+ * Ingest pipeline: discover -> (hash-delta) -> chunk -> embed ->
90
+ * upsert(namespace=indexId), then persist the per-index manifest.
91
+ *
92
+ * Incremental by default: only files whose content hash changed since the last
93
+ * index are re-chunked/re-embedded; unchanged files keep their existing chunks
94
+ * (and vectors), removed files are pruned. `full` forces a clean rebuild.
116
95
  */
117
- declare function sha256File(filePath: string): Promise<string>;
96
+
97
+ /** Staged progress events emitted during ingest (for CLI/REST progress UIs). */
98
+ type IngestProgress = {
99
+ stage: 'discover';
100
+ files: number;
101
+ } | {
102
+ stage: 'delta';
103
+ toIndex: number;
104
+ unchanged: number;
105
+ removed: number;
106
+ } | {
107
+ stage: 'chunk';
108
+ chunks: number;
109
+ } | {
110
+ stage: 'embed';
111
+ done: number;
112
+ total: number;
113
+ } | {
114
+ stage: 'upsert';
115
+ count: number;
116
+ } | {
117
+ stage: 'save';
118
+ };
119
+ interface IngestInput {
120
+ indexId: string;
121
+ /** Workspace root that source paths are resolved against. */
122
+ cwd: string;
123
+ /** Config-driven include/exclude globs for discovery. */
124
+ scope?: DiscoverScope;
125
+ chunk: ChunkOptions;
126
+ /** Use structure-aware chunking for code. */
127
+ ast: boolean;
128
+ /** Force a full rebuild instead of incremental delta. */
129
+ full?: boolean;
130
+ /** ISO timestamp for manifest bookkeeping (injectable for determinism). */
131
+ now: string;
132
+ /** Optional staged-progress sink (best-effort; never affects results). */
133
+ onProgress?: (event: IngestProgress) => void;
134
+ }
135
+ interface IngestResult {
136
+ filesIndexed: number;
137
+ chunks: number;
138
+ /** Files (re)embedded this run (new + changed). */
139
+ added: number;
140
+ updated: number;
141
+ /** Files removed from the index (gone from disk). */
142
+ removed: number;
143
+ /** Files skipped because their content hash was unchanged. */
144
+ unchanged: number;
145
+ }
146
+ declare function ingest(input: IngestInput, services: MindServices): Promise<IngestResult>;
118
147
 
119
148
  /**
120
- * Path utilities for KB Labs Mind
121
- */
122
- /**
123
- * Convert path to POSIX format (forward slashes)
124
- */
125
- declare function toPosix(filePath: string): string;
126
- /**
127
- * Convert POSIX path back to platform-specific format
128
- */
129
- declare function fromPosix(posixPath: string): string;
130
- /**
131
- * Find workspace root by looking for git repository or monorepo indicators
132
- * Searches up the directory tree from cwd
149
+ * Mind facade the single object the plugin (CLI + REST) talks to.
150
+ *
151
+ * `createMind(services, config)` returns verbs that both the CLI commands and
152
+ * REST handlers call identically, so behaviour is shared by construction.
153
+ * Phase 2 ships `index` / `search` / `status`; `ask`, `sync`, `reindex` are
154
+ * layered in later phases.
133
155
  */
134
- declare function findWorkspaceRoot(cwd: string): Promise<string>;
156
+
157
+ interface Mind {
158
+ index(req: IndexRequest, onProgress?: (event: IngestProgress) => void): Promise<IndexResponse>;
159
+ search(req: SearchRequest): Promise<SearchResponse>;
160
+ ask(req: QueryRequest): Promise<AgentResponse>;
161
+ explore(req: ExploreRequest): Promise<ExploreResponse>;
162
+ reindex(req: ReindexRequest): Promise<IndexResponse>;
163
+ drop(req: DropRequest): Promise<DropResponse>;
164
+ syncAdd(paths: string[], indexId?: string): Promise<SyncResponse>;
165
+ syncUpdate(paths: string[], indexId?: string): Promise<SyncResponse>;
166
+ syncDelete(paths: string[], indexId?: string): Promise<SyncResponse>;
167
+ syncList(indexId?: string): Promise<SyncListResponse>;
168
+ syncStatus(indexId?: string): Promise<SyncStatusResponse>;
169
+ status(indexId?: string): Promise<StatusResponse>;
170
+ health(): Promise<HealthResponse>;
171
+ }
172
+ interface CreateMindOptions {
173
+ /** Workspace root that source paths are resolved against (default: process.cwd()). */
174
+ cwd?: string;
175
+ /** Injectable clock for deterministic tests. */
176
+ now?: () => number;
177
+ /** Injectable ISO timestamp source for manifest bookkeeping. */
178
+ isoNow?: () => string;
179
+ }
180
+ declare function createMind(services: MindServices, config: MindConfig, options?: CreateMindOptions): Mind;
181
+
135
182
  /**
136
- * Make path relative to workspace root
183
+ * Pipeline primitive.
184
+ *
185
+ * Every stage is a pure function `(input, services) => output`. A `Tracer`
186
+ * times each stage and collects `StageTrace` entries so the full query flow
187
+ * (retrieve → fuse → rerank → verify → compress → synthesize) is observable.
188
+ * Modes (instant/auto/thinking) are just configs deciding which stages run —
189
+ * never separate code paths.
137
190
  */
138
- declare function makeRelativeToRoot(absolutePath: string, root: string): string;
191
+
192
+ type Stage<I, O> = (input: I, services: MindServices) => Promise<O>;
193
+ /** Monotonic clock; injectable so tests stay deterministic. */
194
+ type Clock = () => number;
195
+ /**
196
+ * Collects per-stage traces for one request. `run` times a stage and records
197
+ * its duration + output size.
198
+ */
199
+ declare class Tracer {
200
+ private readonly requestId;
201
+ private readonly mode;
202
+ private readonly clock;
203
+ private readonly stages;
204
+ constructor(requestId: string, mode: string, clock?: Clock);
205
+ run<I, O>(stage: string, input: I, services: MindServices, fn: Stage<I, O>): Promise<O>;
206
+ /** Record a stage trace manually (for stages not wrapped by `run`). */
207
+ record(trace: StageTrace): void;
208
+ build(totalMs: number): Trace;
209
+ }
210
+
139
211
  /**
140
- * Check if path should be ignored based on common patterns
212
+ * Structure-aware chunking for code (AST-lite).
213
+ *
214
+ * Splits a source file at top-level declaration boundaries (functions, classes,
215
+ * interfaces, etc.) so chunks align with logical units instead of arbitrary
216
+ * windows. Oversized blocks fall back to the sliding window. This is a
217
+ * dependency-free approximation of AST chunking; a tree-sitter backend can
218
+ * replace it later behind the same `chunkFile` entry without touching callers.
141
219
  */
142
- declare function shouldIgnorePath(filePath: string): boolean;
220
+
221
+ declare function structuralChunks(path: string, content: string, opts: ChunkOptions): Chunk[];
222
+ /** Dispatch: structure-aware for code when enabled, else sliding window. */
223
+ declare function chunkFile(path: string, content: string, opts: ChunkOptions, ast: boolean): Chunk[];
143
224
 
144
225
  /**
145
- * Mathematical utility functions for KB Labs Mind
226
+ * BM25 keyword ranking over the in-memory chunk corpus.
227
+ *
228
+ * Self-contained Okapi BM25. Tokenization lowercases and splits on
229
+ * non-alphanumerics, additionally splitting camelCase and snake_case so code
230
+ * identifiers (`getUserId`, `user_id`) match natural-language queries.
146
231
  */
232
+
233
+ interface Ranked {
234
+ id: string;
235
+ score: number;
236
+ }
237
+ declare function tokenize(text: string): string[];
238
+ declare function bm25Search(chunks: Chunk[], query: string, limit: number): Ranked[];
239
+
147
240
  /**
148
- * Calculate cosine similarity between two vectors
149
- *
150
- * Cosine similarity measures the cosine of the angle between two vectors,
151
- * producing a value between -1 and 1, where:
152
- * - 1 means vectors point in the same direction (identical)
153
- * - 0 means vectors are orthogonal (no similarity)
154
- * - -1 means vectors point in opposite directions
155
- *
156
- * @param a - First vector (array of numbers)
157
- * @param b - Second vector (array of numbers)
158
- * @returns Similarity score [0-1], or 0 if vectors have different lengths or zero magnitudes
241
+ * Reciprocal Rank Fusion (RRF) with intent-adaptive weights.
159
242
  *
160
- * @example
161
- * ```typescript
162
- * const similarity = cosineSimilarity([1, 2, 3], [4, 5, 6]);
163
- * console.log(similarity); // ~0.974
164
- * ```
243
+ * Fuses ranked lists by `weight * 1/(k + rank)`. Weights shift the balance
244
+ * between the semantic (vector) and keyword (BM25) lists based on query intent.
165
245
  */
166
- declare function cosineSimilarity(a: number[], b: number[]): number;
246
+
247
+ type QueryIntent = 'lookup' | 'concept' | 'architecture';
248
+ /** Which retrieval signal surfaced a result — the "why" + proof-of-value vs grep. */
249
+ type MatchedBy = 'lexical' | 'semantic' | 'both';
250
+ interface WeightedList {
251
+ ranked: Ranked[];
252
+ weight: number;
253
+ /** Provenance label for this list (so the fused result knows where it came from). */
254
+ label?: Exclude<MatchedBy, 'both'>;
255
+ }
256
+ interface FusedRanked extends Ranked {
257
+ matchedBy: MatchedBy;
258
+ }
259
+ /** Vector/BM25 weighting per intent (vector favored for meaning, BM25 for exact terms). */
260
+ declare function intentWeights(intent?: QueryIntent): {
261
+ vector: number;
262
+ bm25: number;
263
+ };
264
+ declare function rrfFuse(lists: WeightedList[], k: number): FusedRanked[];
265
+
167
266
  /**
168
- * Calculate dot product of two vectors
169
- *
170
- * @param a - First vector
171
- * @param b - Second vector
172
- * @returns Dot product, or 0 if vectors have different lengths
173
- *
174
- * @example
175
- * ```typescript
176
- * const dot = dotProduct([1, 2, 3], [4, 5, 6]);
177
- * console.log(dot); // 32
178
- * ```
267
+ * Retrieval pipeline: BM25 (over manifest corpus) + vector (platform store),
268
+ * fused with intent-adaptive RRF, mapped back to chunks.
179
269
  */
180
- declare function dotProduct(a: number[], b: number[]): number;
270
+
271
+ interface RetrieveInput {
272
+ text: string;
273
+ indexId: string;
274
+ limit: number;
275
+ intent?: QueryIntent;
276
+ rrfK: number;
277
+ /** HyDE: embed an LLM-generated hypothetical doc for the vector search. */
278
+ hyde?: boolean;
279
+ /** Query expansion: append LLM-suggested related terms to the BM25 query. */
280
+ expand?: boolean;
281
+ }
282
+ interface RankedChunk {
283
+ chunk: Chunk;
284
+ /** Fused RRF score (best first). */
285
+ score: number;
286
+ /** Which signal surfaced this chunk (lexical/semantic/both) — provenance. */
287
+ matchedBy: MatchedBy;
288
+ }
289
+ interface RetrieveOutput {
290
+ /** Fused, ranked chunks (best first), truncated to `limit`. */
291
+ ranked: RankedChunk[];
292
+ /** Raw cosine confidence proxy from the vector list (top-3 avg). */
293
+ confidence: number;
294
+ /** Share of returned results found semantic-only (grep would miss them). */
295
+ semanticWinRate: number;
296
+ }
297
+ declare function retrieve(input: RetrieveInput, services: MindServices): Promise<RetrieveOutput>;
298
+
181
299
  /**
182
- * Calculate magnitude (L2 norm) of a vector
300
+ * Heuristic reranking of fused candidates.
183
301
  *
184
- * @param vec - Input vector
185
- * @returns Magnitude (Euclidean length)
302
+ * Lightweight, deterministic signals layered on the fused RRF score:
303
+ * - exact query-term coverage in the chunk text (keyword precision)
304
+ * - identifier hit: a query token appearing verbatim (good for code lookups)
186
305
  *
187
- * @example
188
- * ```typescript
189
- * const mag = magnitude([3, 4]);
190
- * console.log(mag); // 5
191
- * ```
306
+ * An optional LLM reranker can be layered later; this heuristic pass is always
307
+ * available and needs no model.
192
308
  */
193
- declare function magnitude(vec: number[]): number;
309
+
310
+ declare function rerank(ranked: RankedChunk[], query: string): RankedChunk[];
311
+
194
312
  /**
195
- * Normalize a vector to unit length
313
+ * Semantic-ish dedup of ranked chunks via token-set Jaccard similarity.
196
314
  *
197
- * @param vec - Input vector
198
- * @returns Normalized vector (magnitude = 1), or original if magnitude is 0
199
- *
200
- * @example
201
- * ```typescript
202
- * const normalized = normalize([3, 4]);
203
- * console.log(normalized); // [0.6, 0.8]
204
- * ```
315
+ * Keeps the highest-ranked chunk and drops later near-duplicates (e.g. the same
316
+ * code re-surfaced by both BM25 and vector lists, or overlapping windows).
317
+ * Operates on the small post-fusion list, so O(n²) is fine.
205
318
  */
206
- declare function normalize(vec: number[]): number[];
319
+
320
+ declare function dedupRanked(ranked: RankedChunk[], threshold?: number): RankedChunk[];
207
321
 
208
322
  /**
209
- * Base class for file-based stores with rotation support
323
+ * Anti-hallucination verification + confidence stack.
210
324
  *
211
- * Provides JSONL file rotation, segmentation by date, and automatic cleanup.
212
- * Used by history stores, feedback stores, and other persistent logging mechanisms.
325
+ * Each retrieved chunk is verified against the live source via `IStorage`:
326
+ * file exists (0.7) + snippet still present (0.3). The verification rate scales
327
+ * the retrieval confidence — a stale or fabricated source drags confidence down.
328
+ * Mirrors the legacy mind concept (source-verifier + confidence floor).
213
329
  */
214
330
 
215
- interface FileRotationOptions {
216
- /**
217
- * Base directory path for storing files
218
- * @default '.kb/mind/store/'
219
- */
220
- basePath?: string;
221
- /**
222
- * Prefix for generated filenames
223
- * @default 'store-'
224
- */
225
- filePrefix?: string;
226
- /**
227
- * Maximum number of records per file before rotation
228
- * @default 1000
229
- */
230
- maxRecordsPerFile?: number;
231
- /**
232
- * Maximum number of files to keep (oldest deleted first)
233
- * @default 30
234
- */
235
- maxFiles?: number;
331
+ interface VerificationResult {
332
+ /** 0..1 fraction of (weighted) verification across chunks. */
333
+ rate: number;
334
+ /** Per-chunk verification score (0..1), aligned with input order. */
335
+ perChunk: number[];
336
+ }
337
+ declare function verifySources(ranked: RankedChunk[], storage: IStorage): Promise<VerificationResult>;
338
+ interface ConfidenceResult {
339
+ confidence: number;
340
+ warnings: AgentWarning[];
236
341
  }
342
+ /** Combine retrieval confidence with the verification rate; warn below floor. */
343
+ declare function computeConfidence(retrievalConfidence: number, verificationRate: number, floor: number): ConfidenceResult;
344
+
237
345
  /**
238
- * Abstract base class for file-based stores with automatic rotation
239
- *
240
- * Features:
241
- * - JSONL format (one JSON object per line)
242
- * - Date-based file segmentation (YYYYMMDD-timestamp.jsonl)
243
- * - Automatic rotation when maxRecordsPerFile reached
244
- * - Automatic cleanup when maxFiles exceeded
245
- * - Sorted file iteration (oldest to newest)
346
+ * Field-checker an anti-hallucination signal ported from the legacy engine.
246
347
  *
247
- * @example
248
- * ```typescript
249
- * class MyStore extends FileRotationStore<MyRecord> {
250
- * async save(record: MyRecord): Promise<void> {
251
- * return this.appendRecord(record);
252
- * }
253
- *
254
- * async find(criteria: any): Promise<MyRecord[]> {
255
- * return this.readRecords((rec) => rec.id === criteria.id);
256
- * }
257
- * }
258
- * ```
348
+ * An LLM answer that cites code should only name symbols that actually appear
349
+ * in the retrieved sources. We extract code-like symbols from the answer
350
+ * (backtick spans, camelCase/dotted/underscored identifiers, file names) and
351
+ * check each against the retrieved chunks (text + path). The grounded fraction
352
+ * folds into the confidence stack, and any ungrounded terms are surfaced as a
353
+ * warning — a cheap, deterministic guard against fabricated APIs.
259
354
  */
260
- declare abstract class FileRotationStore<TRecord> {
261
- protected readonly storage: IStorage;
262
- protected readonly basePath: string;
263
- protected readonly filePrefix: string;
264
- protected readonly maxRecordsPerFile: number;
265
- protected readonly maxFiles: number;
266
- constructor(storage: IStorage, options?: FileRotationOptions);
267
- /**
268
- * Append a record to the current writable file
269
- *
270
- * Automatically handles:
271
- * - File rotation when maxRecordsPerFile exceeded
272
- * - Cleanup when maxFiles exceeded
273
- * - JSONL formatting
274
- *
275
- * @param record - Record to append
276
- */
277
- protected appendRecord(record: TRecord): Promise<void>;
278
- /**
279
- * Read records from all files, optionally filtering
280
- *
281
- * @param filter - Optional filter function
282
- * @param limit - Maximum number of records to return
283
- * @returns Array of records matching filter
284
- */
285
- protected readRecords(filter?: (record: TRecord) => boolean, limit?: number): Promise<TRecord[]>;
286
- /**
287
- * Get the current writable file path
288
- *
289
- * Creates a new segment if:
290
- * - No files exist
291
- * - Latest file has >= maxRecordsPerFile records
292
- *
293
- * @returns Path to writable file
294
- */
295
- protected getWritableFile(): Promise<string>;
296
- /**
297
- * Get all store files sorted by timestamp (oldest to newest)
298
- *
299
- * @returns Sorted array of file paths
300
- */
301
- protected getFilesSorted(): Promise<string[]>;
302
- /**
303
- * Generate a segment file path from timestamp
304
- *
305
- * Format: {filePrefix}YYYYMMDD-{timestamp}.jsonl
306
- * Example: history-20251209-1733769000123.jsonl
307
- *
308
- * @param ts - Unix timestamp in milliseconds
309
- * @returns Full file path
310
- */
311
- protected segmentPath(ts: number): string;
312
- /**
313
- * Enforce file rotation by deleting oldest files if maxFiles exceeded
314
- */
315
- protected enforceRotation(): Promise<void>;
316
- /**
317
- * Ensure path ends with trailing slash
318
- */
319
- protected ensureTrailingSlash(p: string): string;
355
+
356
+ /** Extract code-like symbols from an answer (deduped, order-preserving). */
357
+ declare function extractSymbols(answer: string): string[];
358
+ interface FieldCheckResult {
359
+ /** 0..1 fraction of extracted symbols grounded in the sources. */
360
+ rate: number;
361
+ /** Symbols not found in any retrieved chunk. */
362
+ ungrounded: string[];
363
+ /** Number of symbols checked (0 → nothing to verify, rate defaults to 1). */
364
+ checked: number;
320
365
  }
366
+ /** Check that symbols named in the answer appear in the retrieved sources. */
367
+ declare function checkFields(answer: string, ranked: RankedChunk[]): FieldCheckResult;
321
368
 
322
369
  /**
323
- * JSON file operations for KB Labs Mind
324
- */
325
- /**
326
- * Read JSON file with error handling
327
- */
328
- declare function readJson<T = unknown>(filePath: string): Promise<T | null>;
329
- /**
330
- * Write JSON file atomically with sorted keys
331
- */
332
- declare function writeJson<T>(filePath: string, data: T): Promise<void>;
333
- /**
334
- * Compute hash of JSON content
370
+ * Query decomposition (multi-step reasoning) for richer agent modes.
371
+ *
372
+ * Asks the LLM to split a complex question into focused sub-queries. Degrades
373
+ * gracefully: if decomposition is disabled or the LLM returns nothing usable,
374
+ * it falls back to the original query alone.
335
375
  */
336
- declare function computeJsonHash(data: unknown): string;
376
+
377
+ declare function decompose(query: string, llm: ILLM, maxSubqueries: number): Promise<string[]>;
337
378
 
338
379
  /**
339
- * @module @kb-labs/mind-core/verification
340
- * Mind index verification utilities
380
+ * Answer synthesis + frozen `AgentResponse` assembly.
341
381
  *
342
- * Moved from mind-gateway to break circular dependency (TASK-004)
382
+ * Produces the `agent-response-v1` contract consumed by CLAUDE.md and the
383
+ * task-rag skill. The response is validated against the contract zod before
384
+ * being returned, so a drift fails loudly here rather than silently downstream.
343
385
  */
344
- interface VerifyResult {
345
- ok: boolean;
346
- code: string | null;
347
- inconsistencies: string[];
348
- hint: string;
386
+
387
+ declare function synthesizeAnswer(query: string, ranked: RankedChunk[], llm: ILLM, useLLM: boolean): Promise<string>;
388
+ interface ToSourcesOptions {
389
+ snippet: SnippetMode;
390
+ staleByFile: Map<string, boolean>;
391
+ }
392
+ declare function toSources(ranked: RankedChunk[], opts: ToSourcesOptions): AgentSource[];
393
+ interface BuildAgentResponseInput {
394
+ answer: string;
395
+ ranked: RankedChunk[];
396
+ confidence: number;
397
+ mode: AgentQueryMode;
398
+ requestId: string;
399
+ timingMs: number;
400
+ indexId: string;
401
+ floor: number;
402
+ snippet: SnippetMode;
403
+ staleByFile: Map<string, boolean>;
404
+ warnings?: AgentWarning[];
349
405
  }
406
+ /** Assemble + validate the lean agent response. */
407
+ declare function buildAgentResponse(input: BuildAgentResponseInput): AgentResponse;
408
+
350
409
  /**
351
- * Verify Mind index integrity
352
- *
353
- * Checks:
354
- * 1. Main index file exists (.kb/mind/index.json)
355
- * 2. Individual file hashes match (api-index, deps, recent-diff)
356
- * 3. Combined index checksum is valid
357
- * 4. Required files are present
410
+ * Query-history feedback loop via the platform cache (sorted set).
358
411
  *
359
- * @param cwd - Workspace root directory
360
- * @returns Verification result with inconsistencies list
361
- *
362
- * @example
363
- * ```typescript
364
- * const result = await verifyIndexes('/path/to/workspace');
365
- * if (!result.ok) {
366
- * console.error('Inconsistencies:', result.inconsistencies);
367
- * console.log('Hint:', result.hint);
368
- * }
369
- * ```
412
+ * Records queries per index (scored by timestamp) so the engine can learn from
413
+ * usage over time. Best-effort: cache failures never break a query.
370
414
  */
371
- declare function verifyIndexes(cwd: string): Promise<VerifyResult>;
372
415
 
373
- /**
374
- * Default configurations for KB Labs Mind
375
- */
416
+ declare function recordQuery(cache: ICache, indexId: string, query: string, at: number): Promise<void>;
417
+ declare function recentQueries(cache: ICache, indexId: string, sinceMs?: number): Promise<string[]>;
376
418
 
377
419
  /**
378
- * Default context budget
379
- */
380
- declare const DEFAULT_BUDGET: ContextBudget;
381
- /**
382
- * Default context preset
420
+ * Incremental document sync — add / update / delete specific paths in an index
421
+ * without a full rebuild. Operates on both the vector store (namespace=indexId)
422
+ * and the persisted manifest (BM25 corpus + bookkeeping).
383
423
  */
384
- declare const DEFAULT_PRESET: ContextPreset;
385
- /**
386
- * Default time budget for indexing operations (ms)
387
- */
388
- declare const DEFAULT_TIME_BUDGET_MS = 800;
389
- /**
390
- * Maximum file size to process (bytes)
391
- */
392
- declare const MAX_FILE_SIZE_BYTES: number;
424
+
425
+ interface SyncOptions {
426
+ indexId: string;
427
+ /** Workspace root that source paths are resolved against. */
428
+ cwd: string;
429
+ chunk: ChunkOptions;
430
+ ast: boolean;
431
+ now: string;
432
+ }
433
+ interface SyncCounts {
434
+ added: number;
435
+ updated: number;
436
+ deleted: number;
437
+ }
438
+ declare function syncAdd(paths: string[], services: MindServices, opts: SyncOptions): Promise<SyncCounts>;
439
+ declare function syncUpdate(paths: string[], services: MindServices, opts: SyncOptions): Promise<SyncCounts>;
440
+ declare function syncDelete(paths: string[], services: MindServices, opts: SyncOptions): Promise<SyncCounts>;
441
+
393
442
  /**
394
- * Maximum lines per snippet
443
+ * Shape ranked chunks into lean wire `SearchResult`s (pointers, not payloads).
444
+ *
445
+ * `snippet` rides only as much text as the caller asked for (`--snippet`):
446
+ * none → nothing; line → the first meaningful line; full → truncated chunk.
395
447
  */
396
- declare const MAX_SNIPPET_LINES = 60;
448
+
449
+ interface ToResultsOptions {
450
+ snippet: SnippetMode;
451
+ /** file → stale (on-disk drift since indexing). */
452
+ staleByFile: Map<string, boolean>;
453
+ }
454
+ declare function toSearchResults(ranked: RankedChunk[], opts: ToResultsOptions): SearchResult[];
455
+
397
456
  /**
398
- * Generator string for artifacts
457
+ * Per-index manifest persistence via `IStorage`.
458
+ *
459
+ * The manifest is the source of truth for the BM25 corpus and for
460
+ * listing/status. Vectors live in the vector store; the manifest holds chunk
461
+ * text + per-file bookkeeping. One manifest per index id.
399
462
  */
400
- declare function getGenerator(): string;
401
463
 
402
- export { DEFAULT_BUDGET, DEFAULT_PRESET, DEFAULT_TIME_BUDGET_MS, DefaultTokenEstimator, ERROR_HINTS, type ErrorCode, type FileRotationOptions, FileRotationStore, MAX_FILE_SIZE_BYTES, MAX_SNIPPET_LINES, MindError, type VerifyResult, computeJsonHash, cosineSimilarity, createMindError, defaultTokenEstimator, dotProduct, estimateTokens, findWorkspaceRoot, fromPosix, getExitCode, getGenerator, isMindError, magnitude, makeRelativeToRoot, normalize, readJson, sha256, sha256Buffer, sha256File, shouldIgnorePath, toPosix, truncateToTokens, verifyIndexes, wrapError, writeJson };
464
+ declare function loadManifest(storage: IStorage, indexId: string): Promise<IndexManifest>;
465
+ declare function saveManifest(storage: IStorage, manifest: IndexManifest): Promise<void>;
466
+
467
+ export { type Chunk, type ChunkMeta, type Clock, type ConfidenceResult, type CreateMindOptions, type FieldCheckResult, type IndexManifest, type IngestInput, type IngestProgress, type IngestResult, type Mind, MindServices, type QueryIntent, type RankedChunk, type RetrieveInput, type RetrieveOutput, type Stage, type SyncCounts, type SyncOptions, Tracer, type VerificationResult, bm25Search, buildAgentResponse, checkFields, chunkFile, chunkId, computeConfidence, createMind, decompose, dedupRanked, extractSymbols, ingest, intentWeights, kindFromPath, loadManifest, recentQueries, recordQuery, rerank, retrieve, rrfFuse, saveManifest, slidingWindowChunks, structuralChunks, syncAdd, syncDelete, syncUpdate, synthesizeAnswer, toSearchResults, toSources, tokenize, verifySources };