akm-cli 0.9.0-beta.56 → 0.9.0-beta.58

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 (53) hide show
  1. package/dist/assets/prompts/extract-session.md +5 -1
  2. package/dist/cli/config-migrate.js +7 -1
  3. package/dist/commands/config-cli.js +8 -11
  4. package/dist/commands/health/stash-exposure.js +46 -0
  5. package/dist/commands/health/windows.js +6 -7
  6. package/dist/commands/health.js +31 -10
  7. package/dist/commands/improve/collapse-detector.js +2 -1
  8. package/dist/commands/improve/consolidate.js +207 -159
  9. package/dist/commands/improve/distill/promote-memory.js +4 -3
  10. package/dist/commands/improve/distill/quality-gate.js +7 -4
  11. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  12. package/dist/commands/improve/distill.js +26 -12
  13. package/dist/commands/improve/extract-prompt.js +16 -2
  14. package/dist/commands/improve/extract.js +16 -8
  15. package/dist/commands/improve/improve-auto-accept.js +22 -1
  16. package/dist/commands/improve/loop-stages.js +7 -2
  17. package/dist/commands/improve/memory/memory-belief.js +14 -15
  18. package/dist/commands/improve/memory/memory-contradiction-detect.js +60 -32
  19. package/dist/commands/improve/memory/memory-improve.js +27 -27
  20. package/dist/commands/improve/preparation.js +4 -0
  21. package/dist/commands/improve/procedural.js +1 -0
  22. package/dist/commands/improve/recombine.js +1 -0
  23. package/dist/commands/improve/reflect-noise.js +1 -1
  24. package/dist/commands/improve/reflect.js +4 -3
  25. package/dist/commands/improve/shared.js +9 -6
  26. package/dist/commands/proposal/drain-policies.js +4 -2
  27. package/dist/commands/read/remember-cli.js +1 -1
  28. package/dist/commands/read/show.js +15 -0
  29. package/dist/commands/remember.js +11 -12
  30. package/dist/commands/sources/init.js +5 -1
  31. package/dist/commands/sources/stash-skeleton.js +34 -0
  32. package/dist/core/asset/frontmatter.js +22 -0
  33. package/dist/core/common.js +1 -15
  34. package/dist/core/config/config-io.js +10 -1
  35. package/dist/core/config/config-migration.js +2 -15
  36. package/dist/core/config/config-schema.js +15 -3
  37. package/dist/core/config/config.js +22 -14
  38. package/dist/core/paths.js +4 -4
  39. package/dist/core/time.js +53 -0
  40. package/dist/indexer/db/db.js +51 -46
  41. package/dist/indexer/indexer.js +77 -65
  42. package/dist/indexer/search/db-search.js +41 -6
  43. package/dist/indexer/search/ranking-contributors.js +14 -8
  44. package/dist/indexer/search/search-source.js +15 -3
  45. package/dist/integrations/agent/profiles.js +7 -1
  46. package/dist/llm/feature-gate.js +4 -8
  47. package/dist/output/renderers.js +4 -0
  48. package/dist/scripts/migrate-storage.js +84 -60
  49. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +6 -0
  50. package/dist/storage/repositories/registry-cache.js +2 -1
  51. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  52. package/dist/workflows/runtime/runs.js +6 -1
  53. package/package.json +1 -1
@@ -7452,6 +7452,11 @@ var init_dist = __esm(() => {
7452
7452
  $visitAsync = visit.visitAsync;
7453
7453
  });
7454
7454
 
7455
+ // src/core/asset/asset-serialize.ts
7456
+ var init_asset_serialize = __esm(() => {
7457
+ init_dist();
7458
+ });
7459
+
7455
7460
  // src/core/asset/frontmatter.ts
7456
7461
  function parseFrontmatter(raw) {
7457
7462
  const parsedBlock = parseFrontmatterBlock(raw);
@@ -7538,6 +7543,7 @@ function countLines(text) {
7538
7543
  }
7539
7544
  var init_frontmatter = __esm(() => {
7540
7545
  init_dist();
7546
+ init_asset_serialize();
7541
7547
  });
7542
7548
 
7543
7549
  // src/core/asset/markdown.ts
@@ -8602,8 +8608,8 @@ function getConfigDir(env = process.env, platform = process.platform) {
8602
8608
  }
8603
8609
  return path3.join(home, ".config", "akm");
8604
8610
  }
8605
- function getConfigPath() {
8606
- return path3.join(getConfigDir(), "config.json");
8611
+ function getConfigPath(env = process.env) {
8612
+ return path3.join(getConfigDir(env), "config.json");
8607
8613
  }
8608
8614
  function getCacheDir(env = process.env) {
8609
8615
  const override = env.AKM_CACHE_DIR?.trim();
@@ -8665,8 +8671,8 @@ function getDataDir(env = process.env, platform = process.platform) {
8665
8671
  return path3.join("/tmp", "akm-data");
8666
8672
  return path3.join(home, ".local", "share", "akm");
8667
8673
  }
8668
- function getDbPath() {
8669
- return path3.join(getDataDir(), "index.db");
8674
+ function getDbPath(env = process.env) {
8675
+ return path3.join(getDataDir(env), "index.db");
8670
8676
  }
8671
8677
  var init_paths = __esm(() => {
8672
8678
  init_common();
@@ -8684,11 +8690,7 @@ function writeFileAtomic(target, content, mode) {
8684
8690
  const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
8685
8691
  const fd = fs4.openSync(tmp, "w", mode ?? 384);
8686
8692
  try {
8687
- if (typeof content === "string") {
8688
- fs4.writeSync(fd, content);
8689
- } else {
8690
- fs4.writeSync(fd, content);
8691
- }
8693
+ fs4.writeSync(fd, typeof content === "string" ? Buffer.from(content) : content);
8692
8694
  try {
8693
8695
  fs4.fdatasyncSync(fd);
8694
8696
  } catch {}
@@ -9838,6 +9840,29 @@ function ensureUsageEventsSchema(db) {
9838
9840
  `);
9839
9841
  }
9840
9842
 
9843
+ // src/storage/repositories/registry-index-cache-repository.ts
9844
+ function upsertRegistryIndexCache(db, registryUrl, indexJson, opts) {
9845
+ db.prepare(`
9846
+ INSERT INTO registry_index_cache (registry_url, fetched_at, etag, last_modified, index_json)
9847
+ VALUES (?, ?, ?, ?, ?)
9848
+ ON CONFLICT(registry_url) DO UPDATE SET
9849
+ fetched_at = excluded.fetched_at,
9850
+ etag = excluded.etag,
9851
+ last_modified = excluded.last_modified,
9852
+ index_json = excluded.index_json
9853
+ `).run(registryUrl, new Date().toISOString(), opts?.etag ?? null, opts?.lastModified ?? null, indexJson);
9854
+ }
9855
+ function getRegistryIndexCache(db, registryUrl, maxAgeMs = 3600000) {
9856
+ const row = db.prepare(`SELECT fetched_at, etag, last_modified, index_json
9857
+ FROM registry_index_cache WHERE registry_url = ?`).get(registryUrl);
9858
+ if (!row)
9859
+ return;
9860
+ const fetchedAt = Date.parse(row.fetched_at);
9861
+ if (Number.isNaN(fetchedAt) || Date.now() - fetchedAt > maxAgeMs)
9862
+ return;
9863
+ return { indexJson: row.index_json, etag: row.etag, lastModified: row.last_modified };
9864
+ }
9865
+
9841
9866
  // src/indexer/db/db.ts
9842
9867
  import { createRequire as createRequire4 } from "node:module";
9843
9868
  function openExistingDatabase(dbPath) {
@@ -10322,12 +10347,15 @@ function backupExistingConfig(configPath) {
10322
10347
  if (!fs7.existsSync(configPath))
10323
10348
  return;
10324
10349
  const backupDir = path8.join(getCacheDir(), "config-backups");
10325
- fs7.mkdirSync(backupDir, { recursive: true });
10350
+ fs7.mkdirSync(backupDir, { recursive: true, mode: 448 });
10351
+ fs7.chmodSync(backupDir, 448);
10326
10352
  const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
10327
10353
  const timestamped = path8.join(backupDir, `config-${timestamp}.json`);
10328
10354
  const latest = path8.join(backupDir, "config.latest.json");
10329
10355
  fs7.copyFileSync(configPath, timestamped);
10330
10356
  fs7.copyFileSync(configPath, latest);
10357
+ fs7.chmodSync(timestamped, 384);
10358
+ fs7.chmodSync(latest, 384);
10331
10359
  pruneOldBackups(backupDir);
10332
10360
  return { timestamped, latest };
10333
10361
  }
@@ -11179,7 +11207,7 @@ var init_opencode = __esm(() => {
11179
11207
  // src/integrations/agent/profiles.ts
11180
11208
  var COMMON_PASSTHROUGH, BUILTINS, HEADLESS_BUILTINS, BUILTIN_AGENT_PROFILE_NAMES;
11181
11209
  var init_profiles = __esm(() => {
11182
- COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR"];
11210
+ COMMON_PASSTHROUGH = ["HOME", "PATH", "USER", "LANG", "LC_ALL", "TERM", "TMPDIR", "AKM_EVENT_SOURCE"];
11183
11211
  BUILTINS = {
11184
11212
  opencode: {
11185
11213
  name: "opencode",
@@ -11524,12 +11552,6 @@ function migrateConfigShape(raw, opts) {
11524
11552
  if (typeof llmFeatures.metadata_enhance === "boolean")
11525
11553
  me.enabled = llmFeatures.metadata_enhance;
11526
11554
  }
11527
- if ("curate_rerank" in llmFeatures) {
11528
- const search = getObj(result, "search");
11529
- const cr = getObj(search, "curateRerank");
11530
- if (typeof llmFeatures.curate_rerank === "boolean")
11531
- cr.enabled = llmFeatures.curate_rerank;
11532
- }
11533
11555
  if ("lesson_quality_gate" in llmFeatures) {
11534
11556
  const distill = getImproveProcess(result, "distill");
11535
11557
  const qg = getObj(distill, "qualityGate");
@@ -11657,15 +11679,6 @@ function migrateConfigShape(raw, opts) {
11657
11679
  }
11658
11680
  if (isObj(features.search)) {
11659
11681
  const fsearch = features.search;
11660
- if ("curate_rerank" in fsearch) {
11661
- const search = getObj(result, "search");
11662
- const cr = getObj(search, "curateRerank");
11663
- const val = fsearch.curate_rerank;
11664
- if (typeof val === "boolean")
11665
- cr.enabled = val;
11666
- else if (isObj(val) && typeof val.enabled === "boolean")
11667
- cr.enabled = val.enabled;
11668
- }
11669
11682
  const knownSearchKeys = new Set(["curate_rerank"]);
11670
11683
  for (const [legacyKey, legacyVal] of Object.entries(fsearch)) {
11671
11684
  if (knownSearchKeys.has(legacyKey))
@@ -15800,7 +15813,7 @@ var init_config_types = __esm(() => {
15800
15813
  });
15801
15814
 
15802
15815
  // src/core/config/config-schema.ts
15803
- 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;
15816
+ var positiveInt, nonNegativeNumber, nonEmptyString, httpUrl, FEEDBACK_FAILURE_MODES, 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;
15804
15817
  var init_config_schema = __esm(() => {
15805
15818
  init_zod();
15806
15819
  init_config_types();
@@ -15810,6 +15823,13 @@ var init_config_schema = __esm(() => {
15810
15823
  httpUrl = exports_external.string().refine((v) => v.startsWith("http://") || v.startsWith("https://"), {
15811
15824
  message: "endpoint must start with http:// or https://"
15812
15825
  });
15826
+ FEEDBACK_FAILURE_MODES = [
15827
+ "incorrect",
15828
+ "outdated",
15829
+ "dangerous",
15830
+ "incomplete",
15831
+ "redundant"
15832
+ ];
15813
15833
  LlmCapabilitiesSchema = exports_external.object({
15814
15834
  structuredOutput: exports_external.boolean().optional()
15815
15835
  }).passthrough();
@@ -16047,7 +16067,6 @@ var init_config_schema = __esm(() => {
16047
16067
  SearchConfigSchema = exports_external.object({
16048
16068
  minScore: nonNegativeNumber.optional(),
16049
16069
  defaultExcludeTypes: exports_external.array(nonEmptyString).optional(),
16050
- curateRerank: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
16051
16070
  graphBoost: SearchGraphBoostSchema.optional()
16052
16071
  }).passthrough();
16053
16072
  FeedbackConfigSchema = exports_external.object({
@@ -16348,6 +16367,7 @@ __export(exports_config, {
16348
16367
  loadConfig: () => loadConfig,
16349
16368
  getSources: () => getSources,
16350
16369
  getIndexPassConfig: () => getIndexPassConfig,
16370
+ getImproveProcessConfig: () => getImproveProcessConfig,
16351
16371
  getEffectiveRegistries: () => getEffectiveRegistries,
16352
16372
  getDefaultLlmConfig: () => getDefaultLlmConfig,
16353
16373
  _setSaveConfigForTests: () => _setSaveConfigForTests,
@@ -16444,6 +16464,12 @@ function getDefaultLlmConfig(config) {
16444
16464
  return;
16445
16465
  return config.profiles?.llm?.[defaultName];
16446
16466
  }
16467
+ function getImproveProcessConfig(config, processName, activeProfile) {
16468
+ const fromActiveProfile = activeProfile?.processes?.[processName];
16469
+ if (fromActiveProfile !== undefined)
16470
+ return fromActiveProfile;
16471
+ return config.profiles?.improve?.default?.processes?.[processName];
16472
+ }
16447
16473
  function maybeAutoMigrateConfigFile(configPath, text) {
16448
16474
  let obj;
16449
16475
  try {
@@ -16672,7 +16698,7 @@ function isFile(filePath) {
16672
16698
  return false;
16673
16699
  }
16674
16700
  }
16675
- 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;
16701
+ var 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;
16676
16702
  var init_config2 = __esm(() => {
16677
16703
  init_errors();
16678
16704
  init_config_io();
@@ -16682,14 +16708,8 @@ var init_config2 = __esm(() => {
16682
16708
  init_paths();
16683
16709
  init_warn();
16684
16710
  init_config_types();
16711
+ init_config_schema();
16685
16712
  init_config_sources();
16686
- FEEDBACK_FAILURE_MODES = [
16687
- "incorrect",
16688
- "outdated",
16689
- "dangerous",
16690
- "incomplete",
16691
- "redundant"
16692
- ];
16693
16713
  DEFAULT_CONFIG = {
16694
16714
  semanticSearchMode: "auto",
16695
16715
  registries: [
@@ -16750,6 +16770,7 @@ __export(exports_db, {
16750
16770
  getEmbeddingCount: () => getEmbeddingCount,
16751
16771
  getEmbeddableEntryCount: () => getEmbeddableEntryCount,
16752
16772
  getDerivedForParent: () => getDerivedForParent,
16773
+ getBaseBeliefStatesForDerivedTwins: () => getBaseBeliefStatesForDerivedTwins,
16753
16774
  getAllEntriesForEmbedding: () => getAllEntriesForEmbedding,
16754
16775
  getAllEntries: () => getAllEntries,
16755
16776
  findEntryIdByRef: () => findEntryIdByRef,
@@ -16917,6 +16938,30 @@ function getDerivedForParent(db, parentRef) {
16917
16938
  return null;
16918
16939
  }
16919
16940
  }
16941
+ function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
16942
+ const out = new Map;
16943
+ if (twinIds.length === 0)
16944
+ return out;
16945
+ for (let i = 0;i < twinIds.length; i += SQLITE_CHUNK_SIZE) {
16946
+ const chunk = twinIds.slice(i, i + SQLITE_CHUNK_SIZE);
16947
+ const placeholders = chunk.map(() => "?").join(",");
16948
+ bestEffort(() => {
16949
+ const rows = db.prepare(`SELECT twin.id AS twin_id, json_extract(base.entry_json, '$.beliefState') AS belief
16950
+ FROM entries twin
16951
+ JOIN entries base
16952
+ ON base.entry_type = 'memory'
16953
+ AND base.entry_key = substr(twin.entry_key, 1, length(twin.entry_key) - length('.derived'))
16954
+ WHERE twin.id IN (${placeholders})
16955
+ AND twin.entry_key LIKE '%.derived'
16956
+ AND json_extract(base.entry_json, '$.beliefState') IS NOT NULL`).all(...chunk);
16957
+ for (const r of rows) {
16958
+ if (typeof r.belief === "string" && r.belief.trim().length > 0)
16959
+ out.set(r.twin_id, r.belief.trim());
16960
+ }
16961
+ }, "legacy DB / entry_json without beliefState — treat as no twin inheritance");
16962
+ }
16963
+ return out;
16964
+ }
16920
16965
  function getPositiveFeedbackCountsByIds(db, ids) {
16921
16966
  const result = new Map;
16922
16967
  if (ids.length === 0)
@@ -17176,7 +17221,8 @@ function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
17176
17221
  });
17177
17222
  }
17178
17223
  return results;
17179
- } catch {
17224
+ } catch (err) {
17225
+ warn("[db] runFtsQuery failed:", err instanceof Error ? err.message : String(err));
17180
17226
  return [];
17181
17227
  }
17182
17228
  }
@@ -17255,8 +17301,7 @@ function getEntryCount(db) {
17255
17301
  return row.cnt;
17256
17302
  }
17257
17303
  function getEmbeddableEntryCount(db) {
17258
- const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get();
17259
- return row.cnt;
17304
+ return getEntryCount(db);
17260
17305
  }
17261
17306
  function getEmbeddingCount(db) {
17262
17307
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
@@ -17588,27 +17633,6 @@ function relinkUsageEvents(db) {
17588
17633
  relinkTx();
17589
17634
  }, "usage_events table may not exist yet during entry_id re-resolution");
17590
17635
  }
17591
- function upsertRegistryIndexCache(db, registryUrl, indexJson, opts) {
17592
- db.prepare(`
17593
- INSERT INTO registry_index_cache (registry_url, fetched_at, etag, last_modified, index_json)
17594
- VALUES (?, ?, ?, ?, ?)
17595
- ON CONFLICT(registry_url) DO UPDATE SET
17596
- fetched_at = excluded.fetched_at,
17597
- etag = excluded.etag,
17598
- last_modified = excluded.last_modified,
17599
- index_json = excluded.index_json
17600
- `).run(registryUrl, new Date().toISOString(), opts?.etag ?? null, opts?.lastModified ?? null, indexJson);
17601
- }
17602
- function getRegistryIndexCache(db, registryUrl, maxAgeMs = 3600000) {
17603
- const row = db.prepare(`SELECT fetched_at, etag, last_modified, index_json
17604
- FROM registry_index_cache WHERE registry_url = ?`).get(registryUrl);
17605
- if (!row)
17606
- return;
17607
- const fetchedAt = Date.parse(row.fetched_at);
17608
- if (Number.isNaN(fetchedAt) || Date.now() - fetchedAt > maxAgeMs)
17609
- return;
17610
- return { indexJson: row.index_json, etag: row.etag, lastModified: row.last_modified };
17611
- }
17612
17636
  function collectTagSetFromEntries(db, entryType) {
17613
17637
  const tags = new Set;
17614
17638
  const stmt = entryType ? db.prepare("SELECT entry_json FROM entries WHERE entry_type = ?") : db.prepare("SELECT entry_json FROM entries");
@@ -7443,6 +7443,11 @@ var init_dist = __esm(() => {
7443
7443
  $visitAsync = visit.visitAsync;
7444
7444
  });
7445
7445
 
7446
+ // src/core/asset/asset-serialize.ts
7447
+ var init_asset_serialize = __esm(() => {
7448
+ init_dist();
7449
+ });
7450
+
7446
7451
  // src/core/asset/frontmatter.ts
7447
7452
  function parseFrontmatter(raw) {
7448
7453
  const parsedBlock = parseFrontmatterBlock(raw);
@@ -7529,6 +7534,7 @@ function countLines(text) {
7529
7534
  }
7530
7535
  var init_frontmatter = __esm(() => {
7531
7536
  init_dist();
7537
+ init_asset_serialize();
7532
7538
  });
7533
7539
 
7534
7540
  // src/core/asset/markdown.ts
@@ -2,7 +2,8 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import { rethrowIfTestIsolationError } from "../../core/errors.js";
5
- import { closeDatabase, getRegistryIndexCache, openIndexDatabase, upsertRegistryIndexCache } from "../../indexer/db/db.js";
5
+ import { closeDatabase, openIndexDatabase } from "../../indexer/db/db.js";
6
+ import { getRegistryIndexCache, upsertRegistryIndexCache } from "./registry-index-cache-repository.js";
6
7
  /**
7
8
  * RAII-style lifecycle helper for the registry cache DB. Opens the DB (treating
8
9
  * a failed open exactly like the legacy fall-through: the bun-test isolation
@@ -0,0 +1,46 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Upsert a registry index cache entry in index.db.
6
+ *
7
+ * @param db - Open index.db connection (from openDatabase / openExistingDatabase).
8
+ * @param registryUrl - Canonical URL of the registry (used as primary key).
9
+ * @param indexJson - Serialised registry index document (JSON string).
10
+ * @param opts.etag - HTTP ETag from the response (optional).
11
+ * @param opts.lastModified - HTTP Last-Modified from the response (optional).
12
+ */
13
+ export function upsertRegistryIndexCache(db, registryUrl, indexJson, opts) {
14
+ db.prepare(`
15
+ INSERT INTO registry_index_cache (registry_url, fetched_at, etag, last_modified, index_json)
16
+ VALUES (?, ?, ?, ?, ?)
17
+ ON CONFLICT(registry_url) DO UPDATE SET
18
+ fetched_at = excluded.fetched_at,
19
+ etag = excluded.etag,
20
+ last_modified = excluded.last_modified,
21
+ index_json = excluded.index_json
22
+ `).run(registryUrl, new Date().toISOString(), opts?.etag ?? null, opts?.lastModified ?? null, indexJson);
23
+ }
24
+ /**
25
+ * Look up a cached registry index entry from index.db.
26
+ * Returns undefined when not found or when the entry is older than `maxAgeMs`.
27
+ *
28
+ * TTL check: if `Date.now() - new Date(fetched_at).getTime() > maxAgeMs` the
29
+ * entry is considered a cache miss and undefined is returned.
30
+ *
31
+ * @param db - Open index.db connection.
32
+ * @param registryUrl - Canonical URL of the registry (primary key).
33
+ * @param maxAgeMs - Maximum age in milliseconds before the entry is stale (default: 1 hour).
34
+ */
35
+ export function getRegistryIndexCache(db, registryUrl, maxAgeMs = 3_600_000 /* 1 hour */) {
36
+ const row = db
37
+ .prepare(`SELECT fetched_at, etag, last_modified, index_json
38
+ FROM registry_index_cache WHERE registry_url = ?`)
39
+ .get(registryUrl);
40
+ if (!row)
41
+ return undefined;
42
+ const fetchedAt = Date.parse(row.fetched_at);
43
+ if (Number.isNaN(fetchedAt) || Date.now() - fetchedAt > maxAgeMs)
44
+ return undefined;
45
+ return { indexJson: row.index_json, etag: row.etag, lastModified: row.last_modified };
46
+ }
@@ -67,10 +67,15 @@ export async function startWorkflowRun(ref, params = {}, options) {
67
67
  })));
68
68
  });
69
69
  const result = await getWorkflowStatus(runId);
70
+ // 07 P1-B: emit only the run id + status — NOT the raw workflowTitle (which
71
+ // comes verbatim from the workflow asset's frontmatter and is therefore
72
+ // attacker-influenceable). Keeping raw titles out of the events stream
73
+ // shrinks the injectable footprint for any consumer that re-surfaces events
74
+ // into agent context.
70
75
  appendEvent({
71
76
  eventType: "workflow_started",
72
77
  ref: ref,
73
- metadata: { runId: result.run.id, title: result.run.workflowTitle },
78
+ metadata: { runId: result.run.id, status: result.run.status },
74
79
  });
75
80
  return result;
76
81
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.0-beta.56",
3
+ "version": "0.9.0-beta.58",
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": [