claude-flow 3.21.0 → 3.22.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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/v3/@claude-flow/cli/dist/src/commands/daemon.js +23 -2
  3. package/v3/@claude-flow/cli/dist/src/commands/doctor.js +56 -0
  4. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.d.ts +27 -0
  5. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +374 -0
  6. package/v3/@claude-flow/cli/dist/src/commands/memory.js +4 -2
  7. package/v3/@claude-flow/cli/dist/src/index.d.ts +1 -1
  8. package/v3/@claude-flow/cli/dist/src/index.js +22 -1
  9. package/v3/@claude-flow/cli/dist/src/init/executor.js +37 -0
  10. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.d.ts +19 -0
  11. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.js +175 -0
  12. package/v3/@claude-flow/cli/dist/src/init/helper-signing.d.ts +30 -0
  13. package/v3/@claude-flow/cli/dist/src/init/helper-signing.js +60 -0
  14. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +16 -0
  15. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +89 -8
  16. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.d.ts +53 -0
  17. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.js +118 -0
  18. package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -7
  19. package/v3/@claude-flow/cli/dist/src/mcp-client.js +13 -0
  20. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +9 -2
  21. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.d.ts +162 -0
  22. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +548 -0
  23. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.d.ts +9 -1
  24. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +4 -1
  25. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +115 -54
  26. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +71 -0
  27. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +330 -2
  28. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.d.ts +111 -0
  29. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.js +510 -0
  30. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +41 -0
  31. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +277 -0
  32. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +22 -1
  33. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +117 -6
  34. package/v3/@claude-flow/cli/package.json +5 -2
@@ -1127,6 +1127,329 @@ async function activateControllerRegistry(dbPath, verbose) {
1127
1127
  }
1128
1128
  return { activated, failed, initTimeMs: performance.now() - startTime };
1129
1129
  }
1130
+ /**
1131
+ * Self-heal an EXISTING memory database that is missing the `vector_indexes`
1132
+ * table or per-namespace rows.
1133
+ *
1134
+ * Why this exists: fresh installs create `vector_indexes` + seed rows, but a
1135
+ * DB written by an older CLI or by agentdb directly may have thousands of
1136
+ * embedded rows in `memory_entries` and NO `vector_indexes` table at all.
1137
+ * Two things break as a result:
1138
+ * 1. The statusline's vector count read collapsed to `0` (the count query
1139
+ * referenced the missing table and failed whole — now split, but the
1140
+ * HNSW flag still needs the table).
1141
+ * 2. #1941 — `memory_search` routes per namespace via `vector_indexes`; a
1142
+ * namespace with no row returns 0 results even when entries exist.
1143
+ *
1144
+ * This is idempotent and conservative:
1145
+ * - Does NOTHING (no writes) when the table already exists and every embedded
1146
+ * namespace already has a row — the common already-healed path, hit on
1147
+ * every MCP start, must not write to the live DB unnecessarily.
1148
+ * - Before ANY write, runs `PRAGMA quick_check`; if the DB reports structural
1149
+ * corruption it SKIPS the repair entirely (returns `corrupt:true`) rather
1150
+ * than writing into a malformed btree and risking making it worse. The
1151
+ * caller/user should recover via `sqlite3 old.db .recover | sqlite3 new.db`.
1152
+ * - Does NOT checkpoint. mode=ro readers already see committed WAL frames, and
1153
+ * forcing a checkpoint on a DB with a torn WAL could persist latent damage.
1154
+ *
1155
+ * When a repair IS needed and the DB is healthy: creates the table if absent,
1156
+ * seeds the fresh-install default rows, and backfills an accurate
1157
+ * `total_vectors` per namespace. Runs on the existing-DB path of
1158
+ * `initializeMemoryDatabase` (MCP start / `memory init`) and from `ruflo init`.
1159
+ *
1160
+ * Uses better-sqlite3 (WAL-safe, native). If the native module is unavailable
1161
+ * it is a silent no-op — the split statusline query already prevents the count
1162
+ * from zeroing; only the HNSW flag and namespace routing stay degraded.
1163
+ */
1164
+ /**
1165
+ * Auto-recover a structurally-corrupt memory DB into a clean one, universally
1166
+ * (better-sqlite3 only — no dependency on the external `sqlite3` CLI, which is
1167
+ * absent on many npx hosts). Safe by construction:
1168
+ * 1. Confirms corruption (quick_check) — no-op on a healthy DB.
1169
+ * 2. Acquires an EXCLUSIVE lock (BEGIN IMMEDIATE). If another process is
1170
+ * writing, it SKIPS (returns reason:'writer-active') rather than racing a
1171
+ * writer and losing its in-flight writes — the mistake that must not recur.
1172
+ * 3. Rebuilds a fresh DB table-by-table (schema + rows), skipping any single
1173
+ * table whose pages won't scan so one bad table can't abort the whole
1174
+ * rebuild.
1175
+ * 4. VERIFIES the rebuild (integrity_check == ok AND recovered
1176
+ * memory_entries count >= the readable source count) BEFORE touching the
1177
+ * original.
1178
+ * 5. Backs up the corrupt DB to `<db>.corrupt-<ts>.bak`, then atomically
1179
+ * renames the verified rebuild into place and drops stale -wal/-shm.
1180
+ * On any failure the original + backup are left intact — never destructive.
1181
+ */
1182
+ export async function recoverMemoryDatabase(dbPath, opts = {}) {
1183
+ if (!dbPath || !fs.existsSync(dbPath))
1184
+ return { recovered: false, reason: 'no-db' };
1185
+ let Database;
1186
+ try {
1187
+ // Module name behind a variable so TS does not statically resolve the
1188
+ // optional native dep's types at build time (CI may not install them).
1189
+ const mod = 'better-sqlite3';
1190
+ Database = (await import(mod)).default;
1191
+ }
1192
+ catch {
1193
+ return { recovered: false, reason: 'no-native' };
1194
+ }
1195
+ const ts = Date.now();
1196
+ const tmpPath = `${dbPath}.recovering-${ts}`;
1197
+ const bakPath = `${dbPath}.corrupt-${ts}.bak`;
1198
+ let src;
1199
+ let dst;
1200
+ try {
1201
+ src = new Database(dbPath, { timeout: 1500 });
1202
+ // Confirm corruption — never rewrite a healthy DB.
1203
+ const qc = src.prepare('PRAGMA quick_check(1)').get();
1204
+ const qcVal = qc ? String(Object.values(qc)[0] ?? '') : '';
1205
+ if (qcVal.toLowerCase() === 'ok') {
1206
+ src.close();
1207
+ return { recovered: false, reason: 'not-corrupt' };
1208
+ }
1209
+ // Exclusive-writer guard: acquire the write lock. If busy, another process
1210
+ // is writing — do NOT race it. Reading within this txn is still allowed.
1211
+ try {
1212
+ src.exec('BEGIN IMMEDIATE');
1213
+ }
1214
+ catch {
1215
+ src.close();
1216
+ return { recovered: false, reason: 'writer-active' };
1217
+ }
1218
+ let srcRows = 0;
1219
+ try {
1220
+ srcRows = src.prepare('SELECT COUNT(*) AS c FROM memory_entries').get()?.c ?? 0;
1221
+ }
1222
+ catch { /* unreadable */ }
1223
+ try {
1224
+ fs.rmSync(tmpPath, { force: true });
1225
+ }
1226
+ catch { /* fresh */ }
1227
+ dst = new Database(tmpPath);
1228
+ // Copy schema: tables first (so data can be inserted), then indexes/triggers.
1229
+ const objects = src
1230
+ .prepare("SELECT type, name, sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'")
1231
+ .all();
1232
+ const tables = objects.filter(o => o.type === 'table');
1233
+ const others = objects.filter(o => o.type !== 'table');
1234
+ for (const t of tables) {
1235
+ try {
1236
+ dst.exec(t.sql);
1237
+ }
1238
+ catch { /* skip an untranslatable table def */ }
1239
+ }
1240
+ // Copy rows table-by-table; a table whose pages won't scan is skipped whole.
1241
+ let copiedEntries = 0;
1242
+ for (const t of tables) {
1243
+ try {
1244
+ const cols = dst.prepare(`PRAGMA table_info("${t.name}")`).all().map(c => c.name);
1245
+ if (!cols.length)
1246
+ continue;
1247
+ const colList = cols.map(c => `"${c}"`).join(',');
1248
+ const placeholders = cols.map(() => '?').join(',');
1249
+ const insert = dst.prepare(`INSERT OR IGNORE INTO "${t.name}" (${colList}) VALUES (${placeholders})`);
1250
+ const rows = src.prepare(`SELECT ${colList} FROM "${t.name}"`).all();
1251
+ const runAll = dst.transaction((rs) => {
1252
+ for (const r of rs)
1253
+ insert.run(cols.map(c => r[c]));
1254
+ });
1255
+ runAll(rows);
1256
+ if (t.name === 'memory_entries')
1257
+ copiedEntries = rows.length;
1258
+ }
1259
+ catch { /* skip a table whose data pages are unreadable */ }
1260
+ }
1261
+ for (const o of others) {
1262
+ try {
1263
+ dst.exec(o.sql);
1264
+ }
1265
+ catch { /* an index over corrupt data — non-fatal */ }
1266
+ }
1267
+ dst.close();
1268
+ dst = null;
1269
+ try {
1270
+ src.exec('ROLLBACK');
1271
+ }
1272
+ catch { /* ignore */ }
1273
+ src.close();
1274
+ src = null;
1275
+ // Verify BEFORE touching the original.
1276
+ const check = new Database(tmpPath, { readonly: true });
1277
+ const integ = String(check.pragma('integrity_check', { simple: true }) ?? '');
1278
+ let dstRows = 0;
1279
+ try {
1280
+ dstRows = check.prepare('SELECT COUNT(*) AS c FROM memory_entries').get()?.c ?? 0;
1281
+ }
1282
+ catch { /* */ }
1283
+ check.close();
1284
+ if (integ.toLowerCase() !== 'ok' || dstRows < srcRows) {
1285
+ try {
1286
+ fs.rmSync(tmpPath, { force: true });
1287
+ }
1288
+ catch { /* */ }
1289
+ if (opts.verbose) {
1290
+ console.log(`memory DB auto-recovery aborted (integrity=${integ}, rows ${dstRows}/${srcRows}) — original untouched`);
1291
+ }
1292
+ return { recovered: false, reason: 'verify-failed' };
1293
+ }
1294
+ // Back up the corrupt DB, then atomically swap in the verified rebuild.
1295
+ fs.copyFileSync(dbPath, bakPath);
1296
+ fs.renameSync(tmpPath, dbPath);
1297
+ for (const s of ['-wal', '-shm']) {
1298
+ try {
1299
+ fs.rmSync(`${dbPath}${s}`, { force: true });
1300
+ }
1301
+ catch { /* */ }
1302
+ }
1303
+ if (opts.verbose) {
1304
+ console.log(`memory DB auto-recovered: ${dstRows} rows, integrity ok. Corrupt original saved to ${bakPath}`);
1305
+ }
1306
+ return { recovered: true, backupPath: bakPath, rows: dstRows };
1307
+ }
1308
+ catch (e) {
1309
+ try {
1310
+ dst?.close();
1311
+ }
1312
+ catch { /* */ }
1313
+ try {
1314
+ src?.exec('ROLLBACK');
1315
+ }
1316
+ catch { /* */ }
1317
+ try {
1318
+ src?.close();
1319
+ }
1320
+ catch { /* */ }
1321
+ try {
1322
+ fs.rmSync(tmpPath, { force: true });
1323
+ }
1324
+ catch { /* */ }
1325
+ if (opts.verbose)
1326
+ console.log(`memory DB auto-recovery error: ${e?.message ?? e}`);
1327
+ return { recovered: false, reason: 'error' };
1328
+ }
1329
+ }
1330
+ export async function repairVectorIndexes(dbPath, opts = {}) {
1331
+ const res = { repaired: false, tableCreated: false, namespaces: [], corrupt: false };
1332
+ if (!dbPath || !fs.existsSync(dbPath))
1333
+ return res;
1334
+ let Database;
1335
+ try {
1336
+ // Module name behind a variable so TS does not statically resolve the
1337
+ // optional native dep's types at build time (CI may not install them).
1338
+ const mod = 'better-sqlite3';
1339
+ Database = (await import(mod)).default;
1340
+ }
1341
+ catch {
1342
+ // Native module absent (e.g. WASM-only host). Statusline fix still covers
1343
+ // the display; nothing to repair here.
1344
+ return res;
1345
+ }
1346
+ let db;
1347
+ try {
1348
+ db = new Database(dbPath, { timeout: 3000 });
1349
+ const tableExists = (name) => (db.prepare("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name=?").get(name)?.c ?? 0) > 0;
1350
+ // Nothing to key off if there is no entries table.
1351
+ if (!tableExists('memory_entries')) {
1352
+ db.close();
1353
+ return res;
1354
+ }
1355
+ const hasVectorIndexes = tableExists('vector_indexes');
1356
+ // Namespaces that actually have embeddings.
1357
+ const nsRows = db
1358
+ .prepare("SELECT COALESCE(namespace, 'default') AS ns, COUNT(*) AS c " +
1359
+ 'FROM memory_entries WHERE embedding IS NOT NULL GROUP BY ns')
1360
+ .all();
1361
+ // Which of those are already present in vector_indexes? If the table exists
1362
+ // and every embedded namespace already has a row, the DB is already healed
1363
+ // — return WITHOUT writing anything (common path on every MCP start).
1364
+ let needsWrite = !hasVectorIndexes;
1365
+ if (hasVectorIndexes) {
1366
+ const present = new Set(db.prepare('SELECT name FROM vector_indexes').all().map(r => r.name));
1367
+ needsWrite = nsRows.some(r => !present.has(String(r.ns || 'default')));
1368
+ }
1369
+ if (!needsWrite) {
1370
+ db.close();
1371
+ return res;
1372
+ }
1373
+ // A write is needed. GUARD: never write into a structurally corrupt DB —
1374
+ // that risks worsening the damage. quick_check is cheaper than a full
1375
+ // integrity_check and only runs on the rare repair path, not every start.
1376
+ const qc = db.prepare('PRAGMA quick_check(1)').get();
1377
+ const qcVal = qc ? String(Object.values(qc)[0] ?? '') : '';
1378
+ if (qcVal.toLowerCase() !== 'ok') {
1379
+ res.corrupt = true;
1380
+ db.close(); // release our handle before recovery may swap the file
1381
+ if (opts.autoRecover) {
1382
+ // Auto-fix: rebuild the corrupt DB (backup + verify + atomic swap), then
1383
+ // provision vector_indexes on the clean rebuild. This is what makes any
1384
+ // npx-deployed ruflo self-repair a corrupt memory DB on init / MCP start.
1385
+ const rec = await recoverMemoryDatabase(dbPath, { verbose: opts.verbose });
1386
+ if (rec.recovered) {
1387
+ const healed = await repairVectorIndexes(dbPath, { verbose: opts.verbose });
1388
+ return { ...healed, corrupt: true, recovered: true, backupPath: rec.backupPath };
1389
+ }
1390
+ if (opts.verbose) {
1391
+ console.log(`vector_indexes repair skipped — corruption (${qcVal}); auto-recovery not run (${rec.reason ?? 'unknown'})`);
1392
+ }
1393
+ }
1394
+ else if (opts.verbose) {
1395
+ console.log('vector_indexes repair SKIPPED — memory DB reports corruption (' + qcVal + '). ' +
1396
+ 'Recover with: sqlite3 <db> .recover | sqlite3 <db>.recovered');
1397
+ }
1398
+ return res;
1399
+ }
1400
+ if (!hasVectorIndexes) {
1401
+ db.exec(`CREATE TABLE IF NOT EXISTS vector_indexes (
1402
+ id TEXT PRIMARY KEY,
1403
+ name TEXT NOT NULL UNIQUE,
1404
+ dimensions INTEGER NOT NULL,
1405
+ metric TEXT DEFAULT 'cosine' CHECK(metric IN ('cosine', 'euclidean', 'dot')),
1406
+ hnsw_m INTEGER DEFAULT 16,
1407
+ hnsw_ef_construction INTEGER DEFAULT 200,
1408
+ hnsw_ef_search INTEGER DEFAULT 100,
1409
+ quantization_type TEXT CHECK(quantization_type IN ('none', 'scalar', 'product')),
1410
+ quantization_bits INTEGER DEFAULT 8,
1411
+ total_vectors INTEGER DEFAULT 0,
1412
+ last_rebuild_at INTEGER,
1413
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
1414
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
1415
+ )`);
1416
+ res.tableCreated = true;
1417
+ }
1418
+ // 384 = default ONNX model dim (Xenova/all-MiniLM-L6-v2); HNSW rejects
1419
+ // dim-mismatched inserts, so this must match the stored embeddings (#1947).
1420
+ const ensureRow = db.prepare('INSERT OR IGNORE INTO vector_indexes (id, name, dimensions) VALUES (?, ?, 384)');
1421
+ const setCount = db.prepare('UPDATE vector_indexes SET total_vectors = ?, updated_at = ? WHERE name = ?');
1422
+ const backfill = db.transaction(() => {
1423
+ // Parity with a fresh install's seed rows.
1424
+ ensureRow.run('default', 'default');
1425
+ ensureRow.run('patterns', 'patterns');
1426
+ const now = Date.now();
1427
+ for (const r of nsRows) {
1428
+ const ns = String(r.ns || 'default');
1429
+ ensureRow.run(ns, ns);
1430
+ setCount.run(r.c, now, ns);
1431
+ res.namespaces.push(ns);
1432
+ }
1433
+ });
1434
+ backfill();
1435
+ res.repaired = res.tableCreated || res.namespaces.length > 0;
1436
+ db.close();
1437
+ if (opts.verbose && res.repaired) {
1438
+ console.log(`vector_indexes ${res.tableCreated ? 'created' : 'refreshed'} — ` +
1439
+ `backfilled ${res.namespaces.length} namespace(s)`);
1440
+ }
1441
+ }
1442
+ catch (e) {
1443
+ try {
1444
+ db?.close();
1445
+ }
1446
+ catch { /* already closed */ }
1447
+ if (opts.verbose) {
1448
+ console.log(`vector_indexes repair skipped: ${e?.message ?? e}`);
1449
+ }
1450
+ }
1451
+ return res;
1452
+ }
1130
1453
  /**
1131
1454
  * Initialize the memory database properly using sql.js
1132
1455
  */
@@ -1155,19 +1478,24 @@ export async function initializeMemoryDatabase(options) {
1155
1478
  // surfaced an `[ERROR]` and a "Initialization failed" spinner even when
1156
1479
  // the existing DB was perfectly healthy.
1157
1480
  if (fs.existsSync(dbPath) && !force) {
1481
+ // #2568-followup: an existing DB may predate `vector_indexes` (or was
1482
+ // written by agentdb directly). Self-heal it here — this branch is hit on
1483
+ // every MCP-server start and `memory init`, so any ruflo repairs itself.
1484
+ // Idempotent + best-effort; never turns a healthy re-init into a failure.
1485
+ const heal = await repairVectorIndexes(dbPath, { verbose, autoRecover: true });
1158
1486
  return {
1159
1487
  success: true,
1160
1488
  alreadyExists: true,
1161
1489
  backend,
1162
1490
  dbPath,
1163
1491
  schemaVersion: '3.0.0',
1164
- tablesCreated: [],
1492
+ tablesCreated: heal.tableCreated ? ['vector_indexes'] : [],
1165
1493
  indexesCreated: [],
1166
1494
  features: {
1167
1495
  vectorEmbeddings: false,
1168
1496
  patternLearning: false,
1169
1497
  temporalDecay: false,
1170
- hnswIndexing: false,
1498
+ hnswIndexing: heal.repaired,
1171
1499
  migrationTracking: false
1172
1500
  }
1173
1501
  };
@@ -0,0 +1,111 @@
1
+ export type PromoteThreshold = 'execution-only' | 'execution+corroborated';
2
+ export interface TuningConfig {
3
+ batchSize: number;
4
+ dedupDistance: number;
5
+ promoteThreshold: PromoteThreshold;
6
+ }
7
+ export interface ParamGrid {
8
+ batchSize?: number[];
9
+ dedupDistance?: number[];
10
+ promoteThreshold?: PromoteThreshold[];
11
+ }
12
+ export interface TuningCandidate {
13
+ config: TuningConfig;
14
+ /** MRR@10 on the inner train-query split (never the true held-out set). */
15
+ trainScore: number;
16
+ trainRecallAt10: number;
17
+ trainQueryCount: number;
18
+ patternCount: number;
19
+ promotedCount: number;
20
+ distillMs: number;
21
+ /** Present iff M1 itself skipped this run (corrupt DB, missing tables, ...). */
22
+ skipped?: string;
23
+ }
24
+ export interface HeldOutScore {
25
+ mrrAt10: number;
26
+ recallAt10: number;
27
+ queryCount: number;
28
+ /** Same held-out query set scored against raw (undistilled) train entries. */
29
+ baselineMrrAt10: number;
30
+ baselineRecallAt10: number;
31
+ }
32
+ export interface TuningProvenance {
33
+ gridSize: number;
34
+ corpusSize: number;
35
+ trainSize: number;
36
+ heldOutSize: number;
37
+ metric: 'mrr@10';
38
+ /** Stamped from `options.now` (or the orchestrator's own Date.now() at the
39
+ * I/O boundary) — never read from inside pure scoring logic. */
40
+ tunedAt: number;
41
+ sourceDbPath: string;
42
+ sourceChecksumSha256: string;
43
+ }
44
+ export interface TuningReport {
45
+ candidates: TuningCandidate[];
46
+ winner: TuningCandidate;
47
+ heldOut: HeldOutScore;
48
+ /** True when held-out MRR@10 is >20% worse (relative) than the winner's train score. */
49
+ overfit: boolean;
50
+ provenance: TuningProvenance;
51
+ }
52
+ export interface TuneDistillationOptions {
53
+ /** Source DB — read (copied + hashed) only; NEVER opened with a DB connection. */
54
+ dbPath: string;
55
+ grid?: ParamGrid;
56
+ /** Namespaces to distill / include in the raw baseline pool (default: all). */
57
+ namespaces?: string[];
58
+ /** Outer train/held-out split fraction (default 0.8). */
59
+ trainFraction?: number;
60
+ /** Namespaces the query set is drawn from (default: feedback + commands). */
61
+ queryNamespaces?: string[];
62
+ topK?: number;
63
+ /** Timestamp stamped into `provenance.tunedAt`. Caller-supplied so the core
64
+ * logic stays a pure function of its inputs; defaults to `Date.now()` at
65
+ * this I/O boundary if omitted. */
66
+ now?: number;
67
+ tmpDir?: string;
68
+ verbose?: boolean;
69
+ }
70
+ export interface TimeSplit {
71
+ totalRows: number;
72
+ trainBoundaryRowid: number;
73
+ trainRowids: number[];
74
+ heldOutRowids: number[];
75
+ }
76
+ export interface TunedConfigFile {
77
+ batchSize: number;
78
+ dedupDistance: number;
79
+ promoteThreshold: PromoteThreshold;
80
+ provenance: TuningProvenance & {
81
+ winnerTrainScore: number;
82
+ heldOutScore: number;
83
+ baselineHeldOutScore: number;
84
+ overfit: boolean;
85
+ };
86
+ }
87
+ export declare const DEFAULT_GRID_BATCH_SIZE: number[];
88
+ export declare const DEFAULT_GRID_DEDUP_DISTANCE: number[];
89
+ export declare const DEFAULT_GRID_PROMOTE_THRESHOLD: PromoteThreshold[];
90
+ /**
91
+ * Grid-search the distillation config against isolated copies of `dbPath`,
92
+ * scored on a held-out split. See module header for the full methodology.
93
+ */
94
+ export declare function tuneDistillation(options: TuneDistillationOptions): Promise<TuningReport>;
95
+ /** Shape the winning config + provenance for persistence. Pure — no I/O. */
96
+ export declare function buildTunedConfigFile(report: TuningReport): TunedConfigFile;
97
+ /** Write the winning config to disk. Caller decides whether/where to call this. */
98
+ export declare function writeTunedConfigFile(report: TuningReport, filePath: string): void;
99
+ /** Where the tuned config lives by default, for daemon/CLI callers. */
100
+ export declare function defaultTunedConfigPath(cwd?: string): string;
101
+ /**
102
+ * Split a table's rows by rowid (a monotonic proxy for insertion time) into
103
+ * an earliest `trainFraction` chunk and a most-recent remainder. Pure given a
104
+ * fixed db snapshot. `table`/`extraWhere` are only ever called with internal
105
+ * literal strings (never external input) — not a SQL-injection surface.
106
+ */
107
+ export declare function computeTimeSplit(db: unknown, trainFraction: number, opts?: {
108
+ table?: string;
109
+ extraWhere?: string;
110
+ }): TimeSplit;
111
+ //# sourceMappingURL=distill-tuning.d.ts.map