akm-cli 0.9.0-beta.52 → 0.9.0-beta.54

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 (66) hide show
  1. package/dist/assets/hints/cli-hints-full.md +6 -5
  2. package/dist/cli/clack.js +56 -0
  3. package/dist/cli/confirm.js +1 -1
  4. package/dist/cli.js +0 -7
  5. package/dist/commands/env/env-cli.js +3 -2
  6. package/dist/commands/env/env.js +14 -67
  7. package/dist/commands/health/checks.js +28 -15
  8. package/dist/commands/health/html-report.js +33 -10
  9. package/dist/commands/health.js +222 -22
  10. package/dist/commands/improve/collapse-detector.js +419 -0
  11. package/dist/commands/improve/consolidate.js +72 -54
  12. package/dist/commands/improve/distill.js +79 -13
  13. package/dist/commands/improve/extract.js +13 -6
  14. package/dist/commands/improve/homeostatic.js +109 -79
  15. package/dist/commands/improve/improve-cli.js +67 -1
  16. package/dist/commands/improve/improve.js +10 -0
  17. package/dist/commands/improve/loop-stages.js +39 -1
  18. package/dist/commands/improve/outcome-loop.js +33 -19
  19. package/dist/commands/improve/preparation.js +36 -11
  20. package/dist/commands/improve/salience.js +49 -32
  21. package/dist/commands/read/curate.js +9 -13
  22. package/dist/commands/read/knowledge.js +4 -0
  23. package/dist/commands/read/search-cli.js +6 -4
  24. package/dist/commands/read/search.js +12 -5
  25. package/dist/commands/read/show.js +6 -8
  26. package/dist/commands/sources/add-cli.js +1 -1
  27. package/dist/commands/sources/init.js +12 -0
  28. package/dist/commands/sources/stash-cli.js +1 -1
  29. package/dist/commands/tasks/default-tasks.js +12 -0
  30. package/dist/core/asset/asset-spec.js +3 -2
  31. package/dist/core/config/config-schema.js +39 -17
  32. package/dist/core/config/config.js +12 -0
  33. package/dist/core/eval/rank-metrics.js +113 -0
  34. package/dist/core/state/migrations.js +56 -0
  35. package/dist/core/state-db.js +146 -19
  36. package/dist/core/warn.js +21 -0
  37. package/dist/indexer/db/db.js +6 -0
  38. package/dist/indexer/ensure-index.js +36 -92
  39. package/dist/indexer/index-writer-lock.js +9 -11
  40. package/dist/indexer/index-written-assets.js +105 -0
  41. package/dist/indexer/indexer.js +16 -4
  42. package/dist/indexer/passes/metadata.js +20 -0
  43. package/dist/indexer/read-preflight.js +23 -0
  44. package/dist/indexer/search/db-search.js +29 -1
  45. package/dist/indexer/search/ranking-contributors.js +33 -1
  46. package/dist/indexer/search/ranking.js +66 -0
  47. package/dist/indexer/search/search-fields.js +6 -0
  48. package/dist/indexer/walk/walker.js +21 -13
  49. package/dist/integrations/agent/detect.js +9 -0
  50. package/dist/integrations/agent/index.js +1 -1
  51. package/dist/llm/client.js +12 -0
  52. package/dist/llm/embedder.js +26 -2
  53. package/dist/llm/embedders/local.js +7 -1
  54. package/dist/llm/feature-gate.js +6 -2
  55. package/dist/output/renderers.js +8 -13
  56. package/dist/output/shapes/helpers.js +0 -3
  57. package/dist/output/shapes/passthrough.js +1 -0
  58. package/dist/scripts/migrate-storage.js +178 -35
  59. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +46 -19
  60. package/dist/setup/detect.js +9 -0
  61. package/dist/setup/registry-stash-loader.js +12 -0
  62. package/dist/setup/setup.js +1 -1
  63. package/dist/storage/repositories/index-db.js +10 -1
  64. package/dist/tasks/backends/index.js +9 -0
  65. package/dist/tasks/runner.js +9 -0
  66. package/package.json +2 -4
@@ -468,21 +468,11 @@ function scanKeys(text) {
468
468
  }
469
469
  return keys;
470
470
  }
471
- function scanComments(text) {
472
- const comments = [];
473
- for (const line of text.split(/\r?\n/)) {
474
- const trimmed = line.trimStart();
475
- if (trimmed.startsWith("#")) {
476
- comments.push(trimmed.slice(1).trimStart());
477
- }
478
- }
479
- return comments;
480
- }
481
471
  function listKeys(envPath) {
482
472
  if (!fs.existsSync(envPath))
483
- return { keys: [], comments: [] };
473
+ return { keys: [] };
484
474
  const text = fs.readFileSync(envPath, "utf8");
485
- return { keys: scanKeys(text), comments: scanComments(text) };
475
+ return { keys: scanKeys(text) };
486
476
  }
487
477
  var import_dotenv, ASSIGN_RE;
488
478
  var init_env = __esm(() => {
@@ -7605,23 +7595,35 @@ function appendToLogFile(level, args) {
7605
7595
  }
7606
7596
  }
7607
7597
  function warn(...args) {
7598
+ if (sinkOverride) {
7599
+ sinkOverride("warn", args);
7600
+ return;
7601
+ }
7608
7602
  appendToLogFile("WARN", args);
7609
7603
  if (!quiet) {
7610
7604
  console.warn(...args);
7611
7605
  }
7612
7606
  }
7613
7607
  function error(...args) {
7608
+ if (sinkOverride) {
7609
+ sinkOverride("error", args);
7610
+ return;
7611
+ }
7614
7612
  appendToLogFile("ERROR", args);
7615
7613
  if (!quiet) {
7616
7614
  console.error(...args);
7617
7615
  }
7618
7616
  }
7619
7617
  function warnVerbose(...args) {
7618
+ if (sinkOverride) {
7619
+ sinkOverride("warnVerbose", args);
7620
+ return;
7621
+ }
7620
7622
  if (isVerbose()) {
7621
7623
  warn(...args);
7622
7624
  }
7623
7625
  }
7624
- var quiet = false, verbose = false, logFilePath;
7626
+ var quiet = false, verbose = false, logFilePath, sinkOverride;
7625
7627
  var init_warn = () => {};
7626
7628
 
7627
7629
  // src/indexer/walk/file-context.ts
@@ -8325,12 +8327,7 @@ function applyScriptMetadata(entry, ctx) {
8325
8327
  }
8326
8328
  }
8327
8329
  function applyEnvMetadata(entry, ctx) {
8328
- const { keys, comments } = listKeys(ctx.absPath);
8329
- if (comments.length > 0 && !entry.description) {
8330
- entry.description = comments.join(" ").slice(0, 500);
8331
- entry.source = "comments";
8332
- entry.confidence = 0.7;
8333
- }
8330
+ const { keys } = listKeys(ctx.absPath);
8334
8331
  if (keys.length > 0) {
8335
8332
  entry.searchHints = keys;
8336
8333
  }
@@ -9401,6 +9398,44 @@ var init_migrations = __esm(() => {
9401
9398
  up: `
9402
9399
  ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
9403
9400
  `
9401
+ },
9402
+ {
9403
+ id: "016-collapse-churn-detector",
9404
+ up: `
9405
+ CREATE TABLE IF NOT EXISTS canary_queries (
9406
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9407
+ canary_set_id TEXT NOT NULL,
9408
+ anchor_ref TEXT NOT NULL,
9409
+ query TEXT NOT NULL,
9410
+ source TEXT NOT NULL DEFAULT 'auto',
9411
+ active INTEGER NOT NULL DEFAULT 1,
9412
+ created_at TEXT NOT NULL
9413
+ );
9414
+ CREATE INDEX IF NOT EXISTS idx_canary_queries_active
9415
+ ON canary_queries(active, canary_set_id);
9416
+
9417
+ CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
9418
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9419
+ run_id TEXT NOT NULL,
9420
+ ts TEXT NOT NULL,
9421
+ pass TEXT NOT NULL,
9422
+ canary_set_id TEXT NOT NULL,
9423
+ mean_recall REAL NOT NULL,
9424
+ mean_ndcg REAL NOT NULL,
9425
+ mean_mrr REAL NOT NULL,
9426
+ canary_ranks_json TEXT NOT NULL,
9427
+ store_total INTEGER NOT NULL,
9428
+ store_by_type_json TEXT NOT NULL,
9429
+ distinct_content_ratio REAL NOT NULL,
9430
+ mean_bigram_diversity REAL NOT NULL,
9431
+ over_generation_count INTEGER NOT NULL,
9432
+ accepted_actions INTEGER NOT NULL,
9433
+ merge_floor_violations INTEGER NOT NULL DEFAULT 0,
9434
+ alerts_json TEXT NOT NULL DEFAULT '[]'
9435
+ );
9436
+ CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
9437
+ ON improve_cycle_metrics(ts);
9438
+ `
9404
9439
  }
9405
9440
  ];
9406
9441
  });
@@ -9423,10 +9458,12 @@ __export(exports_state_db, {
9423
9458
  recordFsProposalsImport: () => recordFsProposalsImport,
9424
9459
  readStateEvents: () => readStateEvents,
9425
9460
  queryTaskHistory: () => queryTaskHistory,
9461
+ queryRecentCycleMetrics: () => queryRecentCycleMetrics,
9426
9462
  queryImproveRuns: () => queryImproveRuns,
9427
9463
  queryCompletedTaskIntervals: () => queryCompletedTaskIntervals,
9428
9464
  purgeOldImproveRuns: () => purgeOldImproveRuns,
9429
9465
  purgeOldEvents: () => purgeOldEvents,
9466
+ purgeOldCycleMetrics: () => purgeOldCycleMetrics,
9430
9467
  proposalToRowValues: () => proposalToRowValues,
9431
9468
  proposalRowToProposal: () => proposalRowToProposal,
9432
9469
  persistPhaseThreshold: () => persistPhaseThreshold,
@@ -9436,8 +9473,11 @@ __export(exports_state_db, {
9436
9473
  listStateProposalIdsByPrefix: () => listStateProposalIdsByPrefix,
9437
9474
  listProposalGateDecisions: () => listProposalGateDecisions,
9438
9475
  listExistingTableNames: () => listExistingTableNames,
9476
+ listActiveCanarySetIds: () => listActiveCanarySetIds,
9439
9477
  insertProposalIfAbsent: () => insertProposalIfAbsent,
9440
9478
  insertEvent: () => insertEvent,
9479
+ insertCycleMetrics: () => insertCycleMetrics,
9480
+ insertCanaries: () => insertCanaries,
9441
9481
  importEventsJsonl: () => importEventsJsonl,
9442
9482
  hasImportedFsProposals: () => hasImportedFsProposals,
9443
9483
  getTaskHistoryRuns: () => getTaskHistoryRuns,
@@ -9446,15 +9486,19 @@ __export(exports_state_db, {
9446
9486
  getStateDbPath: () => getStateDbPath,
9447
9487
  getRecombineHypothesis: () => getRecombineHypothesis,
9448
9488
  getPhaseThreshold: () => getPhaseThreshold,
9489
+ getLatestCycleMetrics: () => getLatestCycleMetrics,
9449
9490
  getLastExtractRunAt: () => getLastExtractRunAt,
9450
9491
  getExtractedSessionsMap: () => getExtractedSessionsMap,
9451
9492
  getExtractedSession: () => getExtractedSession,
9452
9493
  getConsolidationJudgedMap: () => getConsolidationJudgedMap,
9494
+ getCanariesBySetId: () => getCanariesBySetId,
9453
9495
  getBodyEmbeddings: () => getBodyEmbeddings,
9496
+ getActiveCanaries: () => getActiveCanaries,
9454
9497
  findMatchingRecombineHypothesis: () => findMatchingRecombineHypothesis,
9455
9498
  eventRowToEnvelope: () => eventRowToEnvelope,
9456
9499
  embeddingToBlob: () => embeddingToBlob,
9457
9500
  decayUnseenRecombineHypotheses: () => decayUnseenRecombineHypotheses,
9501
+ deactivateCanarySet: () => deactivateCanarySet,
9458
9502
  computeImproveRunMetrics: () => computeImproveRunMetrics,
9459
9503
  blobToEmbedding: () => blobToEmbedding
9460
9504
  });
@@ -9686,7 +9730,7 @@ function insertProposalIfAbsent(db, proposal, stashDir) {
9686
9730
  }
9687
9731
  function isRetryableBeginError(err) {
9688
9732
  const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
9689
- return msg.includes("within a transaction") || msg.includes("database is locked") || msg.includes("database table is locked") || msg.includes("did not open a transaction");
9733
+ return msg.includes("database is locked") || msg.includes("database table is locked") || msg.includes("did not open a transaction");
9690
9734
  }
9691
9735
  function sleepSyncMs(ms) {
9692
9736
  if (ms <= 0)
@@ -9694,6 +9738,9 @@ function sleepSyncMs(ms) {
9694
9738
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
9695
9739
  }
9696
9740
  function withImmediateTransaction(db, fn) {
9741
+ if (db.inTransaction) {
9742
+ return fn();
9743
+ }
9697
9744
  let lastBeginErr;
9698
9745
  for (let attempt = 1;attempt <= WITH_IMMEDIATE_TX_MAX_ATTEMPTS; attempt++) {
9699
9746
  try {
@@ -9704,9 +9751,11 @@ function withImmediateTransaction(db, fn) {
9704
9751
  } catch (err) {
9705
9752
  lastBeginErr = err;
9706
9753
  if (isRetryableBeginError(err) && attempt < WITH_IMMEDIATE_TX_MAX_ATTEMPTS) {
9707
- try {
9708
- db.exec("ROLLBACK");
9709
- } catch {}
9754
+ if (db.inTransaction) {
9755
+ try {
9756
+ db.exec("ROLLBACK");
9757
+ } catch {}
9758
+ }
9710
9759
  sleepSyncMs(2 ** (attempt - 1));
9711
9760
  continue;
9712
9761
  }
@@ -9714,12 +9763,17 @@ function withImmediateTransaction(db, fn) {
9714
9763
  }
9715
9764
  try {
9716
9765
  const result = fn();
9766
+ if (!db.inTransaction) {
9767
+ throw new Error("withImmediateTransaction invariant violated: transaction opened by BEGIN IMMEDIATE was no longer active after the transaction body ran; refusing to COMMIT (writes may have escaped serialization)");
9768
+ }
9717
9769
  db.exec("COMMIT");
9718
9770
  return result;
9719
9771
  } catch (err) {
9720
- try {
9721
- db.exec("ROLLBACK");
9722
- } catch {}
9772
+ if (db.inTransaction) {
9773
+ try {
9774
+ db.exec("ROLLBACK");
9775
+ } catch {}
9776
+ }
9723
9777
  throw err;
9724
9778
  }
9725
9779
  }
@@ -10091,6 +10145,75 @@ function upsertBodyEmbeddings(db, entries) {
10091
10145
  }
10092
10146
  })();
10093
10147
  }
10148
+ function insertCanaries(db, canarySetId, canaries, now) {
10149
+ if (canaries.length === 0)
10150
+ return;
10151
+ const ts = now ?? new Date().toISOString();
10152
+ const stmt = db.prepare(`
10153
+ INSERT INTO canary_queries (canary_set_id, anchor_ref, query, source, active, created_at)
10154
+ VALUES (?, ?, ?, ?, 1, ?)
10155
+ `);
10156
+ db.transaction(() => {
10157
+ for (const c of canaries) {
10158
+ stmt.run(canarySetId, c.anchorRef, c.query, c.source ?? "auto", ts);
10159
+ }
10160
+ })();
10161
+ }
10162
+ function getActiveCanaries(db) {
10163
+ return db.prepare(`SELECT * FROM canary_queries
10164
+ WHERE active = 1 AND canary_set_id = (
10165
+ SELECT canary_set_id FROM canary_queries WHERE active = 1
10166
+ ORDER BY created_at DESC, id DESC LIMIT 1
10167
+ )
10168
+ ORDER BY id`).all();
10169
+ }
10170
+ function getCanariesBySetId(db, canarySetId) {
10171
+ return db.prepare(`SELECT * FROM canary_queries WHERE canary_set_id = ? ORDER BY id`).all(canarySetId);
10172
+ }
10173
+ function listActiveCanarySetIds(db) {
10174
+ const rows = db.prepare(`SELECT DISTINCT canary_set_id FROM canary_queries WHERE active = 1`).all();
10175
+ return rows.map((r) => r.canary_set_id);
10176
+ }
10177
+ function deactivateCanarySet(db, canarySetId) {
10178
+ const result = db.prepare(`UPDATE canary_queries SET active = 0 WHERE canary_set_id = ? AND active = 1`).run(canarySetId);
10179
+ const changes = result.changes ?? 0;
10180
+ return typeof changes === "bigint" ? Number(changes) : changes;
10181
+ }
10182
+ function insertCycleMetrics(db, row) {
10183
+ db.prepare(`
10184
+ INSERT INTO improve_cycle_metrics
10185
+ (run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
10186
+ canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
10187
+ mean_bigram_diversity, over_generation_count, accepted_actions,
10188
+ merge_floor_violations, alerts_json)
10189
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
10190
+ `).run(row.run_id, row.ts, row.pass, row.canary_set_id, row.mean_recall, row.mean_ndcg, row.mean_mrr, row.canary_ranks_json, row.store_total, row.store_by_type_json, row.distinct_content_ratio, row.mean_bigram_diversity, row.over_generation_count, row.accepted_actions, row.merge_floor_violations, row.alerts_json);
10191
+ }
10192
+ function queryRecentCycleMetrics(db, canarySetId, limit) {
10193
+ const rows = db.prepare(`SELECT run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
10194
+ canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
10195
+ mean_bigram_diversity, over_generation_count, accepted_actions,
10196
+ merge_floor_violations, alerts_json
10197
+ FROM improve_cycle_metrics WHERE canary_set_id = ?
10198
+ ORDER BY ts DESC, id DESC LIMIT ?`).all(canarySetId, Math.max(0, limit));
10199
+ return rows.reverse();
10200
+ }
10201
+ function getLatestCycleMetrics(db) {
10202
+ const row = db.prepare(`SELECT run_id, ts, pass, canary_set_id, mean_recall, mean_ndcg, mean_mrr,
10203
+ canary_ranks_json, store_total, store_by_type_json, distinct_content_ratio,
10204
+ mean_bigram_diversity, over_generation_count, accepted_actions,
10205
+ merge_floor_violations, alerts_json
10206
+ FROM improve_cycle_metrics ORDER BY ts DESC, id DESC LIMIT 1`).get();
10207
+ return row == null ? undefined : row;
10208
+ }
10209
+ function purgeOldCycleMetrics(db, retentionDays = 365) {
10210
+ if (!Number.isFinite(retentionDays) || retentionDays <= 0)
10211
+ return 0;
10212
+ const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
10213
+ const result = db.prepare("DELETE FROM improve_cycle_metrics WHERE ts < ?").run(cutoff);
10214
+ const changes = result.changes ?? 0;
10215
+ return typeof changes === "bigint" ? Number(changes) : changes;
10216
+ }
10094
10217
  var WITH_IMMEDIATE_TX_MAX_ATTEMPTS = 5;
10095
10218
  var init_state_db = __esm(() => {
10096
10219
  init_managed_db();
@@ -15790,7 +15913,7 @@ var init_config_types = __esm(() => {
15790
15913
  });
15791
15914
 
15792
15915
  // src/core/config/config-schema.ts
15793
- var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveCalibrationSchema, ImproveExplorationSchema, ImproveSalienceSchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, SetupTaskSchedulesSchema, SetupConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
15916
+ var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, LlmCapabilitiesSchema, LlmConnectionConfigSchema, LlmProfileConfigSchema, EmbeddingOllamaOptionsSchema, EmbeddingConnectionConfigSchema, AgentPlatformSchema, AgentProfileConfigSchema, ImproveProcessConfigSchema, ImproveProfileProcessesSchema, ImproveProfileConfigSchema, ProfilesSchema, DefaultsSchema, SourceConfigEntryOptionsSchema, SourceConfigEntrySchema, RegistryConfigEntrySchema, KitSourceSchema, InstalledStashEntrySchema, OutputConfigSchema, SearchGraphBoostSchema, SearchConfigSchema, FeedbackConfigSchema, ImproveUtilityDecaySchema, ImproveCalibrationSchema, ImproveExplorationSchema, ImproveSalienceSchema, ImproveCollapseDetectorSchema, ImproveConfigSchema, GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED, INDEX_PASS_PROVIDER_KEYS, INDEX_PASS_KNOWN_KEYS, IndexPassConfigSchema, MetadataEnhanceSchema, StalenessDetectionSchema, IndexConfigSchema, SetupTaskSchedulesSchema, SetupConfigSchema, AkmConfigShape, AkmConfigBaseSchema, AkmConfigSchema;
15794
15917
  var init_config_schema = __esm(() => {
15795
15918
  init_zod();
15796
15919
  init_config_types();
@@ -15883,11 +16006,6 @@ var init_config_schema = __esm(() => {
15883
16006
  indexSessions: exports_external.boolean().optional(),
15884
16007
  minSessionDuration: exports_external.number().min(0).optional(),
15885
16008
  p90ChunkSecondsDefault: exports_external.number().finite().positive().optional(),
15886
- homeostaticDemotion: exports_external.object({
15887
- enabled: exports_external.boolean().optional(),
15888
- staleDays: exports_external.number().int().min(0).optional(),
15889
- demotionFactor: exports_external.number().min(0).max(1).optional()
15890
- }).passthrough().optional(),
15891
16009
  schemaSimilarity: exports_external.object({
15892
16010
  enabled: exports_external.boolean().optional(),
15893
16011
  epsilon: exports_external.number().min(0).max(1).optional(),
@@ -15900,7 +16018,9 @@ var init_config_schema = __esm(() => {
15900
16018
  enabled: exports_external.boolean().optional(),
15901
16019
  maxGeneration: exports_external.number().int().min(1).optional(),
15902
16020
  lexicalDiversityCheck: exports_external.boolean().optional(),
15903
- randomClusterFraction: exports_external.number().min(0).max(1).optional()
16021
+ randomClusterFraction: exports_external.number().min(0).max(1).optional(),
16022
+ mergeInformationFloor: exports_external.boolean().optional(),
16023
+ minSpecificityRetention: exports_external.number().min(0).max(1).optional()
15904
16024
  }).passthrough().optional(),
15905
16025
  cls: exports_external.object({
15906
16026
  enabled: exports_external.boolean().optional(),
@@ -16068,12 +16188,23 @@ var init_config_schema = __esm(() => {
16068
16188
  salienceThreshold: exports_external.number().min(0).max(1).optional(),
16069
16189
  replayBudget: exports_external.number().int().min(0).optional()
16070
16190
  }).passthrough();
16191
+ ImproveCollapseDetectorSchema = exports_external.object({
16192
+ enabled: exports_external.boolean().optional(),
16193
+ canaryCount: exports_external.number().int().min(3).max(200).optional(),
16194
+ k: exports_external.number().int().min(1).max(100).optional(),
16195
+ windowCycles: exports_external.number().int().min(2).max(50).optional(),
16196
+ recallDropThreshold: exports_external.number().min(0).max(1).optional(),
16197
+ entropyDropThreshold: exports_external.number().min(0).max(1).optional(),
16198
+ churnMinAcceptedActions: exports_external.number().int().min(1).optional(),
16199
+ retentionDays: exports_external.number().int().min(1).optional()
16200
+ }).passthrough();
16071
16201
  ImproveConfigSchema = exports_external.object({
16072
16202
  utilityDecay: ImproveUtilityDecaySchema.optional(),
16073
16203
  eventRetentionDays: nonNegativeNumber.optional(),
16074
16204
  calibration: ImproveCalibrationSchema.optional(),
16075
16205
  exploration: ImproveExplorationSchema.optional(),
16076
- salience: ImproveSalienceSchema.optional()
16206
+ salience: ImproveSalienceSchema.optional(),
16207
+ collapseDetector: ImproveCollapseDetectorSchema.optional()
16077
16208
  }).passthrough();
16078
16209
  GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED = [
16079
16210
  "memory",
@@ -16332,6 +16463,7 @@ __export(exports_config, {
16332
16463
  getIndexPassConfig: () => getIndexPassConfig,
16333
16464
  getEffectiveRegistries: () => getEffectiveRegistries,
16334
16465
  getDefaultLlmConfig: () => getDefaultLlmConfig,
16466
+ _setSaveConfigForTests: () => _setSaveConfigForTests,
16335
16467
  VALID_HARNESS_IDS: () => VALID_HARNESS_IDS,
16336
16468
  FEEDBACK_FAILURE_MODES: () => FEEDBACK_FAILURE_MODES,
16337
16469
  DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE: () => DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE,
@@ -16474,7 +16606,17 @@ function loadConfig() {
16474
16606
  warnIfProjectConfigPresent(process.cwd());
16475
16607
  return loadUserConfig();
16476
16608
  }
16609
+ function _setSaveConfigForTests(fake) {
16610
+ saveConfigOverride = fake;
16611
+ }
16477
16612
  function saveConfig(config) {
16613
+ if (saveConfigOverride) {
16614
+ saveConfigOverride(config);
16615
+ return;
16616
+ }
16617
+ saveConfigReal(config);
16618
+ }
16619
+ function saveConfigReal(config) {
16478
16620
  cachedConfig = undefined;
16479
16621
  const configPath = getConfigPath();
16480
16622
  const dir = path11.dirname(configPath);
@@ -16643,7 +16785,7 @@ function isFile(filePath) {
16643
16785
  return false;
16644
16786
  }
16645
16787
  }
16646
- var FEEDBACK_FAILURE_MODES, DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE = 4, GRAPH_EXTRACTION_CHARS_PER_BODY = 1500, DEFAULT_CONFIG, PROJECT_CONFIG_RELATIVE_PATH, cachedConfig, INDEX_RESERVED_KEYS, PROJECT_CONFIG_DEPRECATION_WARNED;
16788
+ var FEEDBACK_FAILURE_MODES, DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE = 4, GRAPH_EXTRACTION_CHARS_PER_BODY = 1500, DEFAULT_CONFIG, PROJECT_CONFIG_RELATIVE_PATH, cachedConfig, saveConfigOverride, INDEX_RESERVED_KEYS, PROJECT_CONFIG_DEPRECATION_WARNED;
16647
16789
  var init_config2 = __esm(() => {
16648
16790
  init_errors();
16649
16791
  init_config_io();
@@ -17801,6 +17943,7 @@ function getRetrievalCounts(db, refs) {
17801
17943
  FROM usage_events
17802
17944
  WHERE event_type IN ('search','show','curate')
17803
17945
  AND entry_ref IS NOT NULL
17946
+ AND (source IS NULL OR source NOT IN ('improve','task'))
17804
17947
  AND CASE
17805
17948
  WHEN instr(entry_ref, '//') > 0
17806
17949
  THEN substr(entry_ref, instr(entry_ref, '//') + 2)
@@ -53,12 +53,16 @@ function appendToLogFile(level, args) {
53
53
  }
54
54
  }
55
55
  function warn(...args) {
56
+ if (sinkOverride) {
57
+ sinkOverride("warn", args);
58
+ return;
59
+ }
56
60
  appendToLogFile("WARN", args);
57
61
  if (!quiet) {
58
62
  console.warn(...args);
59
63
  }
60
64
  }
61
- var quiet = false, logFilePath;
65
+ var quiet = false, logFilePath, sinkOverride;
62
66
  var init_warn = () => {};
63
67
 
64
68
  // node_modules/dotenv/lib/main.js
@@ -455,21 +459,11 @@ function scanKeys(text) {
455
459
  }
456
460
  return keys;
457
461
  }
458
- function scanComments(text) {
459
- const comments = [];
460
- for (const line of text.split(/\r?\n/)) {
461
- const trimmed = line.trimStart();
462
- if (trimmed.startsWith("#")) {
463
- comments.push(trimmed.slice(1).trimStart());
464
- }
465
- }
466
- return comments;
467
- }
468
462
  function listKeys(envPath) {
469
463
  if (!fs3.existsSync(envPath))
470
- return { keys: [], comments: [] };
464
+ return { keys: [] };
471
465
  const text = fs3.readFileSync(envPath, "utf8");
472
- return { keys: scanKeys(text), comments: scanComments(text) };
466
+ return { keys: scanKeys(text) };
473
467
  }
474
468
  var import_dotenv, ASSIGN_RE;
475
469
  var init_env = __esm(() => {
@@ -8211,12 +8205,7 @@ function applyScriptMetadata(entry, ctx) {
8211
8205
  }
8212
8206
  }
8213
8207
  function applyEnvMetadata(entry, ctx) {
8214
- const { keys, comments } = listKeys(ctx.absPath);
8215
- if (comments.length > 0 && !entry.description) {
8216
- entry.description = comments.join(" ").slice(0, 500);
8217
- entry.source = "comments";
8218
- entry.confidence = 0.7;
8219
- }
8208
+ const { keys } = listKeys(ctx.absPath);
8220
8209
  if (keys.length > 0) {
8221
8210
  entry.searchHints = keys;
8222
8211
  }
@@ -9142,6 +9131,44 @@ var MIGRATIONS = [
9142
9131
  up: `
9143
9132
  ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
9144
9133
  `
9134
+ },
9135
+ {
9136
+ id: "016-collapse-churn-detector",
9137
+ up: `
9138
+ CREATE TABLE IF NOT EXISTS canary_queries (
9139
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9140
+ canary_set_id TEXT NOT NULL,
9141
+ anchor_ref TEXT NOT NULL,
9142
+ query TEXT NOT NULL,
9143
+ source TEXT NOT NULL DEFAULT 'auto',
9144
+ active INTEGER NOT NULL DEFAULT 1,
9145
+ created_at TEXT NOT NULL
9146
+ );
9147
+ CREATE INDEX IF NOT EXISTS idx_canary_queries_active
9148
+ ON canary_queries(active, canary_set_id);
9149
+
9150
+ CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
9151
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9152
+ run_id TEXT NOT NULL,
9153
+ ts TEXT NOT NULL,
9154
+ pass TEXT NOT NULL,
9155
+ canary_set_id TEXT NOT NULL,
9156
+ mean_recall REAL NOT NULL,
9157
+ mean_ndcg REAL NOT NULL,
9158
+ mean_mrr REAL NOT NULL,
9159
+ canary_ranks_json TEXT NOT NULL,
9160
+ store_total INTEGER NOT NULL,
9161
+ store_by_type_json TEXT NOT NULL,
9162
+ distinct_content_ratio REAL NOT NULL,
9163
+ mean_bigram_diversity REAL NOT NULL,
9164
+ over_generation_count INTEGER NOT NULL,
9165
+ accepted_actions INTEGER NOT NULL,
9166
+ merge_floor_violations INTEGER NOT NULL DEFAULT 0,
9167
+ alerts_json TEXT NOT NULL DEFAULT '[]'
9168
+ );
9169
+ CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
9170
+ ON improve_cycle_metrics(ts);
9171
+ `
9145
9172
  }
9146
9173
  ];
9147
9174
  function runMigrations2(db) {
@@ -14,6 +14,11 @@ import { defaultWhich } from "../integrations/agent/detect.js";
14
14
  import { SESSION_LOG_HARNESSES } from "../integrations/harnesses/index.js";
15
15
  import { spawn } from "../runtime.js";
16
16
  import { detectHarnessConfigs } from "./harness-config-import.js";
17
+ let detectOverrides;
18
+ /** TEST-ONLY. Swap the network/host probes; pass undefined to restore. */
19
+ export function _setDetectForTests(fakes) {
20
+ detectOverrides = fakes;
21
+ }
17
22
  // ── Ollama Detection ────────────────────────────────────────────────────────
18
23
  const OLLAMA_BASE = "http://localhost:11434";
19
24
  /**
@@ -23,6 +28,8 @@ const OLLAMA_BASE = "http://localhost:11434";
23
28
  * via subprocess. Returns available models sorted alphabetically.
24
29
  */
25
30
  export async function detectOllama() {
31
+ if (detectOverrides?.detectOllama)
32
+ return detectOverrides.detectOllama();
26
33
  const result = { available: false, models: [], endpoint: OLLAMA_BASE };
27
34
  // Try HTTP API first
28
35
  try {
@@ -120,6 +127,8 @@ const AGENT_PLATFORMS = SESSION_LOG_HARNESSES.filter((h) => h.setupDetectionDir)
120
127
  * Supports both HOME (Unix) and USERPROFILE (Windows).
121
128
  */
122
129
  export function detectAgentPlatforms() {
130
+ if (detectOverrides?.detectAgentPlatforms)
131
+ return detectOverrides.detectAgentPlatforms();
123
132
  const home = process.env.HOME?.trim() || process.env.USERPROFILE?.trim();
124
133
  if (!home)
125
134
  return [];
@@ -44,6 +44,13 @@ const FALLBACK_STASHES = [
44
44
  defaultSelected: false,
45
45
  },
46
46
  ];
47
+ // ── Test seam ────────────────────────────────────────────────────────────────
48
+ // Swap-and-restore override. Inert in production; only tests call the setter.
49
+ let loadSetupStashesOverride;
50
+ /** TEST-ONLY. Swap the implementation of `loadSetupStashes`; pass undefined to restore. */
51
+ export function _setLoadSetupStashesForTests(fake) {
52
+ loadSetupStashesOverride = fake;
53
+ }
47
54
  // ── Loader ──────────────────────────────────────────────────────────────────
48
55
  /**
49
56
  * Fetch available stashes from the registry and map to SetupStashEntry[].
@@ -55,6 +62,11 @@ const FALLBACK_STASHES = [
55
62
  * @param timeoutMs Fetch timeout in ms (default: 4000).
56
63
  */
57
64
  export async function loadSetupStashes(registryUrl, timeoutMs = 4000) {
65
+ if (loadSetupStashesOverride)
66
+ return loadSetupStashesOverride(registryUrl, timeoutMs);
67
+ return loadSetupStashesReal(registryUrl, timeoutMs);
68
+ }
69
+ async function loadSetupStashesReal(registryUrl, timeoutMs = 4000) {
58
70
  try {
59
71
  const response = await fetch(registryUrl, {
60
72
  signal: AbortSignal.timeout(timeoutMs),
@@ -12,7 +12,7 @@ import { promises as dnsPromises } from "node:dns";
12
12
  import fs from "node:fs";
13
13
  import os from "node:os";
14
14
  import path from "node:path";
15
- import * as p from "@clack/prompts";
15
+ import * as p from "../cli/clack.js";
16
16
  import { akmInit } from "../commands/sources/init.js";
17
17
  import { detectServerDefault, isCiEnvironment, registerDefaultTasks } from "../commands/tasks/default-tasks.js";
18
18
  import { akmTasksAdd, akmTasksList, akmTasksSetEnabled, akmTasksSync } from "../commands/tasks/tasks.js";
@@ -3,6 +3,12 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
5
5
  import { resolveStorageLocations } from "../locations.js";
6
+ /**
7
+ * Busy-timeout (ms) for read-path telemetry writers. Small on purpose: a
8
+ * usage-event insert contending with a background reindex should be dropped,
9
+ * not waited on for the default 30s.
10
+ */
11
+ export const TELEMETRY_BUSY_TIMEOUT_MS = 250;
6
12
  /**
7
13
  * Scoped-resource (loan pattern) helper for the index database (`index.db`).
8
14
  *
@@ -32,9 +38,12 @@ import { resolveStorageLocations } from "../locations.js";
32
38
  * @param fn Receives the open index database; must finish all DB work before returning.
33
39
  * @returns Whatever `fn` returns.
34
40
  */
35
- export function withIndexDb(fn) {
41
+ export function withIndexDb(fn, opts) {
36
42
  const db = openExistingDatabase(resolveStorageLocations().indexDb);
37
43
  try {
44
+ if (opts?.busyTimeoutMs !== undefined) {
45
+ db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(opts.busyTimeoutMs))}`);
46
+ }
38
47
  return fn(db);
39
48
  }
40
49
  finally {
@@ -4,7 +4,14 @@
4
4
  import { CRON_BACKEND } from "./cron.js";
5
5
  import { LAUNCHD_BACKEND } from "./launchd.js";
6
6
  import { SCHTASKS_BACKEND } from "./schtasks.js";
7
+ let backendsOverrides;
8
+ /** TEST-ONLY. Swap backend selection; pass undefined to restore the real implementations. */
9
+ export function _setBackendsForTests(fakes) {
10
+ backendsOverrides = fakes;
11
+ }
7
12
  export function selectBackend(options = {}) {
13
+ if (backendsOverrides?.selectBackend)
14
+ return backendsOverrides.selectBackend(options);
8
15
  const platform = options.platform ?? process.platform;
9
16
  switch (platform) {
10
17
  case "win32":
@@ -16,6 +23,8 @@ export function selectBackend(options = {}) {
16
23
  }
17
24
  }
18
25
  export function backendNameForPlatform(platform = process.platform) {
26
+ if (backendsOverrides?.backendNameForPlatform)
27
+ return backendsOverrides.backendNameForPlatform(platform);
19
28
  if (platform === "win32")
20
29
  return "schtasks";
21
30
  if (platform === "darwin")
@@ -131,6 +131,11 @@ async function runCommandTask(input) {
131
131
  stdout: "pipe",
132
132
  stderr: "pipe",
133
133
  cwd: process.env.HOME ?? "/tmp",
134
+ // Stamp task-runner provenance so any akm invocation in the command tree
135
+ // records usage events as machine traffic, not user demand (DRIFT-6).
136
+ // A more specific stamp already in the environment (e.g. improve's
137
+ // AKM_EVENT_SOURCE=improve on its child spawns) still wins in children.
138
+ env: { ...process.env, AKM_EVENT_SOURCE: process.env.AKM_EVENT_SOURCE ?? "task" },
134
139
  });
135
140
  let timer;
136
141
  let timedOut = false;
@@ -351,6 +356,10 @@ async function runPromptTask(input) {
351
356
  timeoutMs: agentTimeoutMs,
352
357
  cwd: stashDir,
353
358
  ...agentOptions,
359
+ // Stamp task-runner provenance for any akm invocation the agent makes
360
+ // (DRIFT-6: agent-task traffic must not be recorded as user demand).
361
+ // Caller-supplied env still wins on conflicts.
362
+ env: { AKM_EVENT_SOURCE: "task", ...agentOptions?.env },
354
363
  });
355
364
  const finishedAt = now();
356
365
  const log = renderPromptLog({ task, profileName: profile.name, result });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.0-beta.52",
3
+ "version": "0.9.0-beta.54",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
6
6
  "keywords": [
@@ -58,9 +58,7 @@
58
58
  "sweep:tmp": "bun scripts/sweep-test-tmp.ts",
59
59
  "test": "bash scripts/test-unit.sh",
60
60
  "test:unit": "bash scripts/test-unit.sh",
61
- "test:integration": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests/integration ./tests/commands ./tests/workflows",
62
- "test:unit:shard": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests --path-ignore-patterns=tests/integration --shard=${SHARD:?set SHARD=k/N}",
63
- "test:integration:shard": "bun run sweep:tmp && bun test --isolate --timeout=30000 ./tests/integration ./tests/commands ./tests/workflows --shard=${SHARD:?set SHARD=k/N}",
61
+ "test:integration": "bun run sweep:tmp && bun test --timeout=30000 ./tests/integration",
64
62
  "test:node-smoke": "bun scripts/node-smoke.ts",
65
63
  "test:node-compat": "AKM_NODE_COMPAT_TESTS=1 bun test --timeout=120000 tests/integration/node-compat.test.ts",
66
64
  "test:time": "bun scripts/test-timing-report.ts",