akm-cli 0.9.0-beta.57 → 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.
- package/dist/assets/prompts/extract-session.md +5 -1
- package/dist/cli/config-migrate.js +7 -1
- package/dist/commands/config-cli.js +8 -11
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/windows.js +6 -7
- package/dist/commands/health.js +31 -10
- package/dist/commands/improve/collapse-detector.js +2 -1
- package/dist/commands/improve/consolidate.js +207 -159
- package/dist/commands/improve/distill/promote-memory.js +4 -3
- package/dist/commands/improve/distill/quality-gate.js +7 -4
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +26 -12
- package/dist/commands/improve/extract-prompt.js +16 -2
- package/dist/commands/improve/extract.js +16 -8
- package/dist/commands/improve/improve-auto-accept.js +22 -1
- package/dist/commands/improve/loop-stages.js +7 -2
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +60 -32
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/preparation.js +4 -0
- package/dist/commands/improve/procedural.js +1 -0
- package/dist/commands/improve/recombine.js +1 -0
- package/dist/commands/improve/reflect-noise.js +1 -1
- package/dist/commands/improve/reflect.js +4 -3
- package/dist/commands/improve/shared.js +9 -6
- package/dist/commands/proposal/drain-policies.js +4 -2
- package/dist/commands/read/remember-cli.js +1 -1
- package/dist/commands/read/show.js +15 -0
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/init.js +5 -1
- package/dist/commands/sources/stash-skeleton.js +34 -0
- package/dist/core/asset/frontmatter.js +22 -0
- package/dist/core/common.js +1 -15
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +2 -15
- package/dist/core/config/config-schema.js +15 -3
- package/dist/core/config/config.js +22 -14
- package/dist/core/paths.js +4 -4
- package/dist/core/time.js +53 -0
- package/dist/indexer/db/db.js +51 -46
- package/dist/indexer/indexer.js +77 -65
- package/dist/indexer/search/db-search.js +41 -6
- package/dist/indexer/search/ranking-contributors.js +14 -8
- package/dist/indexer/search/search-source.js +15 -3
- package/dist/llm/feature-gate.js +4 -8
- package/dist/output/renderers.js +4 -0
- package/dist/scripts/migrate-storage.js +83 -59
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +6 -0
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/workflows/runtime/runs.js +6 -1
- package/package.json +1 -1
package/dist/indexer/indexer.js
CHANGED
|
@@ -331,7 +331,6 @@ async function akmIndexReal(options) {
|
|
|
331
331
|
walkWarnings: [],
|
|
332
332
|
dirsNeedingLlm: [],
|
|
333
333
|
embeddingResult: null,
|
|
334
|
-
graphExtractionResult: null,
|
|
335
334
|
};
|
|
336
335
|
onProgress({
|
|
337
336
|
phase: "summary",
|
|
@@ -399,14 +398,21 @@ async function akmIndexReal(options) {
|
|
|
399
398
|
}
|
|
400
399
|
});
|
|
401
400
|
}
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Phase 1 (async): walk every source directory and pre-generate all metadata
|
|
403
|
+
* outside any transaction, producing the per-directory scan records that
|
|
404
|
+
* {@link persistDirRecords} later writes.
|
|
405
|
+
*
|
|
406
|
+
* generateMetadataFlat is async (uses dynamic import for the matcher/renderer
|
|
407
|
+
* registry), so it cannot run inside a db.transaction() callback — hence the
|
|
408
|
+
* split into a pure-ish scan pass and a synchronous persist pass.
|
|
409
|
+
*/
|
|
410
|
+
async function scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress) {
|
|
404
411
|
let scannedDirs = 0;
|
|
405
412
|
let skippedDirs = 0;
|
|
406
413
|
let generatedCount = 0;
|
|
407
414
|
const warnings = [];
|
|
408
415
|
const seenPaths = new Set();
|
|
409
|
-
const dirsNeedingLlm = [];
|
|
410
416
|
const dirRecords = [];
|
|
411
417
|
let processedDirs = 0;
|
|
412
418
|
let priorDirsChanged = hadRemovedSources;
|
|
@@ -426,6 +432,53 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
426
432
|
reportScanProgress(`${kind === "scan" ? "Rescanning" : "Skipping"} ${path.relative(currentStashDir, dirPath) || "."} ` +
|
|
427
433
|
`from ${currentStashDir}: ${reason.kind}${detail}${rowInfo}`);
|
|
428
434
|
};
|
|
435
|
+
// Duplicate-directory guard shared by the wiki-root and normal branches. A
|
|
436
|
+
// dir may surface via multiple stash roots; only the first occurrence is
|
|
437
|
+
// indexed, and the later ones are recorded as skips. Returns true when the
|
|
438
|
+
// dir was already seen (caller should skip further processing).
|
|
439
|
+
const markSeenOrSkipDuplicate = (dirPath, currentStashDir, files) => {
|
|
440
|
+
const resolved = path.resolve(dirPath);
|
|
441
|
+
if (seenPaths.has(resolved)) {
|
|
442
|
+
const reason = { kind: "duplicate-dir" };
|
|
443
|
+
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
|
|
444
|
+
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
seenPaths.add(resolved);
|
|
448
|
+
return false;
|
|
449
|
+
};
|
|
450
|
+
// Incremental freshness gate shared by both branches: consult the persisted
|
|
451
|
+
// dir state and record either a skip (unchanged + eligible for incremental
|
|
452
|
+
// skip) or a scan record carrying the candidate stash.
|
|
453
|
+
const recordFreshnessDecision = (dirPath, currentStashDir, stateFiles, stash) => {
|
|
454
|
+
const previousState = getDirIndexState(db, dirPath, stateFiles, builtAtMs);
|
|
455
|
+
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
456
|
+
skippedDirs++;
|
|
457
|
+
dirRecords.push({
|
|
458
|
+
dirPath,
|
|
459
|
+
currentStashDir,
|
|
460
|
+
files: stateFiles,
|
|
461
|
+
stash: null,
|
|
462
|
+
skip: true,
|
|
463
|
+
reason: previousState.reason,
|
|
464
|
+
});
|
|
465
|
+
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
scannedDirs++;
|
|
469
|
+
priorDirsChanged = true;
|
|
470
|
+
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
471
|
+
dirRecords.push({
|
|
472
|
+
dirPath,
|
|
473
|
+
currentStashDir,
|
|
474
|
+
files: stateFiles,
|
|
475
|
+
stash,
|
|
476
|
+
skip: false,
|
|
477
|
+
reason,
|
|
478
|
+
persistedRowCount: previousState.persistedRowCount,
|
|
479
|
+
});
|
|
480
|
+
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
481
|
+
};
|
|
429
482
|
for (const sourceAdded of allSourceEntries) {
|
|
430
483
|
const currentStashDir = sourceAdded.path;
|
|
431
484
|
const fileContexts = walkStashFlat(currentStashDir);
|
|
@@ -463,33 +516,9 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
463
516
|
}
|
|
464
517
|
}
|
|
465
518
|
for (const [dirPath, { files, entries }] of wikiDirGroups) {
|
|
466
|
-
if (
|
|
467
|
-
const reason = { kind: "duplicate-dir" };
|
|
468
|
-
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
|
|
469
|
-
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
seenPaths.add(path.resolve(dirPath));
|
|
473
|
-
const previousState = getDirIndexState(db, dirPath, files, builtAtMs);
|
|
474
|
-
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
475
|
-
skippedDirs++;
|
|
476
|
-
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason: previousState.reason });
|
|
477
|
-
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
519
|
+
if (markSeenOrSkipDuplicate(dirPath, currentStashDir, files))
|
|
478
520
|
continue;
|
|
479
|
-
}
|
|
480
|
-
scannedDirs++;
|
|
481
|
-
priorDirsChanged = true;
|
|
482
|
-
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
483
|
-
dirRecords.push({
|
|
484
|
-
dirPath,
|
|
485
|
-
currentStashDir,
|
|
486
|
-
files,
|
|
487
|
-
stash: { entries },
|
|
488
|
-
skip: false,
|
|
489
|
-
reason,
|
|
490
|
-
persistedRowCount: previousState.persistedRowCount,
|
|
491
|
-
});
|
|
492
|
-
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
521
|
+
recordFreshnessDecision(dirPath, currentStashDir, files, { entries });
|
|
493
522
|
}
|
|
494
523
|
continue;
|
|
495
524
|
}
|
|
@@ -504,13 +533,8 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
504
533
|
}
|
|
505
534
|
for (const [dirPath, files] of dirGroups) {
|
|
506
535
|
const indexableFiles = files.filter((file) => shouldIndexStashFile(currentStashDir, file));
|
|
507
|
-
if (
|
|
508
|
-
const reason = { kind: "duplicate-dir" };
|
|
509
|
-
dirRecords.push({ dirPath, currentStashDir, files: indexableFiles, stash: null, skip: true, reason });
|
|
510
|
-
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
536
|
+
if (markSeenOrSkipDuplicate(dirPath, currentStashDir, indexableFiles))
|
|
511
537
|
continue;
|
|
512
|
-
}
|
|
513
|
-
seenPaths.add(path.resolve(dirPath));
|
|
514
538
|
if (indexableFiles.length === 0) {
|
|
515
539
|
skippedDirs++;
|
|
516
540
|
const reason = { kind: "no-indexable-files" };
|
|
@@ -540,37 +564,17 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
540
564
|
if (generated.entries.length > 0) {
|
|
541
565
|
generatedCount += generated.entries.length;
|
|
542
566
|
}
|
|
543
|
-
|
|
544
|
-
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
545
|
-
skippedDirs++;
|
|
546
|
-
dirRecords.push({
|
|
547
|
-
dirPath,
|
|
548
|
-
currentStashDir,
|
|
549
|
-
files: staleFiles,
|
|
550
|
-
stash: null,
|
|
551
|
-
skip: true,
|
|
552
|
-
reason: previousState.reason,
|
|
553
|
-
});
|
|
554
|
-
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
scannedDirs++;
|
|
558
|
-
priorDirsChanged = true;
|
|
559
|
-
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
560
|
-
dirRecords.push({
|
|
561
|
-
dirPath,
|
|
562
|
-
currentStashDir,
|
|
563
|
-
files: staleFiles,
|
|
564
|
-
stash,
|
|
565
|
-
skip: false,
|
|
566
|
-
reason,
|
|
567
|
-
persistedRowCount: previousState.persistedRowCount,
|
|
568
|
-
});
|
|
569
|
-
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
567
|
+
recordFreshnessDecision(dirPath, currentStashDir, staleFiles, stash);
|
|
570
568
|
}
|
|
571
569
|
}
|
|
572
|
-
|
|
573
|
-
|
|
570
|
+
return { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings };
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Phase 2 (sync): write all pre-generated scan records inside a single
|
|
574
|
+
* transaction, returning the directories that still need LLM enrichment.
|
|
575
|
+
*/
|
|
576
|
+
function persistDirRecords(db, dirRecords, doFullDelete, warnings) {
|
|
577
|
+
const dirsNeedingLlm = [];
|
|
574
578
|
// Cross-stash dedup: track indexed assets by content identity
|
|
575
579
|
// (type + filename + description) so the same asset from a lower-priority
|
|
576
580
|
// stash root is skipped when a higher-priority root already covers it.
|
|
@@ -692,6 +696,14 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
692
696
|
}
|
|
693
697
|
});
|
|
694
698
|
insertTransaction();
|
|
699
|
+
return { dirsNeedingLlm };
|
|
700
|
+
}
|
|
701
|
+
async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, doFullDelete = false, onProgress) {
|
|
702
|
+
// Phase 1 (async): walk directories and pre-generate all metadata outside the
|
|
703
|
+
// transaction.
|
|
704
|
+
const { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings } = await scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress);
|
|
705
|
+
// Phase 2 (sync): write all pre-generated metadata inside a single transaction.
|
|
706
|
+
const { dirsNeedingLlm } = persistDirRecords(db, dirRecords, doFullDelete, warnings);
|
|
695
707
|
return { scannedDirs, skippedDirs, generatedCount, warnings, dirsNeedingLlm };
|
|
696
708
|
}
|
|
697
709
|
async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal, reEnrich = false) {
|
|
@@ -20,7 +20,7 @@ import { defaultRendererRegistry } from "../../core/asset/asset-registry.js";
|
|
|
20
20
|
import { getDbPath } from "../../core/paths.js";
|
|
21
21
|
import { warn } from "../../core/warn.js";
|
|
22
22
|
import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
|
|
23
|
-
import { closeDatabase, getAllEntries, getEntryById, getEntryCount, getMeta, getPositiveFeedbackCountsByIds, openExistingDatabase, sanitizeFtsQuery, searchFts, searchVec, } from "../db/db.js";
|
|
23
|
+
import { closeDatabase, getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getMeta, getPositiveFeedbackCountsByIds, openExistingDatabase, sanitizeFtsQuery, searchFts, searchVec, } from "../db/db.js";
|
|
24
24
|
import { ensureIndex } from "../ensure-index.js";
|
|
25
25
|
import { collectGraphRelatedHit, computeGraphBoost, loadGraphBoostContext, } from "../graph/graph-boost.js";
|
|
26
26
|
import { isProposedQuality } from "../passes/metadata.js";
|
|
@@ -201,7 +201,11 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
201
201
|
const qualityFiltered = includeProposed
|
|
202
202
|
? scopeFiltered
|
|
203
203
|
: scopeFiltered.filter((ie) => !isProposedQuality(ie.entry.quality));
|
|
204
|
-
|
|
204
|
+
// 03-R3: derived twins inherit their base's demoting belief state here too,
|
|
205
|
+
// so the belief FILTER (and the reported hit state) stays consistent on the
|
|
206
|
+
// enumerate/browse path — not only on the FTS-scored path below.
|
|
207
|
+
inheritDerivedTwinBeliefStates(db, qualityFiltered);
|
|
208
|
+
const beliefFiltered = qualityFiltered.filter((ie) => matchBeliefFilter(ie.entry.beliefState, beliefFilter));
|
|
205
209
|
const selected = beliefFiltered.slice(0, limit);
|
|
206
210
|
const hits = await Promise.all(selected.map((ie) => buildDbHit({
|
|
207
211
|
entry: ie.entry,
|
|
@@ -302,6 +306,9 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
302
306
|
catch {
|
|
303
307
|
// Non-fatal — ranking proceeds without scoped utility on any error.
|
|
304
308
|
}
|
|
309
|
+
// 03-R3: derived twins inherit their base's demoting belief state before
|
|
310
|
+
// ranking, so the (03) belief-state ranker demotes a stale flag-free twin.
|
|
311
|
+
inheritDerivedTwinBeliefStates(db, scored);
|
|
305
312
|
applyRankingRules({
|
|
306
313
|
db,
|
|
307
314
|
query,
|
|
@@ -358,7 +365,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
358
365
|
const qualityFiltered = includeProposed
|
|
359
366
|
? scopeFiltered
|
|
360
367
|
: scopeFiltered.filter((item) => !isProposedQuality(item.entry.quality));
|
|
361
|
-
const beliefFiltered = qualityFiltered.filter((item) => matchBeliefFilter(item.entry.
|
|
368
|
+
const beliefFiltered = qualityFiltered.filter((item) => matchBeliefFilter(item.entry.beliefState, beliefFilter));
|
|
362
369
|
const rankMs = Date.now() - tRank0;
|
|
363
370
|
const selected = beliefFiltered.slice(0, limit);
|
|
364
371
|
const hits = await Promise.all(selected.map(({ entry, filePath, score, rankingMode, utilityBoosted }) => {
|
|
@@ -386,11 +393,39 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
386
393
|
}));
|
|
387
394
|
return { embedMs, rankMs, hits };
|
|
388
395
|
}
|
|
389
|
-
|
|
396
|
+
/**
|
|
397
|
+
* 03-R3: let each `.derived` twin inherit its base memory's demoting belief
|
|
398
|
+
* state for this ranking pass, so a stale flag-free twin is demoted like its
|
|
399
|
+
* corrected base. The base carries the flag (a contradicted base takes a real
|
|
400
|
+
* ranking penalty); its near-duplicate `.derived` twin carries none and would
|
|
401
|
+
* otherwise outrank the corrected copy. Done in-memory at search time — NOT by
|
|
402
|
+
* writing the twin's frontmatter — because the SCC belief resolver refreshes any
|
|
403
|
+
* non-frozen state written to a derived memory back to `active` on the next
|
|
404
|
+
* improve run, erasing it. Only twins with no state of their own inherit; an
|
|
405
|
+
* explicit twin state always wins. Reuses the (03) belief-state ranker + filter.
|
|
406
|
+
*/
|
|
407
|
+
function inheritDerivedTwinBeliefStates(db, items) {
|
|
408
|
+
const DEMOTING = new Set(["contradicted", "superseded", "deprecated", "archived"]);
|
|
409
|
+
const twins = items.filter((it) => it.entry.type === "memory" &&
|
|
410
|
+
it.entry.beliefState === undefined &&
|
|
411
|
+
it.entry.name.toLowerCase().endsWith(".derived"));
|
|
412
|
+
if (twins.length === 0)
|
|
413
|
+
return;
|
|
414
|
+
const baseBeliefByTwinId = getBaseBeliefStatesForDerivedTwins(db, twins.map((t) => t.id));
|
|
415
|
+
for (const t of twins) {
|
|
416
|
+
const baseBelief = baseBeliefByTwinId.get(t.id);
|
|
417
|
+
// Only inherit DEMOTIONS — never let a base's active/asserted state lift a twin.
|
|
418
|
+
if (baseBelief && DEMOTING.has(baseBelief)) {
|
|
419
|
+
t.entry.beliefState = baseBelief;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function matchBeliefFilter(beliefState, filter) {
|
|
390
424
|
if (filter === "all")
|
|
391
425
|
return true;
|
|
392
|
-
|
|
393
|
-
|
|
426
|
+
// 03: the belief filter applies to ANY flagged entry, not just memories, so
|
|
427
|
+
// `current`/`historical` filters catch contradicted/superseded KNOWLEDGE too.
|
|
428
|
+
// Unflagged entries (beliefState === undefined) still pass the `current` filter.
|
|
394
429
|
if (filter === "current") {
|
|
395
430
|
// Phase 1A: `asserted` is a "current" state (stronger authority than `active`);
|
|
396
431
|
// `deprecated` is excluded from current results.
|
|
@@ -40,8 +40,10 @@ const DEFAULT_RECENCY_HALF_LIFE_DAYS = 30;
|
|
|
40
40
|
const FEEDBACK_HALF_LIFE_CAP_MULTIPLIER = 4;
|
|
41
41
|
function beliefStateBoost(item) {
|
|
42
42
|
const entry = item.entry;
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// 03: belief-state penalties/boosts apply to ANY flagged entry (memory OR
|
|
44
|
+
// knowledge), so contradicted/superseded KNOWLEDGE is demoted from results
|
|
45
|
+
// just like flagged memories. Entries without a belief state fall through to
|
|
46
|
+
// the `return 0` below (default-safe — no effect on unflagged assets).
|
|
45
47
|
// Phase 1A: `asserted` and `deprecated` are first-class states.
|
|
46
48
|
// `asserted` carries stronger user-explicit authority than `active`.
|
|
47
49
|
// `deprecated` is a frozen historical state — penalized but milder than `superseded`.
|
|
@@ -87,14 +89,18 @@ const typeRankingContributor = {
|
|
|
87
89
|
return TYPE_BOOST[item.entry.type] ?? 0;
|
|
88
90
|
},
|
|
89
91
|
};
|
|
90
|
-
const
|
|
91
|
-
name: "
|
|
92
|
+
const beliefStateRankingContributor = {
|
|
93
|
+
name: "belief-state-ranking",
|
|
92
94
|
appliesTo(item) {
|
|
93
|
-
|
|
95
|
+
// Fire for any entry that carries a belief state, regardless of type — so
|
|
96
|
+
// contradicted/superseded knowledge is demoted, not just memories. The
|
|
97
|
+
// `.derived`-twin `derivedBoost` (±0.12/−0.08) is deleted (03-R3): it made
|
|
98
|
+
// stale flag-free twins outrank their corrected base memory; belief-state
|
|
99
|
+
// demotion is the principled signal, not the twin-name heuristic.
|
|
100
|
+
return item.entry.beliefState !== undefined;
|
|
94
101
|
},
|
|
95
102
|
adjust(item) {
|
|
96
|
-
|
|
97
|
-
return derivedBoost + beliefStateBoost(item);
|
|
103
|
+
return beliefStateBoost(item);
|
|
98
104
|
},
|
|
99
105
|
};
|
|
100
106
|
const tagRankingContributor = {
|
|
@@ -330,7 +336,7 @@ const projectContextRankingContributor = {
|
|
|
330
336
|
export const defaultRankingContributors = [
|
|
331
337
|
exactNameRankingContributor,
|
|
332
338
|
typeRankingContributor,
|
|
333
|
-
|
|
339
|
+
beliefStateRankingContributor,
|
|
334
340
|
tagRankingContributor,
|
|
335
341
|
searchHintRankingContributor,
|
|
336
342
|
aliasRankingContributor,
|
|
@@ -167,14 +167,26 @@ export function getWritableStashDirs(overrideStashDir, existingConfig) {
|
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
169
|
* Find which source a file path belongs to.
|
|
170
|
+
*
|
|
171
|
+
* Longest-matching-prefix wins: a source nested inside another (e.g. `akm add
|
|
172
|
+
* ./sub` where `./sub` lives under the primary stash — which is always
|
|
173
|
+
* `sources[0]`) is the more specific owner and must win over the enclosing
|
|
174
|
+
* source regardless of array order. A first-match-in-order scan would
|
|
175
|
+
* misattribute every asset under the nested source to the primary stash,
|
|
176
|
+
* corrupting origin / editability / provenance decisions for the affected files.
|
|
170
177
|
*/
|
|
171
178
|
export function findSourceForPath(filePath, sources) {
|
|
172
179
|
const resolved = path.resolve(filePath);
|
|
180
|
+
let best;
|
|
181
|
+
let bestLen = -1;
|
|
173
182
|
for (const source of sources) {
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
const base = path.resolve(source.path);
|
|
184
|
+
if (resolved.startsWith(base + path.sep) && base.length > bestLen) {
|
|
185
|
+
best = source;
|
|
186
|
+
bestLen = base.length;
|
|
187
|
+
}
|
|
176
188
|
}
|
|
177
|
-
return
|
|
189
|
+
return best;
|
|
178
190
|
}
|
|
179
191
|
/**
|
|
180
192
|
* Return the primary stash source (first entry in the list).
|
package/dist/llm/feature-gate.js
CHANGED
|
@@ -22,12 +22,11 @@ const FEATURE_LOCATION = {
|
|
|
22
22
|
graph_extraction: (cfg) => cfg.profiles?.improve?.default?.processes?.graphExtraction?.enabled ?? true,
|
|
23
23
|
// Legacy default: false
|
|
24
24
|
metadata_enhance: (cfg) => cfg.index?.metadataEnhance?.enabled ?? false,
|
|
25
|
-
// Legacy default: false
|
|
26
|
-
curate_rerank: (cfg) => cfg.search?.curateRerank?.enabled ?? false,
|
|
27
25
|
// Default ON since R3 (docs/design/improve-self-learning-analysis.md G5):
|
|
28
|
-
// distill is a primary acquisition path
|
|
29
|
-
//
|
|
30
|
-
//
|
|
26
|
+
// distill is a primary acquisition path, so the gate guards minted content by
|
|
27
|
+
// default. The judge fails CLOSED (07 P0-2): no LLM / timeout / parse failure
|
|
28
|
+
// reject the proposal rather than passing it through — an unjudgeable proposal
|
|
29
|
+
// must not slip into the stash. Opt out via
|
|
31
30
|
// profiles.improve.default.processes.distill.qualityGate.enabled: false.
|
|
32
31
|
lesson_quality_gate: (cfg) => cfg.profiles?.improve?.default?.processes?.distill?.qualityGate?.enabled ?? true,
|
|
33
32
|
// Legacy default: false
|
|
@@ -112,9 +111,6 @@ export function isProcessEnabled(section, processName, config) {
|
|
|
112
111
|
return isLlmFeatureEnabled(config, "graph_extraction");
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
|
-
if (section === "search" && (processName === "curate_rerank" || processName === "curateRerank")) {
|
|
116
|
-
return config.search?.curateRerank?.enabled ?? false;
|
|
117
|
-
}
|
|
118
114
|
if (section === "improve") {
|
|
119
115
|
const processes = config.profiles?.improve?.default?.processes;
|
|
120
116
|
const entry = processes?.[processName];
|
package/dist/output/renderers.js
CHANGED
|
@@ -210,6 +210,10 @@ const agentMdRenderer = {
|
|
|
210
210
|
action: "Dispatch using the prompt below verbatim. Use modelHint and toolPolicy if present.",
|
|
211
211
|
description: asNonEmptyString(parsedMd.data.description),
|
|
212
212
|
prompt: parsedMd.content,
|
|
213
|
+
// `tools` is self-declared frontmatter. The provenance CEILING that decides
|
|
214
|
+
// whether this self-declared policy is honoured is applied at the show
|
|
215
|
+
// layer (`akmShowUnified`), which knows whether the source is the operator's
|
|
216
|
+
// own writable stash vs a read-only third-party source (07 P1-D).
|
|
213
217
|
toolPolicy: parsedMd.data.tools,
|
|
214
218
|
modelHint: typeof parsedMd.data.model === "string" ? parsedMd.data.model : undefined,
|
|
215
219
|
};
|