akm-cli 0.9.0-beta.45 → 0.9.0-beta.47

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 (81) hide show
  1. package/dist/assets/wiki/ingest-workflow-template.md +17 -10
  2. package/dist/cli/shared.js +28 -0
  3. package/dist/cli.js +1 -2
  4. package/dist/commands/env/env-cli.js +16 -24
  5. package/dist/commands/env/secret-cli.js +12 -20
  6. package/dist/commands/graph/graph-cli.js +5 -13
  7. package/dist/commands/graph/graph.js +3 -3
  8. package/dist/commands/improve/consolidate/chunking.js +141 -0
  9. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  10. package/dist/commands/improve/consolidate/merge.js +145 -0
  11. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  12. package/dist/commands/improve/consolidate/types.js +4 -0
  13. package/dist/commands/improve/consolidate.js +20 -571
  14. package/dist/commands/improve/distill.js +5 -9
  15. package/dist/commands/improve/eligibility.js +434 -0
  16. package/dist/commands/improve/extract-cli.js +9 -1
  17. package/dist/commands/improve/extract.js +5 -19
  18. package/dist/commands/improve/improve-auto-accept.js +4 -8
  19. package/dist/commands/improve/improve-cli.js +35 -60
  20. package/dist/commands/improve/improve-result-file.js +5 -23
  21. package/dist/commands/improve/improve-session.js +58 -0
  22. package/dist/commands/improve/improve.js +107 -3606
  23. package/dist/commands/improve/locks.js +154 -0
  24. package/dist/commands/improve/loop-stages.js +1079 -0
  25. package/dist/commands/improve/preparation.js +1963 -0
  26. package/dist/commands/improve/recombine.js +6 -12
  27. package/dist/commands/improve/reflect.js +29 -34
  28. package/dist/commands/proposal/drain.js +25 -48
  29. package/dist/commands/proposal/proposal-cli.js +21 -31
  30. package/dist/commands/proposal/validators/proposals.js +3 -7
  31. package/dist/commands/read/curate.js +70 -14
  32. package/dist/commands/read/knowledge.js +2 -2
  33. package/dist/commands/sources/self-update.js +2 -2
  34. package/dist/commands/sources/stash-cli.js +9 -37
  35. package/dist/commands/tasks/tasks-cli.js +19 -27
  36. package/dist/commands/wiki-cli.js +21 -35
  37. package/dist/core/config/config.js +18 -2
  38. package/dist/core/events.js +3 -7
  39. package/dist/core/logs-db.js +6 -63
  40. package/dist/core/state/migrations.js +714 -0
  41. package/dist/core/state-db.js +28 -779
  42. package/dist/indexer/db/db.js +82 -216
  43. package/dist/indexer/indexer.js +11 -112
  44. package/dist/indexer/passes/dir-staleness.js +114 -0
  45. package/dist/indexer/search/search-source.js +10 -24
  46. package/dist/indexer/search/semantic-status.js +4 -0
  47. package/dist/integrations/agent/runner-dispatch.js +59 -0
  48. package/dist/llm/client.js +22 -11
  49. package/dist/llm/embedder.js +15 -0
  50. package/dist/llm/embedders/deterministic.js +66 -0
  51. package/dist/llm/graph-extract.js +28 -39
  52. package/dist/llm/memory-infer.js +34 -22
  53. package/dist/llm/metadata-enhance.js +35 -30
  54. package/dist/llm/structured-call.js +49 -0
  55. package/dist/output/shapes/passthrough.js +0 -1
  56. package/dist/registry/providers/skills-sh.js +21 -147
  57. package/dist/registry/providers/static-index.js +15 -157
  58. package/dist/registry/resolve.js +22 -9
  59. package/dist/scripts/migrate-storage.js +892 -1186
  60. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +214 -179
  61. package/dist/setup/setup.js +26 -5
  62. package/dist/sources/providers/filesystem.js +0 -1
  63. package/dist/sources/providers/git-install.js +206 -0
  64. package/dist/sources/providers/git-provider.js +234 -0
  65. package/dist/sources/providers/git-stash.js +248 -0
  66. package/dist/sources/providers/git.js +10 -671
  67. package/dist/sources/providers/npm.js +2 -6
  68. package/dist/sources/providers/sync-from-ref.js +9 -1
  69. package/dist/sources/providers/website.js +2 -3
  70. package/dist/sources/website-ingest.js +51 -9
  71. package/dist/sources/wiki-fetchers/registry.js +53 -0
  72. package/dist/sources/wiki-fetchers/youtube.js +185 -0
  73. package/dist/storage/database.js +45 -10
  74. package/dist/storage/managed-db.js +82 -0
  75. package/dist/storage/repositories/registry-cache.js +92 -0
  76. package/dist/tasks/runner.js +5 -13
  77. package/dist/workflows/runtime/runs.js +1 -117
  78. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  79. package/package.json +5 -5
  80. package/dist/commands/db-cli.js +0 -23
  81. package/dist/indexer/db/db-backup.js +0 -376
@@ -8751,11 +8751,15 @@ var init_paths = __esm(() => {
8751
8751
 
8752
8752
  // src/storage/database.ts
8753
8753
  import { createRequire } from "module";
8754
- function openDatabase(path5, opts) {
8755
- if (isBun) {
8756
- return openBunDatabase(path5, opts);
8754
+ function selectProvider() {
8755
+ const provider = PROVIDERS.find((p) => p.supported());
8756
+ if (!provider) {
8757
+ throw new Error(`No storage provider supports the current runtime (${isBun ? "Bun" : "Node"}).`);
8757
8758
  }
8758
- return openNodeDatabase(path5, opts);
8759
+ return provider;
8760
+ }
8761
+ function openDatabase(path5, opts) {
8762
+ return selectProvider().open(path5, opts);
8759
8763
  }
8760
8764
  function openBunDatabase(path5, opts) {
8761
8765
  const { Database: BunDatabase } = loadBunSqlite();
@@ -8778,7 +8782,16 @@ function loadBunSqlite() {
8778
8782
  }
8779
8783
  function loadBetterSqlite3() {
8780
8784
  if (!betterSqlite3Ctor) {
8781
- const mod = nodeRequire("better-sqlite3");
8785
+ let mod;
8786
+ try {
8787
+ mod = nodeRequire("better-sqlite3");
8788
+ } catch (err) {
8789
+ throw new Error(`akm could not load 'better-sqlite3', the SQLite driver it needs on Node.js.
8790
+ ` + ` \u2022 Reinstall akm with a working C/C++ build toolchain so its optional
8791
+ ` + " 'better-sqlite3' native binding rebuilds (a global `npm i -g better-sqlite3`\n" + ` will NOT be resolved \u2014 Node loads it from akm's own node_modules).
8792
+ ` + ` \u2022 Or run akm under Bun, which has a built-in SQLite driver and needs no native build.
8793
+ ` + ` Underlying load error: ${err instanceof Error ? err.message : String(err)}`);
8794
+ }
8782
8795
  betterSqlite3Ctor = mod.default ?? mod;
8783
8796
  }
8784
8797
  return betterSqlite3Ctor;
@@ -8793,36 +8806,23 @@ function openNodeDatabase(path5, opts) {
8793
8806
  const db = opts ? new BetterSqlite3(path5, options) : new BetterSqlite3(path5);
8794
8807
  return db;
8795
8808
  }
8796
- var isBun, nodeRequire, bunSqliteModule, betterSqlite3Ctor;
8809
+ var isBun, nodeRequire, bunSqliteProvider, nodeSqliteProvider, PROVIDERS, bunSqliteModule, betterSqlite3Ctor;
8797
8810
  var init_database = __esm(() => {
8798
8811
  isBun = !!process.versions?.bun;
8799
8812
  nodeRequire = createRequire(import.meta.url);
8813
+ bunSqliteProvider = {
8814
+ name: "bun:sqlite",
8815
+ supported: () => isBun,
8816
+ open: openBunDatabase
8817
+ };
8818
+ nodeSqliteProvider = {
8819
+ name: "better-sqlite3",
8820
+ supported: () => !isBun,
8821
+ open: openNodeDatabase
8822
+ };
8823
+ PROVIDERS = [bunSqliteProvider, nodeSqliteProvider];
8800
8824
  });
8801
8825
 
8802
- // src/storage/engines/sqlite-migrations.ts
8803
- function ensureMigrationsTable(db) {
8804
- db.exec(`
8805
- CREATE TABLE IF NOT EXISTS schema_migrations (
8806
- id TEXT PRIMARY KEY,
8807
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
8808
- );
8809
- `);
8810
- }
8811
- function runMigrations(db, migrations, opts) {
8812
- ensureMigrationsTable(db);
8813
- opts?.bootstrap?.(db);
8814
- const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
8815
- const applied = new Set(appliedRows.map((r) => r.id));
8816
- for (const migration of migrations) {
8817
- if (applied.has(migration.id))
8818
- continue;
8819
- db.transaction(() => {
8820
- db.exec(migration.up);
8821
- db.prepare("INSERT INTO schema_migrations (id) VALUES (?)").run(migration.id);
8822
- })();
8823
- }
8824
- }
8825
-
8826
8826
  // src/runtime.ts
8827
8827
  import { createHash } from "crypto";
8828
8828
  import { createWriteStream, statfsSync } from "fs";
@@ -8920,6 +8920,46 @@ var init_sqlite_pragmas = __esm(() => {
8920
8920
  ]);
8921
8921
  });
8922
8922
 
8923
+ // src/storage/managed-db.ts
8924
+ import fs5 from "fs";
8925
+ import path5 from "path";
8926
+ function openManagedDatabase(spec) {
8927
+ const dir = path5.dirname(spec.path);
8928
+ if (!fs5.existsSync(dir)) {
8929
+ fs5.mkdirSync(dir, { recursive: true });
8930
+ }
8931
+ const db = openDatabase(spec.path);
8932
+ applyStandardPragmas(db, spec.pragmas ?? { dataDir: dir });
8933
+ spec.init?.(db);
8934
+ return db;
8935
+ }
8936
+ function withManagedDb(open, fn, opts) {
8937
+ if (opts?.borrowed) {
8938
+ return fn(opts.borrowed);
8939
+ }
8940
+ const db = open();
8941
+ try {
8942
+ return fn(db);
8943
+ } finally {
8944
+ db.close();
8945
+ }
8946
+ }
8947
+ async function withManagedDbAsync(open, fn, opts) {
8948
+ if (opts?.borrowed) {
8949
+ return fn(opts.borrowed);
8950
+ }
8951
+ const db = open();
8952
+ try {
8953
+ return await fn(db);
8954
+ } finally {
8955
+ db.close();
8956
+ }
8957
+ }
8958
+ var init_managed_db = __esm(() => {
8959
+ init_database();
8960
+ init_sqlite_pragmas();
8961
+ });
8962
+
8923
8963
  // src/core/assert.ts
8924
8964
  function assertNever(x, context) {
8925
8965
  let serialized;
@@ -8959,683 +8999,36 @@ function classifyImproveAction(mode) {
8959
8999
  }
8960
9000
  var init_improve_types = () => {};
8961
9001
 
8962
- // src/core/state-db.ts
8963
- var exports_state_db = {};
8964
- __export(exports_state_db, {
8965
- withImmediateTransaction: () => withImmediateTransaction,
8966
- upsertTaskHistory: () => upsertTaskHistory,
8967
- upsertProposal: () => upsertProposal,
8968
- upsertExtractedSession: () => upsertExtractedSession,
8969
- upsertConsolidationJudged: () => upsertConsolidationJudged,
8970
- upsertBodyEmbeddings: () => upsertBodyEmbeddings,
8971
- shouldSkipAlreadyExtractedSession: () => shouldSkipAlreadyExtractedSession,
8972
- runMigrations: () => runMigrations2,
8973
- recordRecombineInduction: () => recordRecombineInduction,
8974
- recordImproveRun: () => recordImproveRun,
8975
- recordFsProposalsImport: () => recordFsProposalsImport,
8976
- readStateEvents: () => readStateEvents,
8977
- queryTaskHistory: () => queryTaskHistory,
8978
- queryImproveRuns: () => queryImproveRuns,
8979
- queryCompletedTaskIntervals: () => queryCompletedTaskIntervals,
8980
- purgeOldImproveRuns: () => purgeOldImproveRuns,
8981
- purgeOldEvents: () => purgeOldEvents,
8982
- proposalToRowValues: () => proposalToRowValues,
8983
- proposalRowToProposal: () => proposalRowToProposal,
8984
- persistPhaseThreshold: () => persistPhaseThreshold,
8985
- openStateDatabase: () => openStateDatabase,
8986
- markRecombineHypothesisPromoted: () => markRecombineHypothesisPromoted,
8987
- listStateProposals: () => listStateProposals,
8988
- listStateProposalIdsByPrefix: () => listStateProposalIdsByPrefix,
8989
- listProposalGateDecisions: () => listProposalGateDecisions,
8990
- listExistingTableNames: () => listExistingTableNames,
8991
- insertProposalIfAbsent: () => insertProposalIfAbsent,
8992
- insertEvent: () => insertEvent,
8993
- importEventsJsonl: () => importEventsJsonl,
8994
- hasImportedFsProposals: () => hasImportedFsProposals,
8995
- getTaskHistoryRuns: () => getTaskHistoryRuns,
8996
- getTaskHistory: () => getTaskHistory,
8997
- getStateProposal: () => getStateProposal,
8998
- getStateDbPath: () => getStateDbPath,
8999
- getRecombineHypothesis: () => getRecombineHypothesis,
9000
- getPhaseThreshold: () => getPhaseThreshold,
9001
- getLastExtractRunAt: () => getLastExtractRunAt,
9002
- getExtractedSessionsMap: () => getExtractedSessionsMap,
9003
- getExtractedSession: () => getExtractedSession,
9004
- getConsolidationJudgedMap: () => getConsolidationJudgedMap,
9005
- getBodyEmbeddings: () => getBodyEmbeddings,
9006
- findMatchingRecombineHypothesis: () => findMatchingRecombineHypothesis,
9007
- eventRowToEnvelope: () => eventRowToEnvelope,
9008
- embeddingToBlob: () => embeddingToBlob,
9009
- decayUnseenRecombineHypotheses: () => decayUnseenRecombineHypotheses,
9010
- computeImproveRunMetrics: () => computeImproveRunMetrics,
9011
- blobToEmbedding: () => blobToEmbedding,
9012
- REGISTRY_INDEX_CACHE_DDL: () => REGISTRY_INDEX_CACHE_DDL
9013
- });
9014
- import fs5 from "fs";
9015
- import path5 from "path";
9016
- function getStateDbPath() {
9017
- return path5.join(getDataDir(), "state.db");
9002
+ // src/storage/engines/sqlite-migrations.ts
9003
+ function ensureMigrationsTable(db) {
9004
+ db.exec(`
9005
+ CREATE TABLE IF NOT EXISTS schema_migrations (
9006
+ id TEXT PRIMARY KEY,
9007
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
9008
+ );
9009
+ `);
9018
9010
  }
9019
- function openStateDatabase(dbPath) {
9020
- const resolvedPath = dbPath ?? getStateDbPath();
9021
- const dir = path5.dirname(resolvedPath);
9022
- if (!fs5.existsSync(dir)) {
9023
- fs5.mkdirSync(dir, { recursive: true });
9011
+ function runMigrations(db, migrations, opts) {
9012
+ ensureMigrationsTable(db);
9013
+ opts?.bootstrap?.(db);
9014
+ const appliedRows = db.prepare("SELECT id FROM schema_migrations").all();
9015
+ const applied = new Set(appliedRows.map((r) => r.id));
9016
+ for (const migration of migrations) {
9017
+ if (applied.has(migration.id))
9018
+ continue;
9019
+ db.transaction(() => {
9020
+ db.exec(migration.up);
9021
+ db.prepare("INSERT INTO schema_migrations (id) VALUES (?)").run(migration.id);
9022
+ })();
9024
9023
  }
9025
- const db = openDatabase(resolvedPath);
9026
- applyStandardPragmas(db, { dataDir: dir });
9027
- runMigrations2(db);
9028
- return db;
9029
9024
  }
9025
+
9026
+ // src/core/state/migrations.ts
9030
9027
  function runMigrations2(db) {
9031
9028
  runMigrations(db, MIGRATIONS);
9032
9029
  }
9033
- function eventRowToEnvelope(row) {
9034
- let metadata;
9035
- try {
9036
- const parsed = JSON.parse(row.metadata_json);
9037
- if (Object.keys(parsed).length > 0) {
9038
- metadata = parsed;
9039
- }
9040
- } catch {}
9041
- return {
9042
- schemaVersion: 1,
9043
- id: row.id,
9044
- ts: row.ts,
9045
- eventType: row.event_type,
9046
- ...row.ref !== null ? { ref: row.ref } : {},
9047
- ...metadata !== undefined ? { metadata } : {}
9048
- };
9049
- }
9050
- function proposalRowToProposal(row) {
9051
- let frontmatter;
9052
- if (row.frontmatter_json) {
9053
- try {
9054
- frontmatter = JSON.parse(row.frontmatter_json);
9055
- } catch {}
9056
- }
9057
- let meta = {};
9058
- try {
9059
- meta = JSON.parse(row.metadata_json);
9060
- } catch {}
9061
- return {
9062
- id: row.id,
9063
- ref: row.ref,
9064
- status: row.status,
9065
- source: row.source,
9066
- ...typeof meta.sourceRun === "string" ? { sourceRun: meta.sourceRun } : {},
9067
- createdAt: row.created_at,
9068
- updatedAt: row.updated_at,
9069
- payload: {
9070
- content: row.content,
9071
- ...frontmatter !== undefined ? { frontmatter } : {}
9072
- },
9073
- ...meta.review !== undefined ? { review: meta.review } : {},
9074
- ...typeof meta.confidence === "number" ? { confidence: meta.confidence } : {},
9075
- ...meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {},
9076
- ...typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {},
9077
- ...typeof meta.eligibilitySource === "string" ? { eligibilitySource: meta.eligibilitySource } : {}
9078
- };
9079
- }
9080
- function proposalToRowValues(proposal, stashDir) {
9081
- const metaObj = {};
9082
- if (proposal.sourceRun !== undefined)
9083
- metaObj.sourceRun = proposal.sourceRun;
9084
- if (proposal.review !== undefined)
9085
- metaObj.review = proposal.review;
9086
- if (proposal.confidence !== undefined)
9087
- metaObj.confidence = proposal.confidence;
9088
- if (proposal.gateDecision !== undefined)
9089
- metaObj.gateDecision = proposal.gateDecision;
9090
- if (proposal.backupContent !== undefined)
9091
- metaObj.backupContent = proposal.backupContent;
9092
- if (proposal.eligibilitySource !== undefined)
9093
- metaObj.eligibilitySource = proposal.eligibilitySource;
9094
- return {
9095
- id: proposal.id,
9096
- stash_dir: stashDir,
9097
- ref: proposal.ref,
9098
- status: proposal.status,
9099
- source: proposal.source,
9100
- created_at: proposal.createdAt,
9101
- updated_at: proposal.updatedAt,
9102
- content: proposal.payload.content,
9103
- frontmatter_json: proposal.payload.frontmatter ? JSON.stringify(proposal.payload.frontmatter) : null,
9104
- metadata_json: JSON.stringify(metaObj)
9105
- };
9106
- }
9107
- function insertEvent(db, input) {
9108
- try {
9109
- const result = db.prepare(`INSERT INTO events (event_type, ts, ref, metadata_json)
9110
- VALUES (?, ?, ?, ?)
9111
- RETURNING id`).get(input.eventType, input.ts, input.ref ?? null, JSON.stringify(input.metadata ?? {}));
9112
- return result?.id;
9113
- } catch (err) {
9114
- const message = err instanceof Error ? err.message : String(err);
9115
- error(`akm: state.db event insert failed (${message})`);
9116
- return;
9117
- }
9118
- }
9119
- function readStateEvents(db, options = {}) {
9120
- const conditions = [];
9121
- const params = [];
9122
- if (options.sinceId !== undefined && options.sinceId > 0) {
9123
- conditions.push("id > ?");
9124
- params.push(options.sinceId);
9125
- }
9126
- if (options.since) {
9127
- conditions.push("ts >= ?");
9128
- params.push(options.since);
9129
- }
9130
- if (options.type) {
9131
- conditions.push("event_type = ?");
9132
- params.push(options.type);
9133
- }
9134
- if (options.ref) {
9135
- conditions.push("ref = ?");
9136
- params.push(options.ref);
9137
- }
9138
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9139
- const rows = db.prepare(`SELECT id, event_type, ts, ref, metadata_json FROM events ${where} ORDER BY id ASC`).all(...params);
9140
- const events = rows.map(eventRowToEnvelope);
9141
- const nextId = events.length > 0 ? events[events.length - 1].id : options.sinceId ?? 0;
9142
- return { events, nextId };
9143
- }
9144
- function purgeOldEvents(db, retentionDays = 90) {
9145
- if (!Number.isFinite(retentionDays) || retentionDays <= 0)
9146
- return 0;
9147
- const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
9148
- const result = db.prepare("DELETE FROM events WHERE ts < ?").run(cutoff);
9149
- const changes = result.changes ?? 0;
9150
- return typeof changes === "bigint" ? Number(changes) : changes;
9151
- }
9152
- function upsertProposal(db, proposal, stashDir) {
9153
- const v = proposalToRowValues(proposal, stashDir);
9154
- db.prepare(`
9155
- INSERT INTO proposals
9156
- (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
9157
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9158
- ON CONFLICT(id) DO UPDATE SET
9159
- stash_dir = excluded.stash_dir,
9160
- ref = excluded.ref,
9161
- status = excluded.status,
9162
- source = excluded.source,
9163
- updated_at = excluded.updated_at,
9164
- content = excluded.content,
9165
- frontmatter_json = excluded.frontmatter_json,
9166
- metadata_json = excluded.metadata_json
9167
- `).run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
9168
- }
9169
- function listStateProposals(db, options = {}) {
9170
- const conditions = [];
9171
- const params = [];
9172
- if (options.stashDir) {
9173
- conditions.push("stash_dir = ?");
9174
- params.push(options.stashDir);
9175
- }
9176
- if (options.status) {
9177
- conditions.push("status = ?");
9178
- params.push(options.status);
9179
- }
9180
- if (options.ref) {
9181
- conditions.push("ref = ?");
9182
- params.push(options.ref);
9183
- }
9184
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9185
- const rows = db.prepare(`SELECT id, stash_dir, ref, status, source, created_at, updated_at,
9186
- content, frontmatter_json, metadata_json
9187
- FROM proposals ${where} ORDER BY created_at ASC, rowid ASC`).all(...params);
9188
- return rows.map(proposalRowToProposal);
9189
- }
9190
- function listProposalGateDecisions(db) {
9191
- const rows = db.prepare("SELECT metadata_json FROM proposals ORDER BY created_at ASC, rowid ASC").all();
9192
- const decisions = [];
9193
- for (const row of rows) {
9194
- let meta;
9195
- try {
9196
- meta = JSON.parse(row.metadata_json);
9197
- } catch {
9198
- continue;
9199
- }
9200
- const decision = meta.gateDecision;
9201
- if (decision && typeof decision === "object" && typeof decision.outcome === "string") {
9202
- decisions.push(decision);
9203
- }
9204
- }
9205
- decisions.sort((a, b) => new Date(a.decidedAt).getTime() - new Date(b.decidedAt).getTime());
9206
- return decisions;
9207
- }
9208
- function getPhaseThreshold(db, phase) {
9209
- const row = db.prepare("SELECT threshold FROM improve_gate_thresholds WHERE phase = ?").get(phase);
9210
- return row?.threshold;
9211
- }
9212
- function persistPhaseThreshold(db, phase, threshold) {
9213
- db.prepare(`INSERT OR REPLACE INTO improve_gate_thresholds (phase, threshold, updated_at)
9214
- VALUES (?, ?, ?)`).run(phase, Math.round(threshold), Date.now());
9215
- }
9216
- function getStateProposal(db, id, stashDir) {
9217
- const sql = `SELECT id, stash_dir, ref, status, source, created_at, updated_at,
9218
- content, frontmatter_json, metadata_json
9219
- FROM proposals WHERE id = ?${stashDir ? " AND stash_dir = ?" : ""}`;
9220
- const row = stashDir ? db.prepare(sql).get(id, stashDir) : db.prepare(sql).get(id);
9221
- return row ? proposalRowToProposal(row) : undefined;
9222
- }
9223
- function listStateProposalIdsByPrefix(db, stashDir, idPrefix) {
9224
- const escaped = idPrefix.replace(/[\\%_]/g, (ch) => `\\${ch}`);
9225
- const rows = db.prepare(`SELECT id FROM proposals
9226
- WHERE stash_dir = ? AND status = 'pending' AND id LIKE ? ESCAPE '\\'
9227
- ORDER BY id ASC`).all(stashDir, `${escaped}%`);
9228
- return rows.map((r) => r.id);
9229
- }
9230
- function hasImportedFsProposals(db, stashDir) {
9231
- return Boolean(db.prepare("SELECT 1 FROM proposal_fs_imports WHERE stash_dir = ?").get(stashDir));
9232
- }
9233
- function recordFsProposalsImport(db, stashDir, importedCount) {
9234
- db.prepare("INSERT OR REPLACE INTO proposal_fs_imports (stash_dir, imported_at, imported_count) VALUES (?, ?, ?)").run(stashDir, new Date().toISOString(), importedCount);
9235
- }
9236
- function insertProposalIfAbsent(db, proposal, stashDir) {
9237
- const v = proposalToRowValues(proposal, stashDir);
9238
- const result = db.prepare(`
9239
- INSERT OR IGNORE INTO proposals
9240
- (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
9241
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9242
- `).run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
9243
- const changes = result.changes ?? 0;
9244
- return Number(changes) > 0;
9245
- }
9246
- function withImmediateTransaction(db, fn) {
9247
- db.exec("BEGIN IMMEDIATE");
9248
- try {
9249
- const result = fn();
9250
- db.exec("COMMIT");
9251
- return result;
9252
- } catch (err) {
9253
- try {
9254
- db.exec("ROLLBACK");
9255
- } catch {}
9256
- throw err;
9257
- }
9258
- }
9259
- function upsertTaskHistory(db, row) {
9260
- db.prepare(`
9261
- INSERT OR IGNORE INTO task_history
9262
- (task_id, status, started_at, completed_at, failed_at, log_path,
9263
- target_kind, target_ref, metadata_json)
9264
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
9265
- `).run(row.task_id, row.status, row.started_at, row.completed_at ?? null, row.failed_at ?? null, row.log_path ?? null, row.target_kind ?? null, row.target_ref ?? null, row.metadata_json);
9266
- }
9267
- function getTaskHistory(db, taskId) {
9268
- return db.prepare(`SELECT id, task_id, status, started_at, completed_at, failed_at, log_path,
9269
- target_kind, target_ref, metadata_json
9270
- FROM task_history WHERE task_id = ? ORDER BY started_at DESC LIMIT 1`).get(taskId);
9271
- }
9272
- function getTaskHistoryRuns(db, taskId, limit = 50) {
9273
- return db.prepare(`SELECT id, task_id, status, started_at, completed_at, failed_at, log_path,
9274
- target_kind, target_ref, metadata_json
9275
- FROM task_history WHERE task_id = ? ORDER BY started_at DESC LIMIT ?`).all(taskId, limit);
9276
- }
9277
- function queryTaskHistory(db, options = {}) {
9278
- const conditions = [];
9279
- const params = [];
9280
- if (options.since) {
9281
- conditions.push("started_at >= ?");
9282
- params.push(options.since);
9283
- }
9284
- if (options.until) {
9285
- conditions.push("started_at <= ?");
9286
- params.push(options.until);
9287
- }
9288
- if (options.status) {
9289
- conditions.push("status = ?");
9290
- params.push(options.status);
9291
- }
9292
- if (options.targetKind) {
9293
- conditions.push("target_kind = ?");
9294
- params.push(options.targetKind);
9295
- }
9296
- if (options.targetRef) {
9297
- conditions.push("target_ref = ?");
9298
- params.push(options.targetRef);
9299
- }
9300
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9301
- return db.prepare(`SELECT task_id, status, started_at, completed_at, failed_at, log_path,
9302
- target_kind, target_ref, metadata_json
9303
- FROM task_history ${where} ORDER BY started_at DESC`).all(...params);
9304
- }
9305
- function queryCompletedTaskIntervals(db, since, until) {
9306
- const sql = until ? "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND started_at < ? AND completed_at IS NOT NULL ORDER BY started_at" : "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND completed_at IS NOT NULL ORDER BY started_at";
9307
- return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9308
- }
9309
- function listExistingTableNames(db, names) {
9310
- if (names.length === 0)
9311
- return [];
9312
- const placeholders = names.map(() => "?").join(", ");
9313
- return db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders}) ORDER BY name`).all(...names);
9314
- }
9315
- async function importEventsJsonl(db, jsonlPath) {
9316
- const { readFileSync, existsSync } = await import("fs");
9317
- if (!existsSync(jsonlPath)) {
9318
- return { imported: 0, maxId: 0, skipped: 0 };
9319
- }
9320
- const text = readFileSync(jsonlPath, "utf8");
9321
- const lines = text.split(`
9322
- `).filter((l) => l.trim().length > 0);
9323
- let imported = 0;
9324
- let maxId = 0;
9325
- let skipped = 0;
9326
- const insertStmt = db.prepare(`INSERT INTO events (event_type, ts, ref, metadata_json)
9327
- VALUES (?, ?, ?, ?)
9328
- RETURNING id`);
9329
- const existsStmt = db.prepare(`SELECT 1 FROM events
9330
- WHERE event_type = ?
9331
- AND ts = ?
9332
- AND ref IS ?
9333
- AND metadata_json = ?
9334
- LIMIT 1`);
9335
- db.transaction(() => {
9336
- for (const line of lines) {
9337
- let parsed;
9338
- try {
9339
- parsed = JSON.parse(line);
9340
- } catch {
9341
- continue;
9342
- }
9343
- const eventType = typeof parsed.eventType === "string" ? parsed.eventType : "unknown";
9344
- const ts = typeof parsed.ts === "string" ? parsed.ts : new Date().toISOString();
9345
- const ref = typeof parsed.ref === "string" ? parsed.ref : null;
9346
- const metadata = parsed.metadata !== undefined && typeof parsed.metadata === "object" ? JSON.stringify(parsed.metadata) : "{}";
9347
- const duplicate = existsStmt.get(eventType, ts, ref, metadata);
9348
- if (duplicate) {
9349
- skipped++;
9350
- continue;
9351
- }
9352
- const result = insertStmt.get(eventType, ts, ref, metadata);
9353
- if (result) {
9354
- imported++;
9355
- if (result.id > maxId)
9356
- maxId = result.id;
9357
- }
9358
- }
9359
- })();
9360
- return { imported, maxId, skipped };
9361
- }
9362
- function computeImproveRunMetrics(result) {
9363
- const plannedCount = Array.isArray(result.plannedRefs) ? result.plannedRefs.length : 0;
9364
- const actions = Array.isArray(result.actions) ? result.actions : [];
9365
- const actionsCount = actions.length;
9366
- let acceptedCount = 0;
9367
- let rejectedCount = 0;
9368
- let autoAcceptedCount = 0;
9369
- let errorCount = 0;
9370
- for (const action of actions) {
9371
- switch (classifyImproveAction(action.mode)) {
9372
- case "accepted":
9373
- acceptedCount++;
9374
- break;
9375
- case "rejected":
9376
- rejectedCount++;
9377
- break;
9378
- case "error":
9379
- errorCount++;
9380
- break;
9381
- case "noop":
9382
- break;
9383
- }
9384
- const r = action.result;
9385
- if (r && r.autoAccepted === true)
9386
- autoAcceptedCount++;
9387
- }
9388
- autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
9389
- return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
9390
- }
9391
- function recordImproveRun(db, input) {
9392
- const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
9393
- db.prepare(`
9394
- INSERT INTO improve_runs
9395
- (id, started_at, completed_at, stash_dir, dry_run, profile,
9396
- scope_mode, scope_value, guidance, ok, result_json, metrics_json, metadata_json)
9397
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9398
- `).run(input.id, input.startedAt, input.completedAt, input.stashDir, input.dryRun ? 1 : 0, input.profile, input.scopeMode, input.scopeValue, input.guidance, input.ok ? 1 : 0, JSON.stringify(input.result), JSON.stringify(metricsObj), JSON.stringify(input.metadata ?? {}));
9399
- }
9400
- function queryImproveRuns(db, since, until) {
9401
- const sql = until ? "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND started_at < ? AND dry_run = 0 ORDER BY started_at DESC" : "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND dry_run = 0 ORDER BY started_at DESC";
9402
- return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9403
- }
9404
- function purgeOldImproveRuns(db, retentionDays = 90) {
9405
- if (!Number.isFinite(retentionDays) || retentionDays <= 0)
9406
- return 0;
9407
- const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
9408
- const result = db.prepare("DELETE FROM improve_runs WHERE started_at < ?").run(cutoff);
9409
- const changes = result.changes ?? 0;
9410
- return typeof changes === "bigint" ? Number(changes) : changes;
9411
- }
9412
- function upsertExtractedSession(db, input) {
9413
- const endedAtIso = typeof input.sessionEndedAt === "number" && Number.isFinite(input.sessionEndedAt) ? new Date(input.sessionEndedAt).toISOString() : null;
9414
- db.prepare(`
9415
- INSERT OR REPLACE INTO extract_sessions_seen
9416
- (harness, session_id, processed_at, session_ended_at, outcome,
9417
- candidate_count, proposal_count, rationale, source_run, metadata_json,
9418
- content_hash)
9419
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9420
- `).run(input.harness, input.sessionId, input.processedAt, endedAtIso, input.outcome, input.candidateCount, input.proposalCount, input.rationale ?? null, input.sourceRun ?? null, JSON.stringify(input.metadata ?? {}), input.contentHash);
9421
- }
9422
- function getExtractedSession(db, harness, sessionId) {
9423
- const row = db.prepare("SELECT * FROM extract_sessions_seen WHERE harness = ? AND session_id = ?").get(harness, sessionId);
9424
- return row ?? undefined;
9425
- }
9426
- function getExtractedSessionsMap(db, harness, sessionIds) {
9427
- const out = new Map;
9428
- if (sessionIds.length === 0)
9429
- return out;
9430
- const CHUNK = 500;
9431
- for (let i = 0;i < sessionIds.length; i += CHUNK) {
9432
- const chunk = sessionIds.slice(i, i + CHUNK);
9433
- const placeholders = chunk.map(() => "?").join(",");
9434
- const rows = db.prepare(`SELECT * FROM extract_sessions_seen
9435
- WHERE harness = ? AND session_id IN (${placeholders})`).all(harness, ...chunk);
9436
- for (const row of rows)
9437
- out.set(row.session_id, row);
9438
- }
9439
- return out;
9440
- }
9441
- function getLastExtractRunAt(db, harness) {
9442
- const row = db.prepare("SELECT MAX(processed_at) AS last FROM extract_sessions_seen WHERE harness = ?").get(harness);
9443
- if (!row?.last)
9444
- return null;
9445
- const ms = Date.parse(row.last);
9446
- return Number.isFinite(ms) ? ms : null;
9447
- }
9448
- function shouldSkipAlreadyExtractedSession(prior, currentContentHash) {
9449
- if (!prior)
9450
- return false;
9451
- if (prior.content_hash == null)
9452
- return false;
9453
- return prior.content_hash === currentContentHash;
9454
- }
9455
- function getConsolidationJudgedMap(db, entryKeys) {
9456
- const out = new Map;
9457
- if (entryKeys.length === 0)
9458
- return out;
9459
- const CHUNK = 500;
9460
- for (let i = 0;i < entryKeys.length; i += CHUNK) {
9461
- const chunk = entryKeys.slice(i, i + CHUNK);
9462
- const placeholders = chunk.map(() => "?").join(",");
9463
- const rows = db.prepare(`SELECT * FROM consolidation_judged WHERE entry_key IN (${placeholders})`).all(...chunk);
9464
- for (const row of rows)
9465
- out.set(row.entry_key, row);
9466
- }
9467
- return out;
9468
- }
9469
- function upsertConsolidationJudged(db, input) {
9470
- db.prepare(`
9471
- INSERT OR REPLACE INTO consolidation_judged
9472
- (entry_key, content_hash, judged_at, outcome)
9473
- VALUES (?, ?, ?, ?)
9474
- `).run(input.entryKey, input.contentHash, input.judgedAt, input.outcome);
9475
- }
9476
- function recordRecombineInduction(db, input) {
9477
- const row = db.prepare(`
9478
- INSERT INTO recombine_hypotheses
9479
- (hypothesis_ref, signature, member_key, consecutive_count, first_seen_at, last_seen_at, last_run)
9480
- VALUES (?, ?, ?, 1, ?, ?, ?)
9481
- ON CONFLICT(hypothesis_ref) DO UPDATE SET
9482
- consecutive_count = consecutive_count + (CASE WHEN last_run IS excluded.last_run THEN 0 ELSE 1 END),
9483
- last_seen_at = excluded.last_seen_at,
9484
- last_run = excluded.last_run,
9485
- signature = excluded.signature,
9486
- member_key = excluded.member_key
9487
- RETURNING consecutive_count
9488
- `).get(input.hypothesisRef, input.signature, input.memberKey, input.seenAt, input.seenAt, input.run);
9489
- return row?.consecutive_count ?? 0;
9490
- }
9491
- function findMatchingRecombineHypothesis(db, input) {
9492
- const candidateMembers = new Set(input.memberKey.split("|").filter((m) => m.length > 0));
9493
- if (candidateMembers.size === 0)
9494
- return;
9495
- const rows = db.prepare("SELECT * FROM recombine_hypotheses WHERE signature = ? AND promoted_at IS NULL ORDER BY last_seen_at DESC").all(input.signature);
9496
- let best;
9497
- let bestOverlap = -1;
9498
- for (const row of rows) {
9499
- const rowMembers = row.member_key.split("|").filter((m) => m.length > 0);
9500
- if (rowMembers.length === 0)
9501
- continue;
9502
- let intersection = 0;
9503
- for (const m of rowMembers) {
9504
- if (candidateMembers.has(m))
9505
- intersection += 1;
9506
- }
9507
- const union = candidateMembers.size + rowMembers.length - intersection;
9508
- const overlap = union === 0 ? 0 : intersection / union;
9509
- if (overlap >= input.minOverlap && overlap > bestOverlap) {
9510
- best = row;
9511
- bestOverlap = overlap;
9512
- }
9513
- }
9514
- return best;
9515
- }
9516
- function getRecombineHypothesis(db, hypothesisRef) {
9517
- const row = db.prepare("SELECT * FROM recombine_hypotheses WHERE hypothesis_ref = ?").get(hypothesisRef);
9518
- return row ?? undefined;
9519
- }
9520
- function markRecombineHypothesisPromoted(db, hypothesisRef, promotedAt) {
9521
- db.prepare("UPDATE recombine_hypotheses SET promoted_at = ?, consecutive_count = 0 WHERE hypothesis_ref = ?").run(promotedAt, hypothesisRef);
9522
- }
9523
- function hypothesisMatchesAnyPresentCluster(row, presentClusters, minOverlap) {
9524
- const rowMembers = row.member_key.split("|").filter((m) => m.length > 0);
9525
- if (rowMembers.length === 0)
9526
- return false;
9527
- const rowSet = new Set(rowMembers);
9528
- for (const cluster of presentClusters) {
9529
- if (cluster.signature !== row.signature)
9530
- continue;
9531
- const clusterMembers = cluster.memberKey.split("|").filter((m) => m.length > 0);
9532
- if (clusterMembers.length === 0)
9533
- continue;
9534
- let intersection = 0;
9535
- for (const m of clusterMembers) {
9536
- if (rowSet.has(m))
9537
- intersection += 1;
9538
- }
9539
- const union = rowSet.size + clusterMembers.length - intersection;
9540
- const overlap = union === 0 ? 0 : intersection / union;
9541
- if (overlap >= minOverlap)
9542
- return true;
9543
- }
9544
- return false;
9545
- }
9546
- function decayUnseenRecombineHypotheses(db, currentRun, seenRefs, opts) {
9547
- let effectiveSeen = seenRefs;
9548
- if (opts && opts.presentClusters.length > 0) {
9549
- const candidates = db.prepare("SELECT hypothesis_ref, signature, member_key FROM recombine_hypotheses WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").all(currentRun);
9550
- const seenSet = new Set(seenRefs);
9551
- for (const row of candidates) {
9552
- if (seenSet.has(row.hypothesis_ref))
9553
- continue;
9554
- if (hypothesisMatchesAnyPresentCluster(row, opts.presentClusters, opts.minOverlap)) {
9555
- seenSet.add(row.hypothesis_ref);
9556
- }
9557
- }
9558
- effectiveSeen = [...seenSet];
9559
- }
9560
- return decayUnseenRecombineHypothesesInner(db, currentRun, effectiveSeen);
9561
- }
9562
- function decayUnseenRecombineHypothesesInner(db, currentRun, seenRefs) {
9563
- if (seenRefs.length === 0) {
9564
- const res2 = db.prepare("UPDATE recombine_hypotheses SET consecutive_count = 0 WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").run(currentRun);
9565
- return Number(res2.changes);
9566
- }
9567
- if (seenRefs.length > 900) {
9568
- const res2 = db.prepare("UPDATE recombine_hypotheses SET consecutive_count = 0 WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").run(currentRun);
9569
- return Number(res2.changes);
9570
- }
9571
- const placeholders = seenRefs.map(() => "?").join(",");
9572
- const res = db.prepare(`UPDATE recombine_hypotheses SET consecutive_count = 0
9573
- WHERE promoted_at IS NULL
9574
- AND (last_run IS NULL OR last_run != ?)
9575
- AND consecutive_count != 0
9576
- AND hypothesis_ref NOT IN (${placeholders})`).run(currentRun, ...seenRefs);
9577
- return Number(res.changes);
9578
- }
9579
- function embeddingToBlob(vec) {
9580
- const f32 = new Float32Array(vec);
9581
- return new Uint8Array(f32.buffer);
9582
- }
9583
- function blobToEmbedding(blob) {
9584
- const f32 = new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
9585
- return Array.from(f32);
9586
- }
9587
- function getBodyEmbeddings(db, contentHashes, expectedModelId) {
9588
- const out = new Map;
9589
- if (contentHashes.length === 0)
9590
- return out;
9591
- const firstRow = db.prepare("SELECT model_id FROM body_embeddings LIMIT 1").get();
9592
- if (firstRow && firstRow.model_id !== expectedModelId) {
9593
- db.exec("DELETE FROM body_embeddings");
9594
- return out;
9595
- }
9596
- const CHUNK = 500;
9597
- for (let i = 0;i < contentHashes.length; i += CHUNK) {
9598
- const chunk = contentHashes.slice(i, i + CHUNK);
9599
- const placeholders = chunk.map(() => "?").join(",");
9600
- const rows = db.prepare(`SELECT content_hash, embedding FROM body_embeddings WHERE content_hash IN (${placeholders})`).all(...chunk);
9601
- for (const row of rows) {
9602
- out.set(row.content_hash, blobToEmbedding(row.embedding));
9603
- }
9604
- }
9605
- return out;
9606
- }
9607
- function upsertBodyEmbeddings(db, entries) {
9608
- if (entries.length === 0)
9609
- return;
9610
- const now = Date.now();
9611
- const stmt = db.prepare(`
9612
- INSERT OR REPLACE INTO body_embeddings (content_hash, embedding, model_id, created_at)
9613
- VALUES (?, ?, ?, ?)
9614
- `);
9615
- db.transaction(() => {
9616
- for (const { contentHash, embedding, modelId } of entries) {
9617
- stmt.run(contentHash, embeddingToBlob(embedding), modelId, now);
9618
- }
9619
- })();
9620
- }
9621
- var MIGRATIONS, REGISTRY_INDEX_CACHE_DDL = `
9622
- CREATE TABLE IF NOT EXISTS registry_index_cache (
9623
- registry_url TEXT PRIMARY KEY,
9624
- fetched_at TEXT NOT NULL,
9625
- etag TEXT,
9626
- last_modified TEXT,
9627
- index_json TEXT NOT NULL DEFAULT '{}'
9628
- );
9629
-
9630
- CREATE INDEX IF NOT EXISTS idx_registry_cache_fetched
9631
- ON registry_index_cache(fetched_at);
9632
- `;
9633
- var init_state_db = __esm(() => {
9634
- init_database();
9635
- init_sqlite_pragmas();
9636
- init_improve_types();
9637
- init_paths();
9638
- init_warn();
9030
+ var MIGRATIONS;
9031
+ var init_migrations = __esm(() => {
9639
9032
  MIGRATIONS = [
9640
9033
  {
9641
9034
  id: "001-initial-schema",
@@ -10018,13 +9411,676 @@ var init_state_db = __esm(() => {
10018
9411
  ON recombine_hypotheses(last_seen_at);
10019
9412
  `
10020
9413
  },
10021
- {
10022
- id: "015-asset-salience-encoding-source",
10023
- up: `
10024
- ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
10025
- `
9414
+ {
9415
+ id: "015-asset-salience-encoding-source",
9416
+ up: `
9417
+ ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
9418
+ `
9419
+ }
9420
+ ];
9421
+ });
9422
+
9423
+ // src/core/state-db.ts
9424
+ var exports_state_db = {};
9425
+ __export(exports_state_db, {
9426
+ withStateDbAsync: () => withStateDbAsync,
9427
+ withStateDb: () => withStateDb,
9428
+ withImmediateTransaction: () => withImmediateTransaction,
9429
+ upsertTaskHistory: () => upsertTaskHistory,
9430
+ upsertProposal: () => upsertProposal,
9431
+ upsertExtractedSession: () => upsertExtractedSession,
9432
+ upsertConsolidationJudged: () => upsertConsolidationJudged,
9433
+ upsertBodyEmbeddings: () => upsertBodyEmbeddings,
9434
+ shouldSkipAlreadyExtractedSession: () => shouldSkipAlreadyExtractedSession,
9435
+ runMigrations: () => runMigrations2,
9436
+ recordRecombineInduction: () => recordRecombineInduction,
9437
+ recordImproveRun: () => recordImproveRun,
9438
+ recordFsProposalsImport: () => recordFsProposalsImport,
9439
+ readStateEvents: () => readStateEvents,
9440
+ queryTaskHistory: () => queryTaskHistory,
9441
+ queryImproveRuns: () => queryImproveRuns,
9442
+ queryCompletedTaskIntervals: () => queryCompletedTaskIntervals,
9443
+ purgeOldImproveRuns: () => purgeOldImproveRuns,
9444
+ purgeOldEvents: () => purgeOldEvents,
9445
+ proposalToRowValues: () => proposalToRowValues,
9446
+ proposalRowToProposal: () => proposalRowToProposal,
9447
+ persistPhaseThreshold: () => persistPhaseThreshold,
9448
+ openStateDatabase: () => openStateDatabase,
9449
+ markRecombineHypothesisPromoted: () => markRecombineHypothesisPromoted,
9450
+ listStateProposals: () => listStateProposals,
9451
+ listStateProposalIdsByPrefix: () => listStateProposalIdsByPrefix,
9452
+ listProposalGateDecisions: () => listProposalGateDecisions,
9453
+ listExistingTableNames: () => listExistingTableNames,
9454
+ insertProposalIfAbsent: () => insertProposalIfAbsent,
9455
+ insertEvent: () => insertEvent,
9456
+ importEventsJsonl: () => importEventsJsonl,
9457
+ hasImportedFsProposals: () => hasImportedFsProposals,
9458
+ getTaskHistoryRuns: () => getTaskHistoryRuns,
9459
+ getTaskHistory: () => getTaskHistory,
9460
+ getStateProposal: () => getStateProposal,
9461
+ getStateDbPath: () => getStateDbPath,
9462
+ getRecombineHypothesis: () => getRecombineHypothesis,
9463
+ getPhaseThreshold: () => getPhaseThreshold,
9464
+ getLastExtractRunAt: () => getLastExtractRunAt,
9465
+ getExtractedSessionsMap: () => getExtractedSessionsMap,
9466
+ getExtractedSession: () => getExtractedSession,
9467
+ getConsolidationJudgedMap: () => getConsolidationJudgedMap,
9468
+ getBodyEmbeddings: () => getBodyEmbeddings,
9469
+ findMatchingRecombineHypothesis: () => findMatchingRecombineHypothesis,
9470
+ eventRowToEnvelope: () => eventRowToEnvelope,
9471
+ embeddingToBlob: () => embeddingToBlob,
9472
+ decayUnseenRecombineHypotheses: () => decayUnseenRecombineHypotheses,
9473
+ computeImproveRunMetrics: () => computeImproveRunMetrics,
9474
+ blobToEmbedding: () => blobToEmbedding
9475
+ });
9476
+ import path6 from "path";
9477
+ function getStateDbPath() {
9478
+ return path6.join(getDataDir(), "state.db");
9479
+ }
9480
+ function openStateDatabase(dbPath) {
9481
+ return openManagedDatabase({ path: dbPath ?? getStateDbPath(), init: runMigrations2 });
9482
+ }
9483
+ function withStateDb(fn, opts) {
9484
+ return withManagedDb(() => openStateDatabase(opts?.path), fn, opts);
9485
+ }
9486
+ function withStateDbAsync(fn, opts) {
9487
+ return withManagedDbAsync(() => openStateDatabase(opts?.path), fn, opts);
9488
+ }
9489
+ function eventRowToEnvelope(row) {
9490
+ let metadata;
9491
+ try {
9492
+ const parsed = JSON.parse(row.metadata_json);
9493
+ if (Object.keys(parsed).length > 0) {
9494
+ metadata = parsed;
9495
+ }
9496
+ } catch {}
9497
+ return {
9498
+ schemaVersion: 1,
9499
+ id: row.id,
9500
+ ts: row.ts,
9501
+ eventType: row.event_type,
9502
+ ...row.ref !== null ? { ref: row.ref } : {},
9503
+ ...metadata !== undefined ? { metadata } : {}
9504
+ };
9505
+ }
9506
+ function proposalRowToProposal(row) {
9507
+ let frontmatter;
9508
+ if (row.frontmatter_json) {
9509
+ try {
9510
+ frontmatter = JSON.parse(row.frontmatter_json);
9511
+ } catch {}
9512
+ }
9513
+ let meta = {};
9514
+ try {
9515
+ meta = JSON.parse(row.metadata_json);
9516
+ } catch {}
9517
+ return {
9518
+ id: row.id,
9519
+ ref: row.ref,
9520
+ status: row.status,
9521
+ source: row.source,
9522
+ ...typeof meta.sourceRun === "string" ? { sourceRun: meta.sourceRun } : {},
9523
+ createdAt: row.created_at,
9524
+ updatedAt: row.updated_at,
9525
+ payload: {
9526
+ content: row.content,
9527
+ ...frontmatter !== undefined ? { frontmatter } : {}
9528
+ },
9529
+ ...meta.review !== undefined ? { review: meta.review } : {},
9530
+ ...typeof meta.confidence === "number" ? { confidence: meta.confidence } : {},
9531
+ ...meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {},
9532
+ ...typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {},
9533
+ ...typeof meta.eligibilitySource === "string" ? { eligibilitySource: meta.eligibilitySource } : {}
9534
+ };
9535
+ }
9536
+ function proposalToRowValues(proposal, stashDir) {
9537
+ const metaObj = {};
9538
+ if (proposal.sourceRun !== undefined)
9539
+ metaObj.sourceRun = proposal.sourceRun;
9540
+ if (proposal.review !== undefined)
9541
+ metaObj.review = proposal.review;
9542
+ if (proposal.confidence !== undefined)
9543
+ metaObj.confidence = proposal.confidence;
9544
+ if (proposal.gateDecision !== undefined)
9545
+ metaObj.gateDecision = proposal.gateDecision;
9546
+ if (proposal.backupContent !== undefined)
9547
+ metaObj.backupContent = proposal.backupContent;
9548
+ if (proposal.eligibilitySource !== undefined)
9549
+ metaObj.eligibilitySource = proposal.eligibilitySource;
9550
+ return {
9551
+ id: proposal.id,
9552
+ stash_dir: stashDir,
9553
+ ref: proposal.ref,
9554
+ status: proposal.status,
9555
+ source: proposal.source,
9556
+ created_at: proposal.createdAt,
9557
+ updated_at: proposal.updatedAt,
9558
+ content: proposal.payload.content,
9559
+ frontmatter_json: proposal.payload.frontmatter ? JSON.stringify(proposal.payload.frontmatter) : null,
9560
+ metadata_json: JSON.stringify(metaObj)
9561
+ };
9562
+ }
9563
+ function insertEvent(db, input) {
9564
+ try {
9565
+ const result = db.prepare(`INSERT INTO events (event_type, ts, ref, metadata_json)
9566
+ VALUES (?, ?, ?, ?)
9567
+ RETURNING id`).get(input.eventType, input.ts, input.ref ?? null, JSON.stringify(input.metadata ?? {}));
9568
+ return result?.id;
9569
+ } catch (err) {
9570
+ const message = err instanceof Error ? err.message : String(err);
9571
+ error(`akm: state.db event insert failed (${message})`);
9572
+ return;
9573
+ }
9574
+ }
9575
+ function readStateEvents(db, options = {}) {
9576
+ const conditions = [];
9577
+ const params = [];
9578
+ if (options.sinceId !== undefined && options.sinceId > 0) {
9579
+ conditions.push("id > ?");
9580
+ params.push(options.sinceId);
9581
+ }
9582
+ if (options.since) {
9583
+ conditions.push("ts >= ?");
9584
+ params.push(options.since);
9585
+ }
9586
+ if (options.type) {
9587
+ conditions.push("event_type = ?");
9588
+ params.push(options.type);
9589
+ }
9590
+ if (options.ref) {
9591
+ conditions.push("ref = ?");
9592
+ params.push(options.ref);
9593
+ }
9594
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9595
+ const rows = db.prepare(`SELECT id, event_type, ts, ref, metadata_json FROM events ${where} ORDER BY id ASC`).all(...params);
9596
+ const events = rows.map(eventRowToEnvelope);
9597
+ const nextId = events.length > 0 ? events[events.length - 1].id : options.sinceId ?? 0;
9598
+ return { events, nextId };
9599
+ }
9600
+ function purgeOldEvents(db, retentionDays = 90) {
9601
+ if (!Number.isFinite(retentionDays) || retentionDays <= 0)
9602
+ return 0;
9603
+ const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
9604
+ const result = db.prepare("DELETE FROM events WHERE ts < ?").run(cutoff);
9605
+ const changes = result.changes ?? 0;
9606
+ return typeof changes === "bigint" ? Number(changes) : changes;
9607
+ }
9608
+ function upsertProposal(db, proposal, stashDir) {
9609
+ const v = proposalToRowValues(proposal, stashDir);
9610
+ db.prepare(`
9611
+ INSERT INTO proposals
9612
+ (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
9613
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9614
+ ON CONFLICT(id) DO UPDATE SET
9615
+ stash_dir = excluded.stash_dir,
9616
+ ref = excluded.ref,
9617
+ status = excluded.status,
9618
+ source = excluded.source,
9619
+ updated_at = excluded.updated_at,
9620
+ content = excluded.content,
9621
+ frontmatter_json = excluded.frontmatter_json,
9622
+ metadata_json = excluded.metadata_json
9623
+ `).run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
9624
+ }
9625
+ function listStateProposals(db, options = {}) {
9626
+ const conditions = [];
9627
+ const params = [];
9628
+ if (options.stashDir) {
9629
+ conditions.push("stash_dir = ?");
9630
+ params.push(options.stashDir);
9631
+ }
9632
+ if (options.status) {
9633
+ conditions.push("status = ?");
9634
+ params.push(options.status);
9635
+ }
9636
+ if (options.ref) {
9637
+ conditions.push("ref = ?");
9638
+ params.push(options.ref);
9639
+ }
9640
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9641
+ const rows = db.prepare(`SELECT id, stash_dir, ref, status, source, created_at, updated_at,
9642
+ content, frontmatter_json, metadata_json
9643
+ FROM proposals ${where} ORDER BY created_at ASC, rowid ASC`).all(...params);
9644
+ return rows.map(proposalRowToProposal);
9645
+ }
9646
+ function listProposalGateDecisions(db) {
9647
+ const rows = db.prepare("SELECT metadata_json FROM proposals ORDER BY created_at ASC, rowid ASC").all();
9648
+ const decisions = [];
9649
+ for (const row of rows) {
9650
+ let meta;
9651
+ try {
9652
+ meta = JSON.parse(row.metadata_json);
9653
+ } catch {
9654
+ continue;
9655
+ }
9656
+ const decision = meta.gateDecision;
9657
+ if (decision && typeof decision === "object" && typeof decision.outcome === "string") {
9658
+ decisions.push(decision);
9659
+ }
9660
+ }
9661
+ decisions.sort((a, b) => new Date(a.decidedAt).getTime() - new Date(b.decidedAt).getTime());
9662
+ return decisions;
9663
+ }
9664
+ function getPhaseThreshold(db, phase) {
9665
+ const row = db.prepare("SELECT threshold FROM improve_gate_thresholds WHERE phase = ?").get(phase);
9666
+ return row?.threshold;
9667
+ }
9668
+ function persistPhaseThreshold(db, phase, threshold) {
9669
+ db.prepare(`INSERT OR REPLACE INTO improve_gate_thresholds (phase, threshold, updated_at)
9670
+ VALUES (?, ?, ?)`).run(phase, Math.round(threshold), Date.now());
9671
+ }
9672
+ function getStateProposal(db, id, stashDir) {
9673
+ const sql = `SELECT id, stash_dir, ref, status, source, created_at, updated_at,
9674
+ content, frontmatter_json, metadata_json
9675
+ FROM proposals WHERE id = ?${stashDir ? " AND stash_dir = ?" : ""}`;
9676
+ const row = stashDir ? db.prepare(sql).get(id, stashDir) : db.prepare(sql).get(id);
9677
+ return row ? proposalRowToProposal(row) : undefined;
9678
+ }
9679
+ function listStateProposalIdsByPrefix(db, stashDir, idPrefix) {
9680
+ const escaped = idPrefix.replace(/[\\%_]/g, (ch) => `\\${ch}`);
9681
+ const rows = db.prepare(`SELECT id FROM proposals
9682
+ WHERE stash_dir = ? AND status = 'pending' AND id LIKE ? ESCAPE '\\'
9683
+ ORDER BY id ASC`).all(stashDir, `${escaped}%`);
9684
+ return rows.map((r) => r.id);
9685
+ }
9686
+ function hasImportedFsProposals(db, stashDir) {
9687
+ return Boolean(db.prepare("SELECT 1 FROM proposal_fs_imports WHERE stash_dir = ?").get(stashDir));
9688
+ }
9689
+ function recordFsProposalsImport(db, stashDir, importedCount) {
9690
+ db.prepare("INSERT OR REPLACE INTO proposal_fs_imports (stash_dir, imported_at, imported_count) VALUES (?, ?, ?)").run(stashDir, new Date().toISOString(), importedCount);
9691
+ }
9692
+ function insertProposalIfAbsent(db, proposal, stashDir) {
9693
+ const v = proposalToRowValues(proposal, stashDir);
9694
+ const result = db.prepare(`
9695
+ INSERT OR IGNORE INTO proposals
9696
+ (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
9697
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9698
+ `).run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
9699
+ const changes = result.changes ?? 0;
9700
+ return Number(changes) > 0;
9701
+ }
9702
+ function withImmediateTransaction(db, fn) {
9703
+ db.exec("BEGIN IMMEDIATE");
9704
+ try {
9705
+ const result = fn();
9706
+ db.exec("COMMIT");
9707
+ return result;
9708
+ } catch (err) {
9709
+ try {
9710
+ db.exec("ROLLBACK");
9711
+ } catch {}
9712
+ throw err;
9713
+ }
9714
+ }
9715
+ function upsertTaskHistory(db, row) {
9716
+ db.prepare(`
9717
+ INSERT OR IGNORE INTO task_history
9718
+ (task_id, status, started_at, completed_at, failed_at, log_path,
9719
+ target_kind, target_ref, metadata_json)
9720
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
9721
+ `).run(row.task_id, row.status, row.started_at, row.completed_at ?? null, row.failed_at ?? null, row.log_path ?? null, row.target_kind ?? null, row.target_ref ?? null, row.metadata_json);
9722
+ }
9723
+ function getTaskHistory(db, taskId) {
9724
+ return db.prepare(`SELECT id, task_id, status, started_at, completed_at, failed_at, log_path,
9725
+ target_kind, target_ref, metadata_json
9726
+ FROM task_history WHERE task_id = ? ORDER BY started_at DESC LIMIT 1`).get(taskId);
9727
+ }
9728
+ function getTaskHistoryRuns(db, taskId, limit = 50) {
9729
+ return db.prepare(`SELECT id, task_id, status, started_at, completed_at, failed_at, log_path,
9730
+ target_kind, target_ref, metadata_json
9731
+ FROM task_history WHERE task_id = ? ORDER BY started_at DESC LIMIT ?`).all(taskId, limit);
9732
+ }
9733
+ function queryTaskHistory(db, options = {}) {
9734
+ const conditions = [];
9735
+ const params = [];
9736
+ if (options.since) {
9737
+ conditions.push("started_at >= ?");
9738
+ params.push(options.since);
9739
+ }
9740
+ if (options.until) {
9741
+ conditions.push("started_at <= ?");
9742
+ params.push(options.until);
9743
+ }
9744
+ if (options.status) {
9745
+ conditions.push("status = ?");
9746
+ params.push(options.status);
9747
+ }
9748
+ if (options.targetKind) {
9749
+ conditions.push("target_kind = ?");
9750
+ params.push(options.targetKind);
9751
+ }
9752
+ if (options.targetRef) {
9753
+ conditions.push("target_ref = ?");
9754
+ params.push(options.targetRef);
9755
+ }
9756
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9757
+ return db.prepare(`SELECT task_id, status, started_at, completed_at, failed_at, log_path,
9758
+ target_kind, target_ref, metadata_json
9759
+ FROM task_history ${where} ORDER BY started_at DESC`).all(...params);
9760
+ }
9761
+ function queryCompletedTaskIntervals(db, since, until) {
9762
+ const sql = until ? "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND started_at < ? AND completed_at IS NOT NULL ORDER BY started_at" : "SELECT started_at, completed_at FROM task_history WHERE task_id = 'akm-improve' AND started_at >= ? AND completed_at IS NOT NULL ORDER BY started_at";
9763
+ return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9764
+ }
9765
+ function listExistingTableNames(db, names) {
9766
+ if (names.length === 0)
9767
+ return [];
9768
+ const placeholders = names.map(() => "?").join(", ");
9769
+ return db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders}) ORDER BY name`).all(...names);
9770
+ }
9771
+ async function importEventsJsonl(db, jsonlPath) {
9772
+ const { readFileSync, existsSync } = await import("fs");
9773
+ if (!existsSync(jsonlPath)) {
9774
+ return { imported: 0, maxId: 0, skipped: 0 };
9775
+ }
9776
+ const text = readFileSync(jsonlPath, "utf8");
9777
+ const lines = text.split(`
9778
+ `).filter((l) => l.trim().length > 0);
9779
+ let imported = 0;
9780
+ let maxId = 0;
9781
+ let skipped = 0;
9782
+ const insertStmt = db.prepare(`INSERT INTO events (event_type, ts, ref, metadata_json)
9783
+ VALUES (?, ?, ?, ?)
9784
+ RETURNING id`);
9785
+ const existsStmt = db.prepare(`SELECT 1 FROM events
9786
+ WHERE event_type = ?
9787
+ AND ts = ?
9788
+ AND ref IS ?
9789
+ AND metadata_json = ?
9790
+ LIMIT 1`);
9791
+ db.transaction(() => {
9792
+ for (const line of lines) {
9793
+ let parsed;
9794
+ try {
9795
+ parsed = JSON.parse(line);
9796
+ } catch {
9797
+ continue;
9798
+ }
9799
+ const eventType = typeof parsed.eventType === "string" ? parsed.eventType : "unknown";
9800
+ const ts = typeof parsed.ts === "string" ? parsed.ts : new Date().toISOString();
9801
+ const ref = typeof parsed.ref === "string" ? parsed.ref : null;
9802
+ const metadata = parsed.metadata !== undefined && typeof parsed.metadata === "object" ? JSON.stringify(parsed.metadata) : "{}";
9803
+ const duplicate = existsStmt.get(eventType, ts, ref, metadata);
9804
+ if (duplicate) {
9805
+ skipped++;
9806
+ continue;
9807
+ }
9808
+ const result = insertStmt.get(eventType, ts, ref, metadata);
9809
+ if (result) {
9810
+ imported++;
9811
+ if (result.id > maxId)
9812
+ maxId = result.id;
9813
+ }
9814
+ }
9815
+ })();
9816
+ return { imported, maxId, skipped };
9817
+ }
9818
+ function computeImproveRunMetrics(result) {
9819
+ const plannedCount = Array.isArray(result.plannedRefs) ? result.plannedRefs.length : 0;
9820
+ const actions = Array.isArray(result.actions) ? result.actions : [];
9821
+ const actionsCount = actions.length;
9822
+ let acceptedCount = 0;
9823
+ let rejectedCount = 0;
9824
+ let autoAcceptedCount = 0;
9825
+ let errorCount = 0;
9826
+ for (const action of actions) {
9827
+ switch (classifyImproveAction(action.mode)) {
9828
+ case "accepted":
9829
+ acceptedCount++;
9830
+ break;
9831
+ case "rejected":
9832
+ rejectedCount++;
9833
+ break;
9834
+ case "error":
9835
+ errorCount++;
9836
+ break;
9837
+ case "noop":
9838
+ break;
9839
+ }
9840
+ const r = action.result;
9841
+ if (r && r.autoAccepted === true)
9842
+ autoAcceptedCount++;
9843
+ }
9844
+ autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
9845
+ return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
9846
+ }
9847
+ function recordImproveRun(db, input) {
9848
+ const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
9849
+ db.prepare(`
9850
+ INSERT INTO improve_runs
9851
+ (id, started_at, completed_at, stash_dir, dry_run, profile,
9852
+ scope_mode, scope_value, guidance, ok, result_json, metrics_json, metadata_json)
9853
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9854
+ `).run(input.id, input.startedAt, input.completedAt, input.stashDir, input.dryRun ? 1 : 0, input.profile, input.scopeMode, input.scopeValue, input.guidance, input.ok ? 1 : 0, JSON.stringify(input.result), JSON.stringify(metricsObj), JSON.stringify(input.metadata ?? {}));
9855
+ }
9856
+ function queryImproveRuns(db, since, until) {
9857
+ const sql = until ? "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND started_at < ? AND dry_run = 0 ORDER BY started_at DESC" : "SELECT id, started_at, completed_at, ok, scope_mode, scope_value, result_json FROM improve_runs WHERE started_at >= ? AND dry_run = 0 ORDER BY started_at DESC";
9858
+ return until ? db.prepare(sql).all(since, until) : db.prepare(sql).all(since);
9859
+ }
9860
+ function purgeOldImproveRuns(db, retentionDays = 90) {
9861
+ if (!Number.isFinite(retentionDays) || retentionDays <= 0)
9862
+ return 0;
9863
+ const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
9864
+ const result = db.prepare("DELETE FROM improve_runs WHERE started_at < ?").run(cutoff);
9865
+ const changes = result.changes ?? 0;
9866
+ return typeof changes === "bigint" ? Number(changes) : changes;
9867
+ }
9868
+ function upsertExtractedSession(db, input) {
9869
+ const endedAtIso = typeof input.sessionEndedAt === "number" && Number.isFinite(input.sessionEndedAt) ? new Date(input.sessionEndedAt).toISOString() : null;
9870
+ db.prepare(`
9871
+ INSERT OR REPLACE INTO extract_sessions_seen
9872
+ (harness, session_id, processed_at, session_ended_at, outcome,
9873
+ candidate_count, proposal_count, rationale, source_run, metadata_json,
9874
+ content_hash)
9875
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9876
+ `).run(input.harness, input.sessionId, input.processedAt, endedAtIso, input.outcome, input.candidateCount, input.proposalCount, input.rationale ?? null, input.sourceRun ?? null, JSON.stringify(input.metadata ?? {}), input.contentHash);
9877
+ }
9878
+ function getExtractedSession(db, harness, sessionId) {
9879
+ const row = db.prepare("SELECT * FROM extract_sessions_seen WHERE harness = ? AND session_id = ?").get(harness, sessionId);
9880
+ return row ?? undefined;
9881
+ }
9882
+ function getExtractedSessionsMap(db, harness, sessionIds) {
9883
+ const out = new Map;
9884
+ if (sessionIds.length === 0)
9885
+ return out;
9886
+ const CHUNK = 500;
9887
+ for (let i = 0;i < sessionIds.length; i += CHUNK) {
9888
+ const chunk = sessionIds.slice(i, i + CHUNK);
9889
+ const placeholders = chunk.map(() => "?").join(",");
9890
+ const rows = db.prepare(`SELECT * FROM extract_sessions_seen
9891
+ WHERE harness = ? AND session_id IN (${placeholders})`).all(harness, ...chunk);
9892
+ for (const row of rows)
9893
+ out.set(row.session_id, row);
9894
+ }
9895
+ return out;
9896
+ }
9897
+ function getLastExtractRunAt(db, harness) {
9898
+ const row = db.prepare("SELECT MAX(processed_at) AS last FROM extract_sessions_seen WHERE harness = ?").get(harness);
9899
+ if (!row?.last)
9900
+ return null;
9901
+ const ms = Date.parse(row.last);
9902
+ return Number.isFinite(ms) ? ms : null;
9903
+ }
9904
+ function shouldSkipAlreadyExtractedSession(prior, currentContentHash) {
9905
+ if (!prior)
9906
+ return false;
9907
+ if (prior.content_hash == null)
9908
+ return false;
9909
+ return prior.content_hash === currentContentHash;
9910
+ }
9911
+ function getConsolidationJudgedMap(db, entryKeys) {
9912
+ const out = new Map;
9913
+ if (entryKeys.length === 0)
9914
+ return out;
9915
+ const CHUNK = 500;
9916
+ for (let i = 0;i < entryKeys.length; i += CHUNK) {
9917
+ const chunk = entryKeys.slice(i, i + CHUNK);
9918
+ const placeholders = chunk.map(() => "?").join(",");
9919
+ const rows = db.prepare(`SELECT * FROM consolidation_judged WHERE entry_key IN (${placeholders})`).all(...chunk);
9920
+ for (const row of rows)
9921
+ out.set(row.entry_key, row);
9922
+ }
9923
+ return out;
9924
+ }
9925
+ function upsertConsolidationJudged(db, input) {
9926
+ db.prepare(`
9927
+ INSERT OR REPLACE INTO consolidation_judged
9928
+ (entry_key, content_hash, judged_at, outcome)
9929
+ VALUES (?, ?, ?, ?)
9930
+ `).run(input.entryKey, input.contentHash, input.judgedAt, input.outcome);
9931
+ }
9932
+ function recordRecombineInduction(db, input) {
9933
+ const row = db.prepare(`
9934
+ INSERT INTO recombine_hypotheses
9935
+ (hypothesis_ref, signature, member_key, consecutive_count, first_seen_at, last_seen_at, last_run)
9936
+ VALUES (?, ?, ?, 1, ?, ?, ?)
9937
+ ON CONFLICT(hypothesis_ref) DO UPDATE SET
9938
+ consecutive_count = consecutive_count + (CASE WHEN last_run IS excluded.last_run THEN 0 ELSE 1 END),
9939
+ last_seen_at = excluded.last_seen_at,
9940
+ last_run = excluded.last_run,
9941
+ signature = excluded.signature,
9942
+ member_key = excluded.member_key
9943
+ RETURNING consecutive_count
9944
+ `).get(input.hypothesisRef, input.signature, input.memberKey, input.seenAt, input.seenAt, input.run);
9945
+ return row?.consecutive_count ?? 0;
9946
+ }
9947
+ function findMatchingRecombineHypothesis(db, input) {
9948
+ const candidateMembers = new Set(input.memberKey.split("|").filter((m) => m.length > 0));
9949
+ if (candidateMembers.size === 0)
9950
+ return;
9951
+ const rows = db.prepare("SELECT * FROM recombine_hypotheses WHERE signature = ? AND promoted_at IS NULL ORDER BY last_seen_at DESC").all(input.signature);
9952
+ let best;
9953
+ let bestOverlap = -1;
9954
+ for (const row of rows) {
9955
+ const rowMembers = row.member_key.split("|").filter((m) => m.length > 0);
9956
+ if (rowMembers.length === 0)
9957
+ continue;
9958
+ let intersection = 0;
9959
+ for (const m of rowMembers) {
9960
+ if (candidateMembers.has(m))
9961
+ intersection += 1;
9962
+ }
9963
+ const union = candidateMembers.size + rowMembers.length - intersection;
9964
+ const overlap = union === 0 ? 0 : intersection / union;
9965
+ if (overlap >= input.minOverlap && overlap > bestOverlap) {
9966
+ best = row;
9967
+ bestOverlap = overlap;
10026
9968
  }
10027
- ];
9969
+ }
9970
+ return best;
9971
+ }
9972
+ function getRecombineHypothesis(db, hypothesisRef) {
9973
+ const row = db.prepare("SELECT * FROM recombine_hypotheses WHERE hypothesis_ref = ?").get(hypothesisRef);
9974
+ return row ?? undefined;
9975
+ }
9976
+ function markRecombineHypothesisPromoted(db, hypothesisRef, promotedAt) {
9977
+ db.prepare("UPDATE recombine_hypotheses SET promoted_at = ?, consecutive_count = 0 WHERE hypothesis_ref = ?").run(promotedAt, hypothesisRef);
9978
+ }
9979
+ function hypothesisMatchesAnyPresentCluster(row, presentClusters, minOverlap) {
9980
+ const rowMembers = row.member_key.split("|").filter((m) => m.length > 0);
9981
+ if (rowMembers.length === 0)
9982
+ return false;
9983
+ const rowSet = new Set(rowMembers);
9984
+ for (const cluster of presentClusters) {
9985
+ if (cluster.signature !== row.signature)
9986
+ continue;
9987
+ const clusterMembers = cluster.memberKey.split("|").filter((m) => m.length > 0);
9988
+ if (clusterMembers.length === 0)
9989
+ continue;
9990
+ let intersection = 0;
9991
+ for (const m of clusterMembers) {
9992
+ if (rowSet.has(m))
9993
+ intersection += 1;
9994
+ }
9995
+ const union = rowSet.size + clusterMembers.length - intersection;
9996
+ const overlap = union === 0 ? 0 : intersection / union;
9997
+ if (overlap >= minOverlap)
9998
+ return true;
9999
+ }
10000
+ return false;
10001
+ }
10002
+ function decayUnseenRecombineHypotheses(db, currentRun, seenRefs, opts) {
10003
+ let effectiveSeen = seenRefs;
10004
+ if (opts && opts.presentClusters.length > 0) {
10005
+ const candidates = db.prepare("SELECT hypothesis_ref, signature, member_key FROM recombine_hypotheses WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").all(currentRun);
10006
+ const seenSet = new Set(seenRefs);
10007
+ for (const row of candidates) {
10008
+ if (seenSet.has(row.hypothesis_ref))
10009
+ continue;
10010
+ if (hypothesisMatchesAnyPresentCluster(row, opts.presentClusters, opts.minOverlap)) {
10011
+ seenSet.add(row.hypothesis_ref);
10012
+ }
10013
+ }
10014
+ effectiveSeen = [...seenSet];
10015
+ }
10016
+ return decayUnseenRecombineHypothesesInner(db, currentRun, effectiveSeen);
10017
+ }
10018
+ function decayUnseenRecombineHypothesesInner(db, currentRun, seenRefs) {
10019
+ if (seenRefs.length === 0) {
10020
+ const res2 = db.prepare("UPDATE recombine_hypotheses SET consecutive_count = 0 WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").run(currentRun);
10021
+ return Number(res2.changes);
10022
+ }
10023
+ if (seenRefs.length > 900) {
10024
+ const res2 = db.prepare("UPDATE recombine_hypotheses SET consecutive_count = 0 WHERE promoted_at IS NULL AND (last_run IS NULL OR last_run != ?) AND consecutive_count != 0").run(currentRun);
10025
+ return Number(res2.changes);
10026
+ }
10027
+ const placeholders = seenRefs.map(() => "?").join(",");
10028
+ const res = db.prepare(`UPDATE recombine_hypotheses SET consecutive_count = 0
10029
+ WHERE promoted_at IS NULL
10030
+ AND (last_run IS NULL OR last_run != ?)
10031
+ AND consecutive_count != 0
10032
+ AND hypothesis_ref NOT IN (${placeholders})`).run(currentRun, ...seenRefs);
10033
+ return Number(res.changes);
10034
+ }
10035
+ function embeddingToBlob(vec) {
10036
+ const f32 = new Float32Array(vec);
10037
+ return new Uint8Array(f32.buffer);
10038
+ }
10039
+ function blobToEmbedding(blob) {
10040
+ const f32 = new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
10041
+ return Array.from(f32);
10042
+ }
10043
+ function getBodyEmbeddings(db, contentHashes, expectedModelId) {
10044
+ const out = new Map;
10045
+ if (contentHashes.length === 0)
10046
+ return out;
10047
+ const firstRow = db.prepare("SELECT model_id FROM body_embeddings LIMIT 1").get();
10048
+ if (firstRow && firstRow.model_id !== expectedModelId) {
10049
+ db.exec("DELETE FROM body_embeddings");
10050
+ return out;
10051
+ }
10052
+ const CHUNK = 500;
10053
+ for (let i = 0;i < contentHashes.length; i += CHUNK) {
10054
+ const chunk = contentHashes.slice(i, i + CHUNK);
10055
+ const placeholders = chunk.map(() => "?").join(",");
10056
+ const rows = db.prepare(`SELECT content_hash, embedding FROM body_embeddings WHERE content_hash IN (${placeholders})`).all(...chunk);
10057
+ for (const row of rows) {
10058
+ out.set(row.content_hash, blobToEmbedding(row.embedding));
10059
+ }
10060
+ }
10061
+ return out;
10062
+ }
10063
+ function upsertBodyEmbeddings(db, entries) {
10064
+ if (entries.length === 0)
10065
+ return;
10066
+ const now = Date.now();
10067
+ const stmt = db.prepare(`
10068
+ INSERT OR REPLACE INTO body_embeddings (content_hash, embedding, model_id, created_at)
10069
+ VALUES (?, ?, ?, ?)
10070
+ `);
10071
+ db.transaction(() => {
10072
+ for (const { contentHash, embedding, modelId } of entries) {
10073
+ stmt.run(contentHash, embeddingToBlob(embedding), modelId, now);
10074
+ }
10075
+ })();
10076
+ }
10077
+ var init_state_db = __esm(() => {
10078
+ init_managed_db();
10079
+ init_improve_types();
10080
+ init_paths();
10081
+ init_warn();
10082
+ init_migrations();
10083
+ init_migrations();
10028
10084
  });
10029
10085
 
10030
10086
  // src/core/best-effort.ts
@@ -10134,260 +10190,11 @@ function ensureUsageEventsSchema(db) {
10134
10190
  `);
10135
10191
  }
10136
10192
 
10137
- // src/indexer/db/db-backup.ts
10138
- import fs6 from "fs";
10139
- import path6 from "path";
10140
- function resolveRetention(env = process.env) {
10141
- const raw = env.AKM_DB_BACKUP_RETAIN?.trim();
10142
- if (!raw)
10143
- return DEFAULT_RETAIN;
10144
- const parsed = Number.parseInt(raw, 10);
10145
- if (Number.isNaN(parsed) || parsed < 1) {
10146
- warn("[akm] AKM_DB_BACKUP_RETAIN=%s is not a positive integer; falling back to %d", raw, DEFAULT_RETAIN);
10147
- return DEFAULT_RETAIN;
10148
- }
10149
- return parsed;
10150
- }
10151
- function isBackupDisabled(env = process.env) {
10152
- const raw = env.AKM_DB_BACKUP?.trim().toLowerCase();
10153
- if (!raw)
10154
- return false;
10155
- return raw === "0" || raw === "false" || raw === "no" || raw === "off";
10156
- }
10157
- function measureDataDirSize(dirPath) {
10158
- if (!fs6.existsSync(dirPath))
10159
- return 0;
10160
- let total = 0;
10161
- const stack = [dirPath];
10162
- while (stack.length > 0) {
10163
- const current = stack.pop();
10164
- if (current === undefined)
10165
- break;
10166
- let entries;
10167
- try {
10168
- entries = fs6.readdirSync(current, { withFileTypes: true });
10169
- } catch {
10170
- continue;
10171
- }
10172
- for (const entry of entries) {
10173
- const full = path6.join(current, entry.name);
10174
- if (current === dirPath && entry.name === BACKUPS_DIR_NAME && entry.isDirectory())
10175
- continue;
10176
- if (entry.isDirectory()) {
10177
- stack.push(full);
10178
- } else if (entry.isFile()) {
10179
- bestEffort(() => {
10180
- total += fs6.statSync(full).size;
10181
- }, "file vanished between readdir and stat");
10182
- }
10183
- }
10184
- }
10185
- return total;
10186
- }
10187
- function getFreeSpace(dirPath) {
10188
- try {
10189
- const stats = fs6.statfsSync;
10190
- if (!stats)
10191
- return null;
10192
- const res = stats(dirPath);
10193
- return Number(res.bavail * res.bsize);
10194
- } catch {
10195
- return null;
10196
- }
10197
- }
10198
- function formatTimestamp(d) {
10199
- return d.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "").replace(/-\d{3}$/, "");
10200
- }
10201
- function listBackups(dataDir) {
10202
- const backupsRoot = path6.join(dataDir, BACKUPS_DIR_NAME);
10203
- if (!fs6.existsSync(backupsRoot))
10204
- return [];
10205
- const entries = fs6.readdirSync(backupsRoot, { withFileTypes: true });
10206
- const results = [];
10207
- for (const entry of entries) {
10208
- if (!entry.isDirectory())
10209
- continue;
10210
- const full = path6.join(backupsRoot, entry.name);
10211
- const metaPath = path6.join(full, BACKUP_METADATA_FILE);
10212
- let createdAt;
10213
- let sourceVersion = null;
10214
- let sizeBytes;
10215
- let reason = DEFAULT_BACKUP_REASON;
10216
- if (fs6.existsSync(metaPath)) {
10217
- bestEffort(() => {
10218
- const raw = fs6.readFileSync(metaPath, "utf8");
10219
- const parsed = JSON.parse(raw);
10220
- if (typeof parsed.createdAt === "string")
10221
- createdAt = parsed.createdAt;
10222
- if (typeof parsed.sourceVersion === "number")
10223
- sourceVersion = parsed.sourceVersion;
10224
- else if (parsed.sourceVersion === null)
10225
- sourceVersion = null;
10226
- if (typeof parsed.sizeBytes === "number")
10227
- sizeBytes = parsed.sizeBytes;
10228
- if (typeof parsed.reason === "string" && parsed.reason.length > 0)
10229
- reason = parsed.reason;
10230
- }, "malformed backup metadata \u2014 fall back to filesystem-derived values");
10231
- }
10232
- if (!createdAt) {
10233
- try {
10234
- createdAt = fs6.statSync(full).mtime.toISOString();
10235
- } catch {
10236
- createdAt = new Date(0).toISOString();
10237
- }
10238
- }
10239
- if (sizeBytes === undefined) {
10240
- sizeBytes = measureDataDirSize(full);
10241
- }
10242
- results.push({
10243
- path: full,
10244
- name: entry.name,
10245
- createdAt,
10246
- sizeBytes,
10247
- sourceVersion,
10248
- reason
10249
- });
10250
- }
10251
- results.sort((a, b) => a.createdAt < b.createdAt ? 1 : a.createdAt > b.createdAt ? -1 : 0);
10252
- return results;
10253
- }
10254
- function pruneOldBackups(dataDir, retain) {
10255
- const existing = listBackups(dataDir);
10256
- if (existing.length <= retain)
10257
- return;
10258
- const toRemove = existing.slice(retain);
10259
- for (const entry of toRemove) {
10260
- try {
10261
- fs6.rmSync(entry.path, { recursive: true, force: true });
10262
- } catch (err) {
10263
- warn("[akm] failed to prune old backup %s \u2014 %s", entry.path, err instanceof Error ? err.message : String(err));
10264
- }
10265
- }
10266
- }
10267
- function backupDataDir(opts) {
10268
- const env = opts.env ?? process.env;
10269
- if (isBackupDisabled(env))
10270
- return null;
10271
- const { dataDir, sourceVersion, targetVersion } = opts;
10272
- const reason = opts.reason && opts.reason.length > 0 ? opts.reason : DEFAULT_BACKUP_REASON;
10273
- if (!fs6.existsSync(dataDir)) {
10274
- return null;
10275
- }
10276
- const dataDirStat = fs6.statSync(dataDir);
10277
- if (!dataDirStat.isDirectory()) {
10278
- warn("[akm] data dir backup skipped \u2014 %s is not a directory", dataDir);
10279
- return null;
10280
- }
10281
- const sourceSize = measureDataDirSize(dataDir);
10282
- if (sourceSize === 0)
10283
- return null;
10284
- const backupsRoot = path6.join(dataDir, BACKUPS_DIR_NAME);
10285
- try {
10286
- fs6.mkdirSync(backupsRoot, { recursive: true });
10287
- } catch (err) {
10288
- warn("[akm] data dir backup skipped \u2014 could not create %s: %s", backupsRoot, err instanceof Error ? err.message : String(err));
10289
- return null;
10290
- }
10291
- const free = getFreeSpace(backupsRoot);
10292
- if (free !== null && free < sourceSize * FREE_SPACE_MULTIPLIER) {
10293
- warn("[akm] data dir backup skipped \u2014 free space %d bytes is less than 1.1\xD7 source size %d bytes (need %d)", free, sourceSize, Math.ceil(sourceSize * FREE_SPACE_MULTIPLIER));
10294
- return null;
10295
- }
10296
- const now = (opts.now ?? (() => new Date))();
10297
- const stamp = formatTimestamp(now);
10298
- const dirSuffix = reason === DEFAULT_BACKUP_REASON ? `pre-v${targetVersion}` : reason;
10299
- const dirName = `${stamp}-${dirSuffix}`;
10300
- const destPath = path6.join(backupsRoot, dirName);
10301
- let finalDest = destPath;
10302
- let suffix = 1;
10303
- while (fs6.existsSync(finalDest)) {
10304
- finalDest = `${destPath}-${suffix}`;
10305
- suffix += 1;
10306
- }
10307
- try {
10308
- copyDataDirExcludingBackups(dataDir, finalDest);
10309
- } catch (err) {
10310
- warn("[akm] data dir backup failed \u2014 %s; upgrade will proceed without a snapshot", err instanceof Error ? err.message : String(err));
10311
- bestEffort(() => fs6.rmSync(finalDest, { recursive: true, force: true }), "cleanup partial backup copy");
10312
- return null;
10313
- }
10314
- const createdAt = now.toISOString();
10315
- const metadata = {
10316
- schemaVersion: 1,
10317
- createdAt,
10318
- sourceVersion,
10319
- targetVersion,
10320
- sizeBytes: sourceSize,
10321
- reason,
10322
- hostname: tryHostname(),
10323
- notes: reason === DEFAULT_BACKUP_REASON ? "Created by AKM before a destructive DB version upgrade. Restore manually by stopping akm and copying the contents back over the live data dir." : `Created by AKM before a destructive ${reason} operation. Restore manually by stopping akm and copying the contents back over the live data dir.`
10324
- };
10325
- try {
10326
- fs6.writeFileSync(path6.join(finalDest, BACKUP_METADATA_FILE), JSON.stringify(metadata, null, 2));
10327
- } catch (err) {
10328
- warn("[akm] data dir backup created at %s but metadata write failed \u2014 %s", finalDest, err instanceof Error ? err.message : String(err));
10329
- }
10330
- const retain = resolveRetention(env);
10331
- pruneOldBackups(dataDir, retain);
10332
- return {
10333
- path: finalDest,
10334
- name: path6.basename(finalDest),
10335
- createdAt,
10336
- sizeBytes: sourceSize,
10337
- sourceVersion,
10338
- targetVersion,
10339
- reason
10340
- };
10341
- }
10342
- function copyDataDirExcludingBackups(srcRoot, destRoot) {
10343
- fs6.mkdirSync(destRoot, { recursive: true });
10344
- const stack = [{ src: srcRoot, dest: destRoot }];
10345
- while (stack.length > 0) {
10346
- const frame = stack.pop();
10347
- if (frame === undefined)
10348
- break;
10349
- const { src, dest } = frame;
10350
- const entries = fs6.readdirSync(src, { withFileTypes: true });
10351
- for (const entry of entries) {
10352
- if (src === srcRoot) {
10353
- if (entry.name === BACKUPS_DIR_NAME && entry.isDirectory())
10354
- continue;
10355
- if (entry.name === "akm.lock" || entry.name === "akm.lock.lck")
10356
- continue;
10357
- }
10358
- const srcPath = path6.join(src, entry.name);
10359
- const destPath = path6.join(dest, entry.name);
10360
- if (entry.isDirectory()) {
10361
- fs6.mkdirSync(destPath, { recursive: true });
10362
- stack.push({ src: srcPath, dest: destPath });
10363
- } else if (entry.isFile()) {
10364
- fs6.copyFileSync(srcPath, destPath);
10365
- } else if (entry.isSymbolicLink()) {
10366
- const target = fs6.readlinkSync(srcPath);
10367
- bestEffort(() => fs6.symlinkSync(target, destPath), "symlink creation can fail on Windows without admin");
10368
- }
10369
- }
10370
- }
10371
- }
10372
- function tryHostname() {
10373
- try {
10374
- const os = __require("os");
10375
- return os.hostname();
10376
- } catch {
10377
- return;
10378
- }
10379
- }
10380
- var DEFAULT_BACKUP_REASON = "version-upgrade", EMBEDDING_DIM_CHANGE_REASON = "embedding-dim-change", BACKUPS_DIR_NAME = "backups", BACKUP_METADATA_FILE = "backup.meta.json", DEFAULT_RETAIN = 5, FREE_SPACE_MULTIPLIER = 1.1;
10381
- var init_db_backup = __esm(() => {
10382
- init_best_effort();
10383
- init_warn();
10384
- });
10385
-
10386
10193
  // src/core/file-lock.ts
10387
- import fs7 from "fs";
10194
+ import fs6 from "fs";
10388
10195
  function tryAcquireLockSync(lockPath, payload) {
10389
10196
  try {
10390
- fs7.writeFileSync(lockPath, payload, { flag: "wx" });
10197
+ fs6.writeFileSync(lockPath, payload, { flag: "wx" });
10391
10198
  return true;
10392
10199
  } catch (err) {
10393
10200
  if (err.code === "EEXIST")
@@ -10399,14 +10206,14 @@ function probeLock(lockPath, opts) {
10399
10206
  let rawContent;
10400
10207
  let ageMs;
10401
10208
  try {
10402
- rawContent = fs7.readFileSync(lockPath, "utf8");
10209
+ rawContent = fs6.readFileSync(lockPath, "utf8");
10403
10210
  } catch (err) {
10404
10211
  if (err.code === "ENOENT")
10405
10212
  return { state: "absent" };
10406
10213
  return { state: "stale", reason: "unreadable" };
10407
10214
  }
10408
10215
  try {
10409
- const stat = fs7.statSync(lockPath);
10216
+ const stat = fs6.statSync(lockPath);
10410
10217
  ageMs = Date.now() - stat.mtimeMs;
10411
10218
  } catch {
10412
10219
  return { state: "stale", reason: "unreadable", rawContent };
@@ -10425,7 +10232,7 @@ function probeLock(lockPath, opts) {
10425
10232
  }
10426
10233
  function releaseLock(lockPath) {
10427
10234
  try {
10428
- fs7.unlinkSync(lockPath);
10235
+ fs6.unlinkSync(lockPath);
10429
10236
  } catch {}
10430
10237
  }
10431
10238
  function extractHolderPid(content) {
@@ -10449,7 +10256,7 @@ var init_file_lock = __esm(() => {
10449
10256
  });
10450
10257
 
10451
10258
  // src/core/config/config-io.ts
10452
- import fs8 from "fs";
10259
+ import fs7 from "fs";
10453
10260
  import path7 from "path";
10454
10261
  function parseConfigText(text, sourcePath) {
10455
10262
  const stripped = stripJsonComments(text);
@@ -10484,22 +10291,22 @@ function writeConfigAtomic(configPath, config) {
10484
10291
  `);
10485
10292
  }
10486
10293
  function backupExistingConfig(configPath) {
10487
- if (!fs8.existsSync(configPath))
10294
+ if (!fs7.existsSync(configPath))
10488
10295
  return;
10489
10296
  const backupDir = path7.join(getCacheDir(), "config-backups");
10490
- fs8.mkdirSync(backupDir, { recursive: true });
10297
+ fs7.mkdirSync(backupDir, { recursive: true });
10491
10298
  const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
10492
10299
  const timestamped = path7.join(backupDir, `config-${timestamp}.json`);
10493
10300
  const latest = path7.join(backupDir, "config.latest.json");
10494
- fs8.copyFileSync(configPath, timestamped);
10495
- fs8.copyFileSync(configPath, latest);
10496
- pruneOldBackups2(backupDir);
10301
+ fs7.copyFileSync(configPath, timestamped);
10302
+ fs7.copyFileSync(configPath, latest);
10303
+ pruneOldBackups(backupDir);
10497
10304
  return { timestamped, latest };
10498
10305
  }
10499
- function pruneOldBackups2(backupDir) {
10306
+ function pruneOldBackups(backupDir) {
10500
10307
  let entries;
10501
10308
  try {
10502
- entries = fs8.readdirSync(backupDir);
10309
+ entries = fs7.readdirSync(backupDir);
10503
10310
  } catch {
10504
10311
  return;
10505
10312
  }
@@ -10507,13 +10314,13 @@ function pruneOldBackups2(backupDir) {
10507
10314
  const full = path7.join(backupDir, name);
10508
10315
  let mtime = 0;
10509
10316
  try {
10510
- mtime = fs8.statSync(full).mtimeMs;
10317
+ mtime = fs7.statSync(full).mtimeMs;
10511
10318
  } catch {}
10512
10319
  return { path: full, mtime };
10513
10320
  }).sort((a, b) => b.mtime - a.mtime);
10514
10321
  for (const stale of timestamped.slice(MAX_CONFIG_BACKUPS)) {
10515
10322
  try {
10516
- fs8.unlinkSync(stale.path);
10323
+ fs7.unlinkSync(stale.path);
10517
10324
  } catch {}
10518
10325
  }
10519
10326
  }
@@ -10526,7 +10333,7 @@ function sleepSyncMs(ms) {
10526
10333
  function acquireConfigLock() {
10527
10334
  const lockPath = getConfigLockPath();
10528
10335
  try {
10529
- fs8.mkdirSync(path7.dirname(lockPath), { recursive: true });
10336
+ fs7.mkdirSync(path7.dirname(lockPath), { recursive: true });
10530
10337
  } catch {}
10531
10338
  for (let attempt = 0;attempt < CONFIG_LOCK_MAX_RETRIES; attempt++) {
10532
10339
  try {
@@ -10685,7 +10492,7 @@ var init_inline_refs = __esm(() => {
10685
10492
  });
10686
10493
 
10687
10494
  // src/integrations/harnesses/claude/session-log.ts
10688
- import fs9 from "fs";
10495
+ import fs8 from "fs";
10689
10496
  import os from "os";
10690
10497
  import path8 from "path";
10691
10498
  function claudeProjectsDir() {
@@ -10747,19 +10554,19 @@ function parseClaudeEvent(entry, sessionId, filePath, fallbackTsMs) {
10747
10554
  class ClaudeCodeProvider {
10748
10555
  name = "claude-code";
10749
10556
  isAvailable() {
10750
- return fs9.existsSync(claudeProjectsDir());
10557
+ return fs8.existsSync(claudeProjectsDir());
10751
10558
  }
10752
10559
  watchRoots() {
10753
10560
  const dir = claudeProjectsDir();
10754
- return fs9.existsSync(dir) ? [dir] : [];
10561
+ return fs8.existsSync(dir) ? [dir] : [];
10755
10562
  }
10756
10563
  *readEvents(input) {
10757
10564
  try {
10758
10565
  for (const jsonlPath of this.#walkJsonl(claudeProjectsDir())) {
10759
- const stat = fs9.statSync(jsonlPath);
10566
+ const stat = fs8.statSync(jsonlPath);
10760
10567
  if (stat.mtimeMs < input.sinceMs)
10761
10568
  continue;
10762
- const lines = fs9.readFileSync(jsonlPath, "utf8").split(`
10569
+ const lines = fs8.readFileSync(jsonlPath, "utf8").split(`
10763
10570
  `).filter(Boolean);
10764
10571
  for (const line of lines) {
10765
10572
  try {
@@ -10790,7 +10597,7 @@ class ClaudeCodeProvider {
10790
10597
  for (const jsonlPath of this.#walkJsonl(root)) {
10791
10598
  let stat;
10792
10599
  try {
10793
- stat = fs9.statSync(jsonlPath);
10600
+ stat = fs8.statSync(jsonlPath);
10794
10601
  } catch {
10795
10602
  continue;
10796
10603
  }
@@ -10813,8 +10620,8 @@ class ClaudeCodeProvider {
10813
10620
  return summaries.sort((a, b) => (b.endedAt ?? 0) - (a.endedAt ?? 0));
10814
10621
  }
10815
10622
  readSession(ref) {
10816
- const stat = fs9.statSync(ref.filePath);
10817
- const lines = fs9.readFileSync(ref.filePath, "utf8").split(`
10623
+ const stat = fs8.statSync(ref.filePath);
10624
+ const lines = fs8.readFileSync(ref.filePath, "utf8").split(`
10818
10625
  `).filter(Boolean);
10819
10626
  const events = [];
10820
10627
  const inlineRefs = [];
@@ -10862,12 +10669,12 @@ class ClaudeCodeProvider {
10862
10669
  #peekJsonl(filePath) {
10863
10670
  const result = {};
10864
10671
  try {
10865
- const fd = fs9.openSync(filePath, "r");
10672
+ const fd = fs8.openSync(filePath, "r");
10866
10673
  try {
10867
- const stat = fs9.fstatSync(fd);
10674
+ const stat = fs8.fstatSync(fd);
10868
10675
  const headSize = Math.min(stat.size, 4096);
10869
10676
  const head = Buffer.alloc(headSize);
10870
- fs9.readSync(fd, head, 0, headSize, 0);
10677
+ fs8.readSync(fd, head, 0, headSize, 0);
10871
10678
  const headLines = head.toString("utf8").split(`
10872
10679
  `).filter(Boolean);
10873
10680
  for (const line of headLines) {
@@ -10889,7 +10696,7 @@ class ClaudeCodeProvider {
10889
10696
  if (stat.size > 4096) {
10890
10697
  const tailSize = Math.min(stat.size, 4096);
10891
10698
  const tail = Buffer.alloc(tailSize);
10892
- fs9.readSync(fd, tail, 0, tailSize, stat.size - tailSize);
10699
+ fs8.readSync(fd, tail, 0, tailSize, stat.size - tailSize);
10893
10700
  const tailLines = tail.toString("utf8").split(`
10894
10701
  `).filter(Boolean);
10895
10702
  for (let i = tailLines.length - 1;i >= 0; i--) {
@@ -10906,14 +10713,14 @@ class ClaudeCodeProvider {
10906
10713
  }
10907
10714
  }
10908
10715
  } finally {
10909
- fs9.closeSync(fd);
10716
+ fs8.closeSync(fd);
10910
10717
  }
10911
10718
  } catch {}
10912
10719
  return result;
10913
10720
  }
10914
10721
  *#walkJsonl(dir) {
10915
10722
  try {
10916
- for (const entry of fs9.readdirSync(dir, { withFileTypes: true })) {
10723
+ for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
10917
10724
  const full = path8.join(dir, entry.name);
10918
10725
  if (entry.isDirectory())
10919
10726
  yield* this.#walkJsonl(full);
@@ -10971,7 +10778,7 @@ var init_agent_builder2 = __esm(() => {
10971
10778
  var init_config_import2 = () => {};
10972
10779
 
10973
10780
  // src/integrations/harnesses/opencode/session-log.ts
10974
- import fs10 from "fs";
10781
+ import fs9 from "fs";
10975
10782
  import os2 from "os";
10976
10783
  import path9 from "path";
10977
10784
  function getOpenCodeBaseDir() {
@@ -10985,31 +10792,31 @@ class OpenCodeProvider {
10985
10792
  name = "opencode";
10986
10793
  #baseDir = getOpenCodeBaseDir();
10987
10794
  isAvailable() {
10988
- return fs10.existsSync(this.#baseDir);
10795
+ return fs9.existsSync(this.#baseDir);
10989
10796
  }
10990
10797
  #dbPath(base) {
10991
10798
  return path9.join(base, OPENCODE_DB_FILENAME);
10992
10799
  }
10993
10800
  watchRoots() {
10994
10801
  const roots = [];
10995
- if (fs10.existsSync(this.#dbPath(this.#baseDir)))
10802
+ if (fs9.existsSync(this.#dbPath(this.#baseDir)))
10996
10803
  roots.push(this.#baseDir);
10997
10804
  const sessionRoot = path9.join(this.#baseDir, "storage", "session");
10998
- if (fs10.existsSync(sessionRoot))
10805
+ if (fs9.existsSync(sessionRoot))
10999
10806
  roots.push(sessionRoot);
11000
10807
  return roots;
11001
10808
  }
11002
10809
  *readEvents(input) {
11003
10810
  const candidates = [this.#baseDir, path9.join(this.#baseDir, "log")];
11004
10811
  for (const dir of candidates) {
11005
- if (!fs10.existsSync(dir))
10812
+ if (!fs9.existsSync(dir))
11006
10813
  continue;
11007
10814
  try {
11008
- for (const file of fs10.readdirSync(dir)) {
10815
+ for (const file of fs9.readdirSync(dir)) {
11009
10816
  const full = path9.join(dir, file);
11010
10817
  let stat;
11011
10818
  try {
11012
- stat = fs10.statSync(full);
10819
+ stat = fs9.statSync(full);
11013
10820
  } catch {
11014
10821
  continue;
11015
10822
  }
@@ -11019,7 +10826,7 @@ class OpenCodeProvider {
11019
10826
  continue;
11020
10827
  if (!file.endsWith(".json") && !file.endsWith(".jsonl") && !file.endsWith(".log"))
11021
10828
  continue;
11022
- const content = fs10.readFileSync(full, "utf8");
10829
+ const content = fs9.readFileSync(full, "utf8");
11023
10830
  const lines = content.includes(`
11024
10831
  `) ? content.split(`
11025
10832
  `) : [content];
@@ -11047,30 +10854,30 @@ class OpenCodeProvider {
11047
10854
  const base = input.location ?? this.#baseDir;
11048
10855
  const sinceMs = input.sinceMs ?? 0;
11049
10856
  const dbPath = this.#dbPath(base);
11050
- if (fs10.existsSync(dbPath))
10857
+ if (fs9.existsSync(dbPath))
11051
10858
  return this.#listSessionsFromDb(dbPath, sinceMs);
11052
10859
  const sessionRoot = path9.join(base, "storage", "session");
11053
- if (!fs10.existsSync(sessionRoot))
10860
+ if (!fs9.existsSync(sessionRoot))
11054
10861
  return [];
11055
10862
  const summaries = [];
11056
10863
  try {
11057
- for (const projectId of fs10.readdirSync(sessionRoot)) {
10864
+ for (const projectId of fs9.readdirSync(sessionRoot)) {
11058
10865
  const projectDir = path9.join(sessionRoot, projectId);
11059
10866
  let pstat;
11060
10867
  try {
11061
- pstat = fs10.statSync(projectDir);
10868
+ pstat = fs9.statSync(projectDir);
11062
10869
  } catch {
11063
10870
  continue;
11064
10871
  }
11065
10872
  if (!pstat.isDirectory())
11066
10873
  continue;
11067
- for (const file of fs10.readdirSync(projectDir)) {
10874
+ for (const file of fs9.readdirSync(projectDir)) {
11068
10875
  if (!file.endsWith(".json"))
11069
10876
  continue;
11070
10877
  const filePath = path9.join(projectDir, file);
11071
10878
  let stat;
11072
10879
  try {
11073
- stat = fs10.statSync(filePath);
10880
+ stat = fs9.statSync(filePath);
11074
10881
  } catch {
11075
10882
  continue;
11076
10883
  }
@@ -11078,7 +10885,7 @@ class OpenCodeProvider {
11078
10885
  continue;
11079
10886
  let meta;
11080
10887
  try {
11081
- meta = JSON.parse(fs10.readFileSync(filePath, "utf8"));
10888
+ meta = JSON.parse(fs9.readFileSync(filePath, "utf8"));
11082
10889
  } catch {
11083
10890
  continue;
11084
10891
  }
@@ -11107,7 +10914,7 @@ class OpenCodeProvider {
11107
10914
  return this.#readSessionFromDb(ref);
11108
10915
  let meta = {};
11109
10916
  try {
11110
- meta = JSON.parse(fs10.readFileSync(ref.filePath, "utf8"));
10917
+ meta = JSON.parse(fs9.readFileSync(ref.filePath, "utf8"));
11111
10918
  } catch {}
11112
10919
  const time = meta.time ?? undefined;
11113
10920
  const startedAt = typeof time?.created === "number" ? time.created : undefined;
@@ -11118,14 +10925,14 @@ class OpenCodeProvider {
11118
10925
  const inlineRefs = [];
11119
10926
  const inferredBase = this.#inferBaseFromSessionPath(ref.filePath) ?? this.#baseDir;
11120
10927
  const msgDir = path9.join(inferredBase, "storage", "message", ref.sessionId);
11121
- if (fs10.existsSync(msgDir)) {
10928
+ if (fs9.existsSync(msgDir)) {
11122
10929
  try {
11123
- const files = fs10.readdirSync(msgDir).filter((f) => f.endsWith(".json"));
10930
+ const files = fs9.readdirSync(msgDir).filter((f) => f.endsWith(".json"));
11124
10931
  for (const file of files) {
11125
10932
  const full = path9.join(msgDir, file);
11126
10933
  let msg;
11127
10934
  try {
11128
- msg = JSON.parse(fs10.readFileSync(full, "utf8"));
10935
+ msg = JSON.parse(fs9.readFileSync(full, "utf8"));
11129
10936
  } catch {
11130
10937
  continue;
11131
10938
  }
@@ -16530,7 +16337,7 @@ __export(exports_config, {
16530
16337
  DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE: () => DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE,
16531
16338
  DEFAULT_CONFIG: () => DEFAULT_CONFIG
16532
16339
  });
16533
- import fs11 from "fs";
16340
+ import fs10 from "fs";
16534
16341
  import path10 from "path";
16535
16342
  function resolveBatchSize(configured, contextLength) {
16536
16343
  const base = configured && configured > 0 ? configured : DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE;
@@ -16546,7 +16353,7 @@ function loadUserConfig() {
16546
16353
  const configPath = getConfigPath();
16547
16354
  let stat;
16548
16355
  try {
16549
- stat = fs11.statSync(configPath);
16356
+ stat = fs10.statSync(configPath);
16550
16357
  } catch {
16551
16358
  cachedConfig = undefined;
16552
16359
  return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
@@ -16556,7 +16363,7 @@ function loadUserConfig() {
16556
16363
  }
16557
16364
  let text;
16558
16365
  try {
16559
- text = fs11.readFileSync(configPath, "utf8");
16366
+ text = fs10.readFileSync(configPath, "utf8");
16560
16367
  } catch {
16561
16368
  cachedConfig = undefined;
16562
16369
  return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
@@ -16565,7 +16372,7 @@ function loadUserConfig() {
16565
16372
  const finalConfig = applyRuntimeEnvApiKeys(parseAndValidate(text, configPath));
16566
16373
  let finalStat = stat;
16567
16374
  try {
16568
- finalStat = fs11.statSync(configPath);
16375
+ finalStat = fs10.statSync(configPath);
16569
16376
  } catch {}
16570
16377
  cachedConfig = {
16571
16378
  config: finalConfig,
@@ -16625,7 +16432,9 @@ function maybeAutoMigrateConfigFile(configPath, text) {
16625
16432
  } catch {
16626
16433
  return text;
16627
16434
  }
16628
- if (compareConfigVersion(obj.configVersion, CURRENT_CONFIG_VERSION) === 1) {
16435
+ const onDiskVersion = obj.configVersion;
16436
+ const versionOrder = compareConfigVersion(onDiskVersion, CURRENT_CONFIG_VERSION);
16437
+ if (versionOrder === 1 || onDiskVersion !== undefined && versionOrder === undefined) {
16629
16438
  return text;
16630
16439
  }
16631
16440
  const { changed, result } = migrateConfigShape(obj);
@@ -16669,7 +16478,7 @@ function saveConfig(config) {
16669
16478
  cachedConfig = undefined;
16670
16479
  const configPath = getConfigPath();
16671
16480
  const dir = path10.dirname(configPath);
16672
- fs11.mkdirSync(dir, { recursive: true });
16481
+ fs10.mkdirSync(dir, { recursive: true });
16673
16482
  const sanitized = sanitizeConfigForWrite(config);
16674
16483
  const parseResult = AkmConfigSchema.safeParse(sanitized);
16675
16484
  if (!parseResult.success) {
@@ -16793,7 +16602,7 @@ function applyRuntimeEnvApiKeys(config) {
16793
16602
  if (envKey)
16794
16603
  next.embedding = { ...next.embedding, apiKey: envKey };
16795
16604
  }
16796
- const defaultProfile = next.defaults?.llm;
16605
+ const defaultProfile = resolveDefaultLlmProfileName(next);
16797
16606
  if (next.profiles?.llm) {
16798
16607
  const updated = { ...next.profiles.llm };
16799
16608
  let changed = false;
@@ -16829,7 +16638,7 @@ function warnIfProjectConfigPresent(startDir) {
16829
16638
  }
16830
16639
  function isFile(filePath) {
16831
16640
  try {
16832
- return fs11.statSync(filePath).isFile();
16641
+ return fs10.statSync(filePath).isFile();
16833
16642
  } catch {
16834
16643
  return false;
16835
16644
  }
@@ -16885,8 +16694,9 @@ __export(exports_db, {
16885
16694
  sanitizeFtsQuery: () => sanitizeFtsQuery,
16886
16695
  relinkUsageEvents: () => relinkUsageEvents,
16887
16696
  rebuildFts: () => rebuildFts,
16697
+ purgeEmbeddings: () => purgeEmbeddings,
16698
+ openIndexDatabase: () => openIndexDatabase,
16888
16699
  openExistingDatabase: () => openExistingDatabase,
16889
- openDatabase: () => openDatabase2,
16890
16700
  isVecAvailable: () => isVecAvailable,
16891
16701
  getZeroResultSearches: () => getZeroResultSearches,
16892
16702
  getUtilityScoresByIds: () => getUtilityScoresByIds,
@@ -16932,20 +16742,20 @@ __export(exports_db, {
16932
16742
  EMBEDDING_DIM: () => EMBEDDING_DIM,
16933
16743
  DB_VERSION: () => DB_VERSION
16934
16744
  });
16935
- import fs12 from "fs";
16745
+ import fs11 from "fs";
16936
16746
  import { createRequire as createRequire3 } from "module";
16937
16747
  import path11 from "path";
16938
- function openDatabase2(dbPath, options) {
16748
+ function openIndexDatabase(dbPath, options) {
16939
16749
  const resolvedPath = dbPath ?? getDbPath();
16940
16750
  const dir = path11.dirname(resolvedPath);
16941
- if (!fs12.existsSync(dir)) {
16942
- fs12.mkdirSync(dir, { recursive: true });
16751
+ if (!fs11.existsSync(dir)) {
16752
+ fs11.mkdirSync(dir, { recursive: true });
16943
16753
  }
16944
16754
  const db = openDatabase(resolvedPath);
16945
16755
  applyStandardPragmas(db, { dataDir: dir });
16946
16756
  loadVecExtension(db);
16947
16757
  const resolvedDim = options?.embeddingDim ?? resolveConfiguredEmbeddingDim();
16948
- ensureSchema(db, resolvedDim, { dataDir: dir });
16758
+ ensureSchema(db, resolvedDim);
16949
16759
  warnIfVecMissing(db, { once: true });
16950
16760
  return db;
16951
16761
  }
@@ -17001,34 +16811,13 @@ function warnIfVecMissing(db, { once } = { once: false }) {
17001
16811
  }
17002
16812
  }, "embeddings table may not exist yet during init");
17003
16813
  }
17004
- function ensureSchema(db, embeddingDim, options) {
16814
+ function ensureSchema(db, embeddingDim) {
17005
16815
  db.exec(`
17006
16816
  CREATE TABLE IF NOT EXISTS index_meta (
17007
16817
  key TEXT PRIMARY KEY,
17008
16818
  value TEXT NOT NULL
17009
16819
  );
17010
16820
  `);
17011
- if (options?.dataDir) {
17012
- const storedVersionRaw = getMeta(db, "version");
17013
- const storedVersion = storedVersionRaw !== undefined && storedVersionRaw !== "" ? Number.parseInt(storedVersionRaw, 10) : null;
17014
- const willUpgrade = storedVersionRaw !== undefined && storedVersionRaw !== "" && storedVersionRaw !== String(DB_VERSION);
17015
- if (willUpgrade) {
17016
- try {
17017
- const result = backupDataDir({
17018
- dataDir: options.dataDir,
17019
- sourceVersion: storedVersion !== null && !Number.isNaN(storedVersion) ? storedVersion : null,
17020
- targetVersion: DB_VERSION,
17021
- env: process.env
17022
- });
17023
- if (result) {
17024
- warn("[akm] data directory backed up to %s before v%s\u2192v%d upgrade", result.path, storedVersionRaw, DB_VERSION);
17025
- }
17026
- } catch (err) {
17027
- warn("[akm] pre-upgrade data dir backup raised an unexpected error \u2014 %s; upgrade will proceed without a snapshot", err instanceof Error ? err.message : String(err));
17028
- }
17029
- }
17030
- }
17031
- const usageBackup = handleVersionUpgrade(db);
17032
16821
  db.exec(`
17033
16822
  CREATE TABLE IF NOT EXISTS entries (
17034
16823
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -17231,10 +17020,7 @@ function ensureSchema(db, embeddingDim, options) {
17231
17020
  if (dimExplicit) {
17232
17021
  const storedDim = getMeta(db, "embeddingDim");
17233
17022
  if (storedDim && storedDim !== String(embeddingDim)) {
17234
- backupBeforeEmbeddingDimChange(options?.dataDir, storedDim, String(embeddingDim));
17235
- bestEffort(() => db.exec("DROP TABLE IF EXISTS entries_vec"), "drop entries_vec on dim change");
17236
- bestEffort(() => db.exec("DELETE FROM embeddings"), "delete stale embeddings on dim change");
17237
- setMeta(db, "hasEmbeddings", "0");
17023
+ purgeEmbeddings(db, { dropVecTable: true });
17238
17024
  }
17239
17025
  }
17240
17026
  const vecExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entries_vec'").get();
@@ -17256,109 +17042,20 @@ function ensureSchema(db, embeddingDim, options) {
17256
17042
  if (dimExplicit) {
17257
17043
  const storedDim = getMeta(db, "embeddingDim");
17258
17044
  if (storedDim && storedDim !== String(embeddingDim)) {
17259
- backupBeforeEmbeddingDimChange(options?.dataDir, storedDim, String(embeddingDim));
17260
- bestEffort(() => db.exec("DELETE FROM embeddings"), "delete embeddings on explicit dim change");
17261
- setMeta(db, "hasEmbeddings", "0");
17045
+ purgeEmbeddings(db);
17262
17046
  }
17263
17047
  setMeta(db, "embeddingDim", String(embeddingDim));
17264
17048
  }
17265
17049
  }
17266
17050
  ensureUsageEventsSchema(db);
17267
17051
  db.exec(REGISTRY_INDEX_CACHE_DDL);
17268
- restoreUsageEventsBackup(db, usageBackup);
17269
- }
17270
- function handleVersionUpgrade(db) {
17271
- const storedVersion = getMeta(db, "version");
17272
- if (storedVersion === undefined || storedVersion === "" || storedVersion === String(DB_VERSION))
17273
- return [];
17274
- let usageBackup = [];
17275
- bestEffort(() => {
17276
- usageBackup = db.prepare("SELECT * FROM usage_events").all();
17277
- }, "usage_events table may not exist in older versions");
17278
- db.exec("DROP TABLE IF EXISTS utility_scores");
17279
- db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
17280
- db.exec("DROP INDEX IF EXISTS idx_utility_scores_scoped_entry_id");
17281
- db.exec("DROP TABLE IF EXISTS usage_events");
17282
- db.exec("DROP TABLE IF EXISTS embeddings");
17283
- db.exec("DROP TABLE IF EXISTS entries_vec");
17284
- db.exec("DROP TABLE IF EXISTS entries_fts");
17285
- db.exec("DROP TABLE IF EXISTS index_dir_state");
17286
- db.exec("DROP TABLE IF EXISTS llm_enrichment_cache");
17287
- db.exec("DROP INDEX IF EXISTS idx_llm_cache_updated");
17288
- db.exec("DROP TABLE IF EXISTS graph_extraction_queue");
17289
- db.exec("DROP TABLE IF EXISTS graph_file_relations");
17290
- db.exec("DROP TABLE IF EXISTS graph_file_entities");
17291
- db.exec("DROP TABLE IF EXISTS graph_files");
17292
- db.exec("DROP TABLE IF EXISTS graph_meta");
17293
- db.exec("DROP TABLE IF EXISTS graph_relations");
17294
- db.exec("DROP TABLE IF EXISTS graph_entities");
17295
- db.exec("DROP TABLE IF EXISTS graph_nodes");
17296
- db.exec("DROP TABLE IF EXISTS graph_stashes");
17297
- db.exec("DROP INDEX IF EXISTS idx_entries_dir");
17298
- db.exec("DROP INDEX IF EXISTS idx_entries_type");
17299
- db.exec("DROP TABLE IF EXISTS entries");
17300
- db.exec("DELETE FROM index_meta");
17301
- warn("[akm] Index rebuilt due to version upgrade. Run 'akm index' to repopulate.");
17302
- return usageBackup;
17303
- }
17304
- function backupBeforeEmbeddingDimChange(dataDir, fromDim, toDim) {
17305
- if (!dataDir)
17306
- return;
17307
- try {
17308
- const result = backupDataDir({
17309
- dataDir,
17310
- sourceVersion: DB_VERSION,
17311
- targetVersion: DB_VERSION,
17312
- reason: EMBEDDING_DIM_CHANGE_REASON,
17313
- env: process.env
17314
- });
17315
- if (result) {
17316
- warn("[akm] embedding dimension changed %s\u2192%s; data directory backed up to %s; embeddings will be regenerated", fromDim, toDim, result.path);
17317
- }
17318
- } catch (err) {
17319
- warn("[akm] pre-embedding-dim-change data dir backup raised an unexpected error \u2014 %s; embeddings will be regenerated without a snapshot", err instanceof Error ? err.message : String(err));
17320
- }
17321
17052
  }
17322
- function restoreUsageEventsBackup(db, backup) {
17323
- if (backup.length === 0)
17324
- return;
17325
- try {
17326
- const targetCols = db.prepare("PRAGMA table_info(usage_events)").all().map((c) => c.name);
17327
- if (targetCols.length === 0) {
17328
- warn("[db] restoreUsageEventsBackup: usage_events table missing \u2014 discarding %d backup row(s)", backup.length);
17329
- return;
17330
- }
17331
- const targetSet = new Set(targetCols);
17332
- const backupCols = Object.keys(backup[0] ?? {});
17333
- const projectedCols = backupCols.filter((c) => targetSet.has(c));
17334
- const droppedCols = backupCols.filter((c) => !targetSet.has(c));
17335
- if (projectedCols.length === 0) {
17336
- warn("[db] restoreUsageEventsBackup: no overlapping columns between backup and current schema \u2014 discarding %d row(s); dropped: %s", backup.length, droppedCols.join(", ") || "(none)");
17337
- return;
17338
- }
17339
- if (droppedCols.length > 0) {
17340
- warn("[db] restoreUsageEventsBackup: dropping columns no longer in usage_events schema: %s", droppedCols.join(", "));
17341
- }
17342
- let restored = 0;
17343
- let failed = 0;
17344
- db.transaction(() => {
17345
- const placeholders = projectedCols.map(() => "?").join(", ");
17346
- const insert = db.prepare(`INSERT INTO usage_events (${projectedCols.join(", ")}) VALUES (${placeholders})`);
17347
- for (const row of backup) {
17348
- try {
17349
- insert.run(...projectedCols.map((c) => row[c]));
17350
- restored++;
17351
- } catch {
17352
- failed++;
17353
- }
17354
- }
17355
- })();
17356
- if (failed > 0) {
17357
- warn("[db] restoreUsageEventsBackup: restored %d row(s); skipped %d incompatible row(s)", restored, failed);
17358
- }
17359
- } catch (err) {
17360
- warn("[db] restoreUsageEventsBackup: discarded %d backup row(s) \u2014 %s", backup.length, err instanceof Error ? err.message : String(err));
17053
+ function purgeEmbeddings(db, opts) {
17054
+ bestEffort(() => db.exec("DELETE FROM embeddings"), "purge embeddings");
17055
+ if (isVecAvailable(db)) {
17056
+ bestEffort(() => db.exec(opts?.dropVecTable ? "DROP TABLE IF EXISTS entries_vec" : "DELETE FROM entries_vec"), "purge entries_vec");
17361
17057
  }
17058
+ setMeta(db, "hasEmbeddings", "0");
17362
17059
  }
17363
17060
  function getMeta(db, key) {
17364
17061
  const row = db.prepare("SELECT value FROM index_meta WHERE key = ?").get(key);
@@ -18282,18 +17979,27 @@ function collectTagSetFromEntries(db, entryType) {
18282
17979
  }
18283
17980
  return tags;
18284
17981
  }
18285
- var DB_VERSION = 17, EMBEDDING_DIM = 384, GRAPH_SCHEMA_VERSION = 4, vecStatus, VEC_DOCS_URL = "https://github.com/itlackey/akm/blob/main/docs/configuration.md#sqlite-vec-extension", VEC_FALLBACK_THRESHOLD = 1e4, vecInitWarnedDbs, SQLITE_CHUNK_SIZE = 500, upsertStmtsByDb, FEEDBACK_LR = 0.1, FEEDBACK_REWARD_POSITIVE = 1, FEEDBACK_REWARD_NEGATIVE = 0, MAX_NEG_DELTA_PER_CALL = 0.15, UTILITY_REVIEW_THRESHOLD = 0.5, HIGH_UTILITY_THRESHOLD = 0.5;
17982
+ var DB_VERSION = 17, EMBEDDING_DIM = 384, GRAPH_SCHEMA_VERSION = 4, vecStatus, VEC_DOCS_URL = "https://github.com/itlackey/akm/blob/main/docs/configuration.md#sqlite-vec-extension", VEC_FALLBACK_THRESHOLD = 1e4, vecInitWarnedDbs, REGISTRY_INDEX_CACHE_DDL = `
17983
+ CREATE TABLE IF NOT EXISTS registry_index_cache (
17984
+ registry_url TEXT PRIMARY KEY,
17985
+ fetched_at TEXT NOT NULL,
17986
+ etag TEXT,
17987
+ last_modified TEXT,
17988
+ index_json TEXT NOT NULL DEFAULT '{}'
17989
+ );
17990
+
17991
+ CREATE INDEX IF NOT EXISTS idx_registry_cache_fetched
17992
+ ON registry_index_cache(fetched_at);
17993
+ `, SQLITE_CHUNK_SIZE = 500, upsertStmtsByDb, FEEDBACK_LR = 0.1, FEEDBACK_REWARD_POSITIVE = 1, FEEDBACK_REWARD_NEGATIVE = 0, MAX_NEG_DELTA_PER_CALL = 0.15, UTILITY_REVIEW_THRESHOLD = 0.5, HIGH_UTILITY_THRESHOLD = 0.5;
18286
17994
  var init_db = __esm(() => {
18287
17995
  init_asset_ref();
18288
17996
  init_best_effort();
18289
17997
  init_paths();
18290
- init_state_db();
18291
17998
  init_warn();
18292
17999
  init_types();
18293
18000
  init_runtime();
18294
18001
  init_database();
18295
18002
  init_sqlite_pragmas();
18296
- init_db_backup();
18297
18003
  vecStatus = new WeakMap;
18298
18004
  vecInitWarnedDbs = new WeakSet;
18299
18005
  upsertStmtsByDb = new WeakMap;
@@ -18313,12 +18019,12 @@ __export(exports_graph_db, {
18313
18019
  drainExtractionQueue: () => drainExtractionQueue,
18314
18020
  deleteStoredGraph: () => deleteStoredGraph
18315
18021
  });
18316
- import fs13 from "fs";
18022
+ import fs12 from "fs";
18317
18023
  function withReadableGraphDb(db, fn) {
18318
18024
  if (db)
18319
18025
  return fn(db);
18320
18026
  const dbPath = getDbPath();
18321
- if (!fs13.existsSync(dbPath))
18027
+ if (!fs12.existsSync(dbPath))
18322
18028
  throw new Error("GRAPH_DB_MISSING");
18323
18029
  const opened = openExistingDatabase(dbPath);
18324
18030
  try {
@@ -18662,7 +18368,7 @@ var init_graph_db = __esm(() => {
18662
18368
 
18663
18369
  // scripts/migrate-storage.ts
18664
18370
  init_paths();
18665
- import fs14 from "fs";
18371
+ import fs13 from "fs";
18666
18372
  import os3 from "os";
18667
18373
  import path12 from "path";
18668
18374
  import readline from "readline";
@@ -18705,14 +18411,14 @@ var PATHS = {
18705
18411
  };
18706
18412
  var versionReports = [];
18707
18413
  function ensureDir(dir) {
18708
- if (!fs14.existsSync(dir)) {
18709
- fs14.mkdirSync(dir, { recursive: true });
18414
+ if (!fs13.existsSync(dir)) {
18415
+ fs13.mkdirSync(dir, { recursive: true });
18710
18416
  }
18711
18417
  }
18712
18418
  function copyAndVerify(src, dest) {
18713
- fs14.copyFileSync(src, dest);
18714
- const srcStat = fs14.statSync(src);
18715
- const destStat = fs14.statSync(dest);
18419
+ fs13.copyFileSync(src, dest);
18420
+ const srcStat = fs13.statSync(src);
18421
+ const destStat = fs13.statSync(dest);
18716
18422
  return destStat.size === srcStat.size;
18717
18423
  }
18718
18424
  function copyTree(src, dest, opts) {
@@ -18720,7 +18426,7 @@ function copyTree(src, dest, opts) {
18720
18426
  let skipped = 0;
18721
18427
  let failed = 0;
18722
18428
  ensureDir(dest);
18723
- for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
18429
+ for (const entry of fs13.readdirSync(src, { withFileTypes: true })) {
18724
18430
  const srcEntry = path12.join(src, entry.name);
18725
18431
  const destEntry = path12.join(dest, entry.name);
18726
18432
  if (entry.isDirectory()) {
@@ -18730,7 +18436,7 @@ function copyTree(src, dest, opts) {
18730
18436
  failed += sub.failed;
18731
18437
  continue;
18732
18438
  }
18733
- if (!opts.clobber && fs14.existsSync(destEntry)) {
18439
+ if (!opts.clobber && fs13.existsSync(destEntry)) {
18734
18440
  skipped++;
18735
18441
  continue;
18736
18442
  }
@@ -18748,7 +18454,7 @@ function copyTree(src, dest, opts) {
18748
18454
  }
18749
18455
  function countFilesRecursive(dir) {
18750
18456
  let count = 0;
18751
- for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
18457
+ for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
18752
18458
  if (entry.isDirectory()) {
18753
18459
  count += countFilesRecursive(path12.join(dir, entry.name));
18754
18460
  } else {
@@ -18765,10 +18471,10 @@ async function runSteps(steps, ctx) {
18765
18471
  function migrateDb(ctx, filename) {
18766
18472
  const src = path12.join(ctx.paths.cacheDir, filename);
18767
18473
  const dest = path12.join(ctx.paths.dataDir, filename);
18768
- if (!fs14.existsSync(src)) {
18474
+ if (!fs13.existsSync(src)) {
18769
18475
  return { name: filename, status: "skipped", detail: "source not found" };
18770
18476
  }
18771
- if (fs14.existsSync(dest)) {
18477
+ if (fs13.existsSync(dest)) {
18772
18478
  return { name: filename, status: "skipped", detail: "destination already exists" };
18773
18479
  }
18774
18480
  if (ctx.dryRun) {
@@ -18787,7 +18493,7 @@ function migrateDb(ctx, filename) {
18787
18493
  }
18788
18494
  async function migrateEventsJsonl(ctx) {
18789
18495
  const src = path12.join(ctx.paths.cacheDir, "events.jsonl");
18790
- if (!fs14.existsSync(src)) {
18496
+ if (!fs13.existsSync(src)) {
18791
18497
  return { name: "events.jsonl \u2192 state.db", status: "skipped", detail: "source not found" };
18792
18498
  }
18793
18499
  if (ctx.dryRun) {
@@ -18833,11 +18539,11 @@ async function migrateEventsJsonl(ctx) {
18833
18539
  function migrateTaskHistory(ctx) {
18834
18540
  const src = path12.join(ctx.paths.cacheDir, "tasks", "history");
18835
18541
  const dest = path12.join(ctx.paths.stateDir, "tasks", "history");
18836
- if (!fs14.existsSync(src)) {
18542
+ if (!fs13.existsSync(src)) {
18837
18543
  return { name: "tasks/history/", status: "skipped", detail: "source directory not found" };
18838
18544
  }
18839
18545
  try {
18840
- const files = fs14.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18546
+ const files = fs13.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18841
18547
  if (files.length === 0) {
18842
18548
  return { name: "tasks/history/", status: "skipped", detail: "no *.jsonl files found in source directory" };
18843
18549
  }
@@ -18884,10 +18590,10 @@ function migrateTaskHistory(ctx) {
18884
18590
  function migrateLockfile(ctx) {
18885
18591
  const src = path12.join(ctx.paths.configDir, "akm.lock");
18886
18592
  const dest = path12.join(ctx.paths.dataDir, "akm.lock");
18887
- if (!fs14.existsSync(src)) {
18593
+ if (!fs13.existsSync(src)) {
18888
18594
  return { name: "akm.lock", status: "skipped", detail: "source not found" };
18889
18595
  }
18890
- if (fs14.existsSync(dest)) {
18596
+ if (fs13.existsSync(dest)) {
18891
18597
  return { name: "akm.lock", status: "skipped", detail: "destination already exists" };
18892
18598
  }
18893
18599
  if (ctx.dryRun) {
@@ -18909,10 +18615,10 @@ function migrateLockfile(ctx) {
18909
18615
  function migrateConfigBackups(ctx) {
18910
18616
  const src = path12.join(ctx.paths.cacheDir, "config-backups");
18911
18617
  const dest = path12.join(ctx.paths.dataDir, "config-backups");
18912
- if (!fs14.existsSync(src)) {
18618
+ if (!fs13.existsSync(src)) {
18913
18619
  return { name: "config-backups/", status: "skipped", detail: "source directory not found" };
18914
18620
  }
18915
- if (fs14.existsSync(dest)) {
18621
+ if (fs13.existsSync(dest)) {
18916
18622
  return { name: "config-backups/", status: "skipped", detail: "destination already exists" };
18917
18623
  }
18918
18624
  if (ctx.dryRun) {
@@ -18929,7 +18635,7 @@ function migrateConfigBackups(ctx) {
18929
18635
  try {
18930
18636
  const srcCount = countFilesRecursive(src);
18931
18637
  const { copied, failed } = copyTree(src, dest, { clobber: true });
18932
- const destCount = fs14.existsSync(dest) ? countFilesRecursive(dest) : 0;
18638
+ const destCount = fs13.existsSync(dest) ? countFilesRecursive(dest) : 0;
18933
18639
  if (failed === 0 && destCount === srcCount) {
18934
18640
  return {
18935
18641
  name: "config-backups/",
@@ -18949,10 +18655,10 @@ function migrateConfigBackups(ctx) {
18949
18655
  }
18950
18656
  async function migrateTaskHistoryToDb(ctx) {
18951
18657
  const src = path12.join(ctx.paths.cacheDir, "tasks", "history");
18952
- if (!fs14.existsSync(src)) {
18658
+ if (!fs13.existsSync(src)) {
18953
18659
  return { name: "tasks/history/ \u2192 state.db", status: "skipped", detail: "source directory not found" };
18954
18660
  }
18955
- const files = fs14.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18661
+ const files = fs13.readdirSync(src).filter((f) => f.endsWith(".jsonl"));
18956
18662
  if (files.length === 0) {
18957
18663
  return {
18958
18664
  name: "tasks/history/ \u2192 state.db",
@@ -18976,7 +18682,7 @@ async function migrateTaskHistoryToDb(ctx) {
18976
18682
  try {
18977
18683
  for (const file of files) {
18978
18684
  const filePath = path12.join(src, file);
18979
- const text = fs14.readFileSync(filePath, "utf8");
18685
+ const text = fs13.readFileSync(filePath, "utf8");
18980
18686
  const lines = text.split(`
18981
18687
  `).filter((l) => l.trim().length > 0);
18982
18688
  db.exec("BEGIN");
@@ -19036,10 +18742,10 @@ async function migrateTaskHistoryToDb(ctx) {
19036
18742
  }
19037
18743
  function noteRegistryIndexCache(ctx) {
19038
18744
  const src = path12.join(ctx.paths.cacheDir, "registry-index");
19039
- if (!fs14.existsSync(src)) {
18745
+ if (!fs13.existsSync(src)) {
19040
18746
  return { name: "registry-index/ (note)", status: "skipped", detail: "no old $CACHE/registry-index/ directory found" };
19041
18747
  }
19042
- const legacyFiles = fs14.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
18748
+ const legacyFiles = fs13.readdirSync(src).filter((f) => f.endsWith(".json") && !f.startsWith("website-"));
19043
18749
  if (legacyFiles.length === 0) {
19044
18750
  return { name: "registry-index/ (note)", status: "skipped", detail: "no old *.json cache files found" };
19045
18751
  }
@@ -19071,7 +18777,7 @@ var v07To08Migration = {
19071
18777
  path12.join(paths.cacheDir, "config-backups"),
19072
18778
  path12.join(paths.cacheDir, "registry-index")
19073
18779
  ];
19074
- return candidates.some((p) => fs14.existsSync(p));
18780
+ return candidates.some((p) => fs13.existsSync(p));
19075
18781
  },
19076
18782
  steps: [
19077
18783
  { id: "index-db", title: "index.db", run: (ctx) => migrateDb(ctx, "index.db") },
@@ -19093,13 +18799,13 @@ function legacyGraphCandidatePaths(ctx) {
19093
18799
  }
19094
18800
  function findLegacyGraphFile(ctx) {
19095
18801
  for (const candidate of legacyGraphCandidatePaths(ctx)) {
19096
- if (fs14.existsSync(candidate) && fs14.statSync(candidate).isFile())
18802
+ if (fs13.existsSync(candidate) && fs13.statSync(candidate).isFile())
19097
18803
  return candidate;
19098
18804
  }
19099
18805
  const graphDir = path12.join(ctx.paths.cacheDir, "graph");
19100
- if (fs14.existsSync(graphDir) && fs14.statSync(graphDir).isDirectory()) {
18806
+ if (fs13.existsSync(graphDir) && fs13.statSync(graphDir).isDirectory()) {
19101
18807
  try {
19102
- const json = fs14.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path12.join(graphDir, f));
18808
+ const json = fs13.readdirSync(graphDir).filter((f) => f.endsWith(".json")).map((f) => path12.join(graphDir, f));
19103
18809
  if (json.length > 0)
19104
18810
  return json[0];
19105
18811
  } catch {}
@@ -19135,7 +18841,7 @@ async function migrateGraphFileToDb(ctx) {
19135
18841
  if (!legacyFile) {
19136
18842
  return { name, status: "skipped", detail: "no legacy graph file found" };
19137
18843
  }
19138
- if (!fs14.existsSync(ctx.paths.indexDbPath)) {
18844
+ if (!fs13.existsSync(ctx.paths.indexDbPath)) {
19139
18845
  return {
19140
18846
  name,
19141
18847
  status: "skipped",
@@ -19150,7 +18856,7 @@ async function migrateGraphFileToDb(ctx) {
19150
18856
  };
19151
18857
  }
19152
18858
  try {
19153
- const raw = fs14.readFileSync(legacyFile, "utf8");
18859
+ const raw = fs13.readFileSync(legacyFile, "utf8");
19154
18860
  let parsed;
19155
18861
  try {
19156
18862
  parsed = JSON.parse(raw);
@@ -19227,7 +18933,7 @@ async function migrateGraphFileToDb(ctx) {
19227
18933
  }
19228
18934
  const renamed = `${legacyFile}.migrated`;
19229
18935
  try {
19230
- fs14.renameSync(legacyFile, renamed);
18936
+ fs13.renameSync(legacyFile, renamed);
19231
18937
  } catch (err) {
19232
18938
  const msg = err instanceof Error ? err.message : String(err);
19233
18939
  return {
@@ -19252,8 +18958,8 @@ async function migrateGraphFileToDb(ctx) {
19252
18958
  function resolvePrimaryStashDir(configDirPath) {
19253
18959
  try {
19254
18960
  const cfgPath = path12.join(configDirPath, "config.json");
19255
- if (fs14.existsSync(cfgPath)) {
19256
- const cfg = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
18961
+ if (fs13.existsSync(cfgPath)) {
18962
+ const cfg = JSON.parse(fs13.readFileSync(cfgPath, "utf8"));
19257
18963
  if (typeof cfg.stashDir === "string" && cfg.stashDir.length > 0)
19258
18964
  return cfg.stashDir;
19259
18965
  }
@@ -19262,7 +18968,7 @@ function resolvePrimaryStashDir(configDirPath) {
19262
18968
  }
19263
18969
  function countEnvFilesRecursive(dir) {
19264
18970
  let count = 0;
19265
- for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
18971
+ for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
19266
18972
  const full = path12.join(dir, entry.name);
19267
18973
  if (entry.isDirectory()) {
19268
18974
  count += countEnvFilesRecursive(full);
@@ -19275,12 +18981,12 @@ function countEnvFilesRecursive(dir) {
19275
18981
  function chmodTreeSecure(dir) {
19276
18982
  const isWin = process.platform === "win32";
19277
18983
  try {
19278
- fs14.chmodSync(dir, 448);
18984
+ fs13.chmodSync(dir, 448);
19279
18985
  } catch (err) {
19280
18986
  if (!isWin)
19281
18987
  return { ok: false, detail: `chmod 0700 ${dir} failed: ${err instanceof Error ? err.message : err}` };
19282
18988
  }
19283
- for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
18989
+ for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
19284
18990
  const full = path12.join(dir, entry.name);
19285
18991
  if (entry.isDirectory()) {
19286
18992
  const sub = chmodTreeSecure(full);
@@ -19289,8 +18995,8 @@ function chmodTreeSecure(dir) {
19289
18995
  continue;
19290
18996
  }
19291
18997
  try {
19292
- fs14.chmodSync(full, 384);
19293
- if (!isWin && (fs14.statSync(full).mode & 511) !== 384) {
18998
+ fs13.chmodSync(full, 384);
18999
+ if (!isWin && (fs13.statSync(full).mode & 511) !== 384) {
19294
19000
  return { ok: false, detail: `mode verification failed for ${full}` };
19295
19001
  }
19296
19002
  } catch (err) {
@@ -19306,10 +19012,10 @@ async function migrateVaultsToEnv(ctx) {
19306
19012
  const vaultsDir = path12.join(stashDir, "vaults");
19307
19013
  const envDir = path12.join(stashDir, "env");
19308
19014
  const marker = path12.join(vaultsDir, MIGRATED_MARKER);
19309
- if (!fs14.existsSync(vaultsDir)) {
19015
+ if (!fs13.existsSync(vaultsDir)) {
19310
19016
  return { name, status: "skipped", detail: `no vaults/ directory under ${stashDir}` };
19311
19017
  }
19312
- if (fs14.existsSync(marker)) {
19018
+ if (fs13.existsSync(marker)) {
19313
19019
  return { name, status: "skipped", detail: "already migrated (.migrated marker present)" };
19314
19020
  }
19315
19021
  const vaultEnvCount = countEnvFilesRecursive(vaultsDir);
@@ -19340,7 +19046,7 @@ async function migrateVaultsToEnv(ctx) {
19340
19046
  detail: `post-copy count ${envEnvCount} < source ${vaultEnvCount}; no marker written`
19341
19047
  };
19342
19048
  }
19343
- fs14.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
19049
+ fs13.writeFileSync(marker, `migrated vaults/ -> env/ at ${new Date().toISOString()}
19344
19050
  `, { mode: 384 });
19345
19051
  return {
19346
19052
  name,