akm-cli 0.10.261007-beta → 0.10.261009-beta

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 (43) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/STABILITY.md +10 -4
  3. package/dist/api.d.ts +6 -0
  4. package/dist/api.js +12 -1
  5. package/dist/assets/prompts/consolidate-pair-check.md +2 -0
  6. package/dist/assets/prompts/consolidate-pair-same-age.md +1 -0
  7. package/dist/assets/prompts/consolidate-pair-terms.md +6 -0
  8. package/dist/assets/prompts/consolidate-pair.md +4 -12
  9. package/dist/commands/health.js +1 -2
  10. package/dist/commands/improve/consolidate/pair-pass.js +59 -31
  11. package/dist/commands/improve/extract.js +18 -12
  12. package/dist/commands/improve/memory/memory-belief.js +2 -2
  13. package/dist/commands/proposal/repository.js +44 -13
  14. package/dist/commands/read/curate.js +77 -18
  15. package/dist/commands/read/search-cli.js +25 -11
  16. package/dist/commands/read/search.js +3 -3
  17. package/dist/commands/read/show.js +47 -1
  18. package/dist/core/adapter/adapters/akm-adapter.js +1 -0
  19. package/dist/core/adapter/adapters/llm-wiki-adapter.js +3 -5
  20. package/dist/core/adapter/adapters/okf-adapter.js +3 -16
  21. package/dist/core/asset/markdown-link-targets.js +43 -0
  22. package/dist/core/common.js +17 -2
  23. package/dist/core/config/config-io.js +14 -4
  24. package/dist/indexer/passes/metadata.js +17 -0
  25. package/dist/indexer/scan/doc-to-entry.js +1 -0
  26. package/dist/indexer/search/db-search.js +16 -7
  27. package/dist/indexer/search/search-hit-enrichers.js +3 -3
  28. package/dist/output/shapes/curate.js +3 -1
  29. package/dist/output/shapes/helpers.js +6 -0
  30. package/dist/output/text/show-format.js +7 -0
  31. package/dist/scripts/akm-migrate-node.js +1824 -324
  32. package/dist/scripts/akm-migrate.js +1824 -324
  33. package/dist/storage/repositories/index-entries-repository.js +16 -0
  34. package/dist/storage/repositories/index-fts-repository.js +28 -6
  35. package/dist/storage/repositories/index-vec-repository.js +19 -3
  36. package/docs/architecture/0.10-process-rules.md +4 -4
  37. package/docs/architecture/architecture.md +4 -0
  38. package/docs/architecture/persisted-data-compat.md +1 -1
  39. package/docs/reference/api.md +19 -1
  40. package/docs/reference/cli.md +56 -4
  41. package/docs/reference/data-and-telemetry.md +1 -1
  42. package/docs/reference/memory.md +7 -6
  43. package/package.json +2 -1
@@ -22,7 +22,7 @@ import { embed } from "../../llm/embedder.js";
22
22
  import { applyEmbeddingTemplate, resolveEmbeddingProfile } from "../../llm/embedders/profile.js";
23
23
  import { normalizeEmbeddingEndpoint } from "../../llm/embedders/remote.js";
24
24
  import { assertIndexPathReadable, closeDatabase, openExistingDatabase, openReadonlyExistingDatabase, } from "../../storage/repositories/index-connection.js";
25
- import { getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getEntryRefsAndTypes, } from "../../storage/repositories/index-entries-repository.js";
25
+ import { getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntriesByIds, getEntryById, getEntryCount, getEntryRefsAndTypes, } from "../../storage/repositories/index-entries-repository.js";
26
26
  import { searchFts } from "../../storage/repositories/index-fts-repository.js";
27
27
  import { getMeta } from "../../storage/repositories/index-meta-repository.js";
28
28
  import { getEmbeddingCount, searchVec } from "../../storage/repositories/index-vec-repository.js";
@@ -221,8 +221,14 @@ async function searchDatabase(db, input) {
221
221
  const typeFilter = searchType === "any" ? undefined : searchType;
222
222
  const startedAt = Date.now();
223
223
  // The query embedding request goes out first, so FTS runs while it is in flight.
224
- const vectorChannel = startVectorChannel(db, query, config);
225
- const lexical = searchFts(db, query, CHANNEL_DEPTH, typeFilter, defaultExcludes);
224
+ const filterCandidates = (ids) => {
225
+ const entries = getEntriesByIds(db, ids)
226
+ .filter(hasIndexedProvenance)
227
+ .filter((row) => (typeFilter ? row.entry.type === typeFilter : !defaultExcludes.includes(row.entry.type)));
228
+ return new Set(applyEntryFilters(entries, filterOptions).map((row) => row.id));
229
+ };
230
+ const vectorChannel = startVectorChannel(db, query, config, filterCandidates);
231
+ const lexical = searchFts(db, query, CHANNEL_DEPTH, typeFilter, defaultExcludes, filterCandidates);
226
232
  const vector = await vectorChannel;
227
233
  const embedMs = Date.now() - startedAt;
228
234
  const tRank0 = Date.now();
@@ -244,6 +250,7 @@ async function searchDatabase(db, input) {
244
250
  sources,
245
251
  config,
246
252
  db,
253
+ eligible: (row) => applyEntryFilters([row], filterOptions).length > 0,
247
254
  })));
248
255
  const mode = vector.warning ? "fts-fallback" : vector.neighbors ? "semantic" : "keyword";
249
256
  return { embedMs, rankMs, hits, mode, semanticWarning: vector.warning, feedbackWarning: feedback.warning };
@@ -371,6 +378,7 @@ async function enumerateEntries(opts) {
371
378
  sources,
372
379
  config,
373
380
  db,
381
+ eligible: (row) => applyEntryFilters([row], opts).length > 0,
374
382
  })));
375
383
  }
376
384
  /**
@@ -378,7 +386,7 @@ async function enumerateEntries(opts) {
378
386
  * source-narrowing → scope → proposed-quality → derived-twin belief
379
387
  * inheritance → belief filter.
380
388
  */
381
- function applyEntryFilters(items, opts) {
389
+ export function applyEntryFilters(items, opts) {
382
390
  const { filters } = opts;
383
391
  // Source filter: when the caller narrowed `sources` via `--from <name>`,
384
392
  // drop entries whose filePath does not live under any requested source. The
@@ -428,7 +436,7 @@ function inheritDerivedTwinBeliefStates(db, items) {
428
436
  }
429
437
  }
430
438
  }
431
- function matchBeliefFilter(beliefState, filter) {
439
+ export function matchBeliefFilter(beliefState, filter) {
432
440
  if (filter === "all")
433
441
  return true;
434
442
  // 03: the belief filter applies to ANY flagged entry, not just memories, so
@@ -452,7 +460,7 @@ function matchBeliefFilter(beliefState, filter) {
452
460
  * query overlaps it. A slow or failing embedder degrades the search to keyword
453
461
  * ranking with a warning after `embedding.queryTimeoutMs`.
454
462
  */
455
- function startVectorChannel(db, query, config) {
463
+ function startVectorChannel(db, query, config, filterCandidates) {
456
464
  if (config.semanticSearchMode === "off")
457
465
  return Promise.resolve({ neighbors: null });
458
466
  // A real-time completeness fact, not a cached verdict: skip the round trip
@@ -472,7 +480,7 @@ function startVectorChannel(db, query, config) {
472
480
  });
473
481
  const queryText = applyEmbeddingTemplate(resolveEmbeddingProfile(config.embedding).queryTemplate, query);
474
482
  return Promise.race([embed(queryText, config.embedding, controller.signal), timeout])
475
- .then((vector) => ({ neighbors: searchVec(db, vector, CHANNEL_DEPTH) }))
483
+ .then((vector) => ({ neighbors: searchVec(db, vector, CHANNEL_DEPTH, undefined, filterCandidates) }))
476
484
  .catch((error) => ({ neighbors: null, warning: buildVectorFallbackWarning(config, error) }))
477
485
  .finally(() => clearTimeout(timer));
478
486
  }
@@ -574,6 +582,7 @@ export async function buildDbHit(input) {
574
582
  stashDir: entryStashDir,
575
583
  bundleId: input.bundleId,
576
584
  db: input.db,
585
+ eligible: input.eligible,
577
586
  });
578
587
  return hit;
579
588
  }
@@ -17,7 +17,7 @@ export async function enrichSearchHit(hit, ctx) {
17
17
  renderer?.enrichSearchHit?.(hit, ctx.stashDir);
18
18
  }
19
19
  if (ctx.type === "memory" && ctx.db)
20
- surfaceDerivedMemory(hit, ctx.db, ctx.bundleId);
20
+ surfaceDerivedMemory(hit, ctx.db, ctx.bundleId, ctx.eligible);
21
21
  }
22
22
  /**
23
23
  * Phase 5A / Advantage D5 — when a parent memory has a `.derived` child
@@ -28,7 +28,7 @@ export async function enrichSearchHit(hit, ctx) {
28
28
  * The parent ref is preserved on the hit — only the surface text is
29
29
  * swapped, so links and provenance still point at the canonical parent.
30
30
  */
31
- function surfaceDerivedMemory(hit, db, bundleId) {
31
+ function surfaceDerivedMemory(hit, db, bundleId, eligible) {
32
32
  // Never recurse: a `.derived` hit is itself the child surface; leaving
33
33
  // it untouched also avoids `<parent>.derived.derived` chains.
34
34
  if (hit.name.toLowerCase().endsWith(".derived"))
@@ -38,7 +38,7 @@ function surfaceDerivedMemory(hit, db, bundleId) {
38
38
  // carries. INTERNAL lookup key into `getDerivedForParent`: the `derived_from`
39
39
  // column stores this same conceptId grammar.
40
40
  const derived = getDerivedForParent(db, `memories/${hit.name}`, bundleId);
41
- if (!derived)
41
+ if (!derived || (eligible && !eligible(derived)))
42
42
  return;
43
43
  // Swap description / tags from the derived child (SourceSearchHit carries no
44
44
  // `searchHints`).
@@ -5,7 +5,7 @@ import { capDescription, NORMAL_DESCRIPTION_LIMIT, pickFields } from "./helpers.
5
5
  // Curation is a small, high-signal top-N. Even at `brief` we keep `followUp`
6
6
  // (the actionable `akm show <ref>` command) and `reason` (why this asset was
7
7
  // selected) — these are the point of curate, unlike a bulk search listing.
8
- const BRIEF_FIELDS = ["source", "type", "name", "ref", "id", "supportRefs", "followUp", "reason"];
8
+ const BRIEF_FIELDS = ["source", "type", "name", "ref", "id", "supportRefs", "beliefState", "followUp", "reason"];
9
9
  const NORMAL_FIELDS = [
10
10
  "source",
11
11
  "type",
@@ -21,6 +21,7 @@ const NORMAL_FIELDS = [
21
21
  "parameters",
22
22
  "run",
23
23
  "supportRefs",
24
+ "beliefState",
24
25
  "followUp",
25
26
  "reason",
26
27
  "score",
@@ -37,6 +38,7 @@ const AGENT_FIELDS = [
37
38
  "id",
38
39
  "description",
39
40
  "supportRefs",
41
+ "beliefState",
40
42
  "followUp",
41
43
  "reason",
42
44
  "score",
@@ -386,6 +386,9 @@ export function shapeShowOutput(result, detail, shape = "human") {
386
386
  "steps",
387
387
  "keys",
388
388
  "links",
389
+ "evidence",
390
+ "evidenceSources",
391
+ "provenance",
389
392
  ...FRAGMENT_PROVENANCE_FIELDS,
390
393
  ...FRAGMENT_CONTEXT_FIELDS,
391
394
  ]);
@@ -418,6 +421,9 @@ export function shapeShowOutput(result, detail, shape = "human") {
418
421
  "activeRun",
419
422
  "keys",
420
423
  "links",
424
+ "evidence",
425
+ "evidenceSources",
426
+ "provenance",
421
427
  ...FRAGMENT_PROVENANCE_FIELDS,
422
428
  ...FRAGMENT_CONTEXT_FIELDS,
423
429
  // ref, path, and editable are always projected — at every --detail level,
@@ -20,6 +20,13 @@ export function formatShowPlain(r, detail) {
20
20
  }
21
21
  if (r.description)
22
22
  lines.push(`description: ${String(r.description)}`);
23
+ // Evidence is captured text: keep it on one line so it cannot forge other fields.
24
+ if (r.evidence)
25
+ lines.push(`evidence: ${String(r.evidence).replace(/\s+/g, " ")}`);
26
+ if (Array.isArray(r.evidenceSources))
27
+ lines.push(`evidenceSources: ${r.evidenceSources.join(", ")}`);
28
+ if (r.evidence && r.provenance)
29
+ lines.push(`provenance: ${JSON.stringify(r.provenance)}`);
23
30
  if (r.workflowTitle)
24
31
  lines.push(`workflowTitle: ${String(r.workflowTitle)}`);
25
32
  if (r.agent)