akm-cli 0.9.17-alpha.7 → 0.9.17-alpha.9
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/CHANGELOG.md +473 -0
- package/STABILITY.md +9 -8
- package/dist/akm +55 -22
- package/dist/akm-migrate +38 -19
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/improve-strategies/catchup.json +0 -3
- package/dist/assets/improve-strategies/consolidate.json +0 -1
- package/dist/assets/improve-strategies/default.json +1 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
- package/dist/assets/improve-strategies/quick.json +1 -2
- package/dist/assets/improve-strategies/reflect-distill.json +1 -2
- package/dist/assets/improve-strategies/thorough.json +0 -3
- package/dist/assets/prompts/consolidate-pair.md +20 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
- package/dist/assets/templates/html/health.html +3 -5
- package/dist/cli/retired-commands.js +1 -1
- package/dist/commands/health/archive-usage.js +98 -0
- package/dist/commands/health/data-dir-usage.js +25 -13
- package/dist/commands/health/html-report.js +1 -4
- package/dist/commands/health/improve-metrics.js +0 -25
- package/dist/commands/health/md-report.js +1 -6
- package/dist/commands/health/report-view-model.js +4 -14
- package/dist/commands/health/windows.js +0 -1
- package/dist/commands/health.js +13 -0
- package/dist/commands/improve/consolidate/continuity-check.js +137 -0
- package/dist/commands/improve/consolidate/pair-pass.js +791 -0
- package/dist/commands/improve/consolidate.js +38 -63
- package/dist/commands/improve/extract-prompt.js +1 -2
- package/dist/commands/improve/improve-cli.js +1 -1
- package/dist/commands/improve/improve-strategies.js +23 -5
- package/dist/commands/improve/improve.js +19 -30
- package/dist/commands/improve/ledger.js +3 -2
- package/dist/commands/improve/loop-stages.js +5 -84
- package/dist/commands/improve/memory/memory-belief.js +3 -1
- package/dist/commands/improve/memory/memory-improve.js +269 -11
- package/dist/commands/improve/planner.js +0 -5
- package/dist/commands/improve/preparation.js +20 -135
- package/dist/commands/improve/retrieval-scope.js +19 -4
- package/dist/commands/improve/salience.js +1 -14
- package/dist/commands/improve/stage.js +0 -1
- package/dist/commands/lint/base-linter.js +19 -11
- package/dist/commands/proposal/drain.js +8 -1
- package/dist/commands/proposal/proposal-cli.js +16 -2
- package/dist/commands/proposal/proposal-types.js +7 -0
- package/dist/commands/proposal/proposal.js +37 -6
- package/dist/commands/proposal/repository.js +613 -4
- package/dist/commands/proposal/validators/proposals.js +9 -0
- package/dist/commands/read/curate.js +40 -13
- package/dist/commands/read/knowledge.js +3 -2
- package/dist/commands/read/show.js +55 -16
- package/dist/commands/sources/info.js +3 -0
- package/dist/commands/sources/stash-cli.js +2 -2
- package/dist/core/adapter/adapters/akm-adapter.js +2 -0
- package/dist/core/adapter/adapters/akm-metadata.js +31 -0
- package/dist/core/bundle-rename.js +1 -7
- package/dist/core/config/config-schema.js +8 -1
- package/dist/core/config/config.js +23 -48
- package/dist/core/config/engine-semantics.js +0 -2
- package/dist/core/config/schema/improve-processes.js +17 -42
- package/dist/core/config/schema/index-config.js +5 -25
- package/dist/core/file-change.js +13 -5
- package/dist/core/improve-result.js +16 -5
- package/dist/core/improve-types.js +0 -1
- package/dist/core/loopback.js +7 -12
- package/dist/core/parse.js +13 -16
- package/dist/core/state/migrations.js +15 -0
- package/dist/core/time.js +0 -20
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/ensure-index.js +2 -2
- package/dist/indexer/index-written-assets.js +2 -3
- package/dist/indexer/indexer.js +18 -418
- package/dist/indexer/links/declared-links.js +90 -0
- package/dist/indexer/passes/metadata.js +0 -19
- package/dist/indexer/scan/doc-to-entry.js +1 -0
- package/dist/indexer/walk/walker.js +3 -4
- package/dist/llm/client.js +8 -10
- package/dist/llm/embedders/remote.js +1 -2
- package/dist/llm/feature-gate.js +0 -5
- package/dist/output/shapes/helpers.js +23 -4
- package/dist/output/text/command-format.js +0 -8
- package/dist/output/text/proposal-format.js +47 -1
- package/dist/output/text/show-format.js +13 -17
- package/dist/scripts/akm-migrate-node.js +2754 -2836
- package/dist/scripts/akm-migrate.js +2754 -2836
- package/dist/setup/steps/connection.js +5 -6
- package/dist/setup/steps/platforms.js +2 -2
- package/dist/sources/providers/git-stash.js +55 -4
- package/dist/storage/repositories/improve-ledger-repository.js +48 -7
- package/dist/storage/repositories/index-entries-repository.js +16 -13
- package/dist/storage/repositories/index-entry-schema.js +22 -3
- package/dist/storage/repositories/index-links-repository.js +143 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +7 -26
- package/dist/storage/repositories/index-schema.js +82 -104
- package/dist/storage/repositories/proposals-repository.js +61 -0
- package/dist/storage/repositories/salience-repository.js +1 -19
- package/dist/tasks/source/task-to-v4.js +462 -74
- package/docs/migration/release-notes/0.9.17.md +7 -5
- package/docs/reference/cli.md +33 -21
- package/docs/reference/configuration.md +21 -12
- package/docs/reference/data-and-telemetry.md +0 -1
- package/package.json +1 -1
- package/schemas/akm-config.json +0 -342
- package/dist/assets/improve-strategies/graph-refresh.json +0 -15
- package/dist/assets/prompts/contradiction-judge.md +0 -33
- package/dist/assets/prompts/graph-extract-system.md +0 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
- package/dist/assets/prompts/metadata-enhance-system.md +0 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
- package/dist/indexer/db/graph-db.js +0 -431
- package/dist/indexer/graph/graph-extraction.js +0 -807
- package/dist/indexer/graph/graph-related.js +0 -131
- package/dist/indexer/graph/graph-types.js +0 -4
- package/dist/llm/graph-extract.js +0 -903
- package/dist/llm/metadata-enhance.js +0 -95
- package/dist/tasks/source/task-to-v3.js +0 -453
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* `akm consolidate` — show the model the memory pool in chunks of similar
|
|
6
6
|
* memories and queue a knowledge proposal for each memory it says should be
|
|
7
|
-
* promoted. Promotion
|
|
8
|
-
*
|
|
7
|
+
* promoted. Promotion emits a reviewable proposal and never touches the
|
|
8
|
+
* memory directly; accepting it later retires the source memory (O1, in
|
|
9
|
+
* `proposal/repository.ts`). Memories the improve ledger judged recently and
|
|
9
10
|
* that have not changed since are not judged again.
|
|
10
11
|
*
|
|
11
|
-
* Accounting invariant: `processed == promoted +
|
|
12
|
-
* Σ(skipReasons) + failedChunkMemories`.
|
|
12
|
+
* Accounting invariant (the promote pass only): `processed == promoted +
|
|
13
|
+
* judgedNoAction + Σ(skipReasons) + failedChunkMemories`.
|
|
14
|
+
*
|
|
15
|
+
* A second pass, the pair pass (`consolidate/pair-pass.ts`, alpha.9), runs
|
|
16
|
+
* alongside this one and keeps its own separate counters (`pairPass` on the
|
|
17
|
+
* result) — it judges near-duplicate and superseding pairs across the wider
|
|
18
|
+
* memory tier and mints `retire` proposals; see that module's own doc
|
|
19
|
+
* comment.
|
|
13
20
|
*/
|
|
14
21
|
import fs from "node:fs";
|
|
15
22
|
import path from "node:path";
|
|
@@ -21,7 +28,6 @@ import { conceptIdFromTypeName, displayRef, parseRefInput } from "../../core/ass
|
|
|
21
28
|
import { getImproveProcessConfig, loadConfig } from "../../core/config/config.js";
|
|
22
29
|
import { parseEmbeddedJsonResponse } from "../../core/parse.js";
|
|
23
30
|
import { openStateDatabase } from "../../core/state-db.js";
|
|
24
|
-
import { parseSinceToIsoLenient } from "../../core/time.js";
|
|
25
31
|
import { warn, warnVerbose } from "../../core/warn.js";
|
|
26
32
|
import { resolveWriteTarget } from "../../core/write-source.js";
|
|
27
33
|
import { deriveInstallations } from "../../indexer/installations.js";
|
|
@@ -31,11 +37,11 @@ import { assertRunnerCredentials } from "../../integrations/agent/runner-dispatc
|
|
|
31
37
|
import { cosineSimilarity, embedBatch, resolveEmbeddingModelId } from "../../llm/embedder.js";
|
|
32
38
|
import { getBodyEmbeddings, upsertBodyEmbeddings } from "../../storage/repositories/embeddings-repository.js";
|
|
33
39
|
import { closeDatabase, openExistingDatabase, openReadonlyExistingDatabase, } from "../../storage/repositories/index-connection.js";
|
|
34
|
-
import {
|
|
35
|
-
import { getNeighborsByEntryId } from "../../storage/repositories/index-vec-repository.js";
|
|
40
|
+
import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
|
|
36
41
|
import { listProposals, listProposalsReadOnly, proposalContent } from "../proposal/repository.js";
|
|
37
42
|
import { hasHotCaptureMode, hasSupersededStatus, validateProposalFrontmatter, } from "../proposal/validators/proposal-quality-validators.js";
|
|
38
43
|
import { buildChunkPrompt, computeSafeChunkSize, DEFAULT_CONTEXT_LENGTH_TOKENS } from "./consolidate/chunking.js";
|
|
44
|
+
import { runConsolidatePairPass } from "./consolidate/pair-pass.js";
|
|
39
45
|
import { sanitizeMergedContent } from "./consolidate/sanitize.js";
|
|
40
46
|
import { contentHash } from "./content-hash.js";
|
|
41
47
|
import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
|
|
@@ -77,7 +83,8 @@ export function isHotCapturedMemory(filePath) {
|
|
|
77
83
|
}
|
|
78
84
|
/**
|
|
79
85
|
* Structured-output schema for a plan. Promote-only: merge/delete/contradict
|
|
80
|
-
* were
|
|
86
|
+
* were removed in 0.9.17-alpha.1 (`e82eec811`) after running in production —
|
|
87
|
+
* they cost thousands of completion tokens.
|
|
81
88
|
*/
|
|
82
89
|
export const CONSOLIDATE_PLAN_JSON_SCHEMA = {
|
|
83
90
|
type: "object",
|
|
@@ -435,9 +442,6 @@ export function inspectConsolidationPool(opts, stashDir, warnings, existingKnowl
|
|
|
435
442
|
const beforeScope = memories.length;
|
|
436
443
|
memories = memories.filter((memory) => isInRetrievalScope(retrievalScope, conceptIdFromTypeName("memory", memory.name), memory.filePath));
|
|
437
444
|
const outsideRetrievalScope = beforeScope - memories.length;
|
|
438
|
-
if (opts.incrementalSince && memories.length > 0) {
|
|
439
|
-
memories = narrowToIncrementalCandidates(memories, opts.incrementalSince, warnings, opts.neighborsPerChanged, readOnly);
|
|
440
|
-
}
|
|
441
445
|
const dedupPoolSize = memories.length;
|
|
442
446
|
if (opts.limit === undefined && memories.length > 150) {
|
|
443
447
|
warnings.push(`Consolidation: pool has ${memories.length} memories and no limit is set. Consider adding a limit to your consolidate config to prevent timeouts on slow LLM endpoints.`);
|
|
@@ -656,6 +660,12 @@ async function consolidate(opts, config, stashDir, startMs, stateDb) {
|
|
|
656
660
|
warnings.push(`Consolidation: pre-filtered ${prefilteredAlreadyPromoted} ${plural(prefilteredAlreadyPromoted)} whose body already exists verbatim in knowledge/ before chunking.`);
|
|
657
661
|
}
|
|
658
662
|
const target = opts.target ?? stashDir;
|
|
663
|
+
// The pair pass (alpha.9) has its own initiator/candidate selection (it
|
|
664
|
+
// sees .derived memories, flat knowledge and lessons, not just the
|
|
665
|
+
// promote pool above), so it runs regardless of whether the promote pool
|
|
666
|
+
// is empty — every return path below carries its result.
|
|
667
|
+
const pairPassBundleId = resolveConsolidationSourceOwner(opts, stashDir)?.bundleId;
|
|
668
|
+
const pairPass = await runConsolidatePairPass(opts, config, stashDir, pairPassBundleId, warnings);
|
|
659
669
|
if (memories.length === 0) {
|
|
660
670
|
return makeConsolidateResult({
|
|
661
671
|
dryRun: opts.dryRun ?? false,
|
|
@@ -663,6 +673,7 @@ async function consolidate(opts, config, stashDir, startMs, stateDb) {
|
|
|
663
673
|
warnings,
|
|
664
674
|
durationMs: Date.now() - startMs,
|
|
665
675
|
prefilteredAlreadyPromoted,
|
|
676
|
+
pairPass,
|
|
666
677
|
});
|
|
667
678
|
}
|
|
668
679
|
const acc = {
|
|
@@ -692,7 +703,7 @@ async function consolidate(opts, config, stashDir, startMs, stateDb) {
|
|
|
692
703
|
durationMs: Date.now() - startMs,
|
|
693
704
|
});
|
|
694
705
|
if (opts.dryRun)
|
|
695
|
-
return makeConsolidateResult({ ...summary(), dryRun: true, previewOnly: true });
|
|
706
|
+
return makeConsolidateResult({ ...summary(), dryRun: true, previewOnly: true, pairPass });
|
|
696
707
|
warn(`[consolidate] plan: ${plan.allOps.length} operation(s)`);
|
|
697
708
|
const ctx = {
|
|
698
709
|
config,
|
|
@@ -720,6 +731,7 @@ async function consolidate(opts, config, stashDir, startMs, stateDb) {
|
|
|
720
731
|
...summary(),
|
|
721
732
|
promoted: ctx.promoted,
|
|
722
733
|
failedPromotions: ctx.promotionFailures.count,
|
|
734
|
+
pairPass,
|
|
723
735
|
perfTelemetry: {
|
|
724
736
|
dedupPoolSize: pool.dedupPoolSize,
|
|
725
737
|
llmPoolSize: plan.llmPoolSize,
|
|
@@ -799,6 +811,12 @@ export async function emitPromotionProposal(op, ctx) {
|
|
|
799
811
|
catch (e) {
|
|
800
812
|
return skip("promote_read_failed", `Promote: could not read ${op.ref}: ${String(e)}`);
|
|
801
813
|
}
|
|
814
|
+
// O1 hash nit: the RAW body, before sanitization — accept re-reads the
|
|
815
|
+
// source with a plain fs.readFileSync and never re-sanitizes, so hashing
|
|
816
|
+
// anything else here would compare two different representations of the
|
|
817
|
+
// same unedited memory and report a false "changed since mint" (measured:
|
|
818
|
+
// 3 of 767 real memories sanitize to different bytes than their raw body).
|
|
819
|
+
const sourceRawBodyHash = contentHash(memoryContent, "body");
|
|
802
820
|
const sanitized = sanitizeMergedContent(memoryContent);
|
|
803
821
|
if (!sanitized.ok) {
|
|
804
822
|
return skip("promote_sanitization_failed", `Promote: rejected ${op.ref} — source memory failed sanitization (${sanitized.reason}).`);
|
|
@@ -856,6 +874,14 @@ export async function emitPromotionProposal(op, ctx) {
|
|
|
856
874
|
...(typeof op.confidence === "number" ? { confidence: op.confidence } : {}),
|
|
857
875
|
// The ledger keys the attempt by the source memory.
|
|
858
876
|
attemptedRefs: [op.ref],
|
|
877
|
+
// O1 (alpha.9): on accept, promoteProposal retires this source memory
|
|
878
|
+
// (and its .derived twin) so promotion no longer leaves a duplicate.
|
|
879
|
+
promotionSource: op.ref,
|
|
880
|
+
// B3: recorded so accept can refuse to archive a source that was
|
|
881
|
+
// edited after this promotion was queued — the RAW body hash (see
|
|
882
|
+
// sourceRawBodyHash's own comment above), not `bodyHash`, which is the
|
|
883
|
+
// sanitized-for-knowledge representation accept never re-derives.
|
|
884
|
+
promotionSourceHash: sourceRawBodyHash,
|
|
859
885
|
});
|
|
860
886
|
ctx.promoted.push(proposal.id);
|
|
861
887
|
ctx.promotedSourceRefs.add(op.ref);
|
|
@@ -865,57 +891,6 @@ export async function emitPromotionProposal(op, ctx) {
|
|
|
865
891
|
skip("promote_create_failed", `Promote: createProposal failed for ${op.ref}: ${String(e)}`);
|
|
866
892
|
}
|
|
867
893
|
}
|
|
868
|
-
/**
|
|
869
|
-
* {changed} ∪ {top-k indexed neighbours of each changed memory}, within the
|
|
870
|
-
* pool: nothing changed → []; everything changed or no index → the full pool.
|
|
871
|
-
*/
|
|
872
|
-
export function narrowToIncrementalCandidates(memories, since, warnings, neighborsPerChanged = 5, readOnly = false) {
|
|
873
|
-
// Lenient: a garbage `since` passes through and selects nothing.
|
|
874
|
-
const sinceIso = parseSinceToIsoLenient(since);
|
|
875
|
-
const changed = memories.filter((m) => {
|
|
876
|
-
try {
|
|
877
|
-
return fs.statSync(m.filePath).mtime.toISOString() > sinceIso;
|
|
878
|
-
}
|
|
879
|
-
catch {
|
|
880
|
-
return true; // never silently drop a memory we cannot stat
|
|
881
|
-
}
|
|
882
|
-
});
|
|
883
|
-
if (changed.length === 0)
|
|
884
|
-
return [];
|
|
885
|
-
if (changed.length === memories.length)
|
|
886
|
-
return memories;
|
|
887
|
-
const inPool = new Set(memories.map((m) => m.name));
|
|
888
|
-
const keep = new Set(changed.map((m) => m.name));
|
|
889
|
-
let db;
|
|
890
|
-
try {
|
|
891
|
-
db = readOnly ? openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true }) : openExistingDatabase();
|
|
892
|
-
if (!db)
|
|
893
|
-
return memories;
|
|
894
|
-
for (const m of changed) {
|
|
895
|
-
const id = findEntryIdByRef(db, conceptIdFromTypeName("memory", m.name));
|
|
896
|
-
if (id === undefined)
|
|
897
|
-
continue;
|
|
898
|
-
for (const hit of getNeighborsByEntryId(db, id, neighborsPerChanged + 1)) {
|
|
899
|
-
if (hit.id === id)
|
|
900
|
-
continue;
|
|
901
|
-
const name = getEntryById(db, hit.id)?.entry.name;
|
|
902
|
-
if (name && inPool.has(name))
|
|
903
|
-
keep.add(name);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
catch {
|
|
908
|
-
warnings.push("Incremental consolidation: index unavailable — processing full pool.");
|
|
909
|
-
return memories;
|
|
910
|
-
}
|
|
911
|
-
finally {
|
|
912
|
-
if (db)
|
|
913
|
-
closeDatabase(db);
|
|
914
|
-
}
|
|
915
|
-
const candidates = memories.filter((m) => keep.has(m.name));
|
|
916
|
-
warnings.push(`Incremental consolidation: ${changed.length} changed + neighbours → ${candidates.length}/${memories.length} memories considered (since ${since}${sinceIso !== since ? ` = ${sinceIso}` : ""}).`);
|
|
917
|
-
return candidates;
|
|
918
|
-
}
|
|
919
894
|
/** The target bundle's eligible memories from the index, else walked from disk. */
|
|
920
895
|
function loadMemoriesForSource(source, warnings, readOnly) {
|
|
921
896
|
let memories = [];
|
|
@@ -111,8 +111,7 @@ function formatAlreadyPreserved(inlineRefs) {
|
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
113
|
* Delimiters that fence the untrusted session transcript in the extract prompt.
|
|
114
|
-
*
|
|
115
|
-
* between the markers is DATA to analyze, never instructions to obey. The
|
|
114
|
+
* Everything between the markers is DATA to analyze, never instructions to obey. The
|
|
116
115
|
* transcript is external, attacker-influenceable content, so an explicit,
|
|
117
116
|
* greppable boundary defuses prompt-injection that tries to pose as a command.
|
|
118
117
|
*/
|
|
@@ -250,7 +250,7 @@ export const improveCommand = defineCommand({
|
|
|
250
250
|
},
|
|
251
251
|
strategy: {
|
|
252
252
|
type: "string",
|
|
253
|
-
description: "Named improve strategy from improve.strategies or built-in strategies (catchup, consolidate, default,
|
|
253
|
+
description: "Named improve strategy from improve.strategies or built-in strategies (catchup, consolidate, default, proactive-maintenance, quick, reflect-distill, thorough). Controls which sub-processes run and which asset types are processed.",
|
|
254
254
|
},
|
|
255
255
|
sync: {
|
|
256
256
|
type: "boolean",
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
import catchup from "../../assets/improve-strategies/catchup.json" with { type: "json" };
|
|
5
5
|
import consolidate from "../../assets/improve-strategies/consolidate.json" with { type: "json" };
|
|
6
6
|
import defaultStrategy from "../../assets/improve-strategies/default.json" with { type: "json" };
|
|
7
|
-
import graphRefresh from "../../assets/improve-strategies/graph-refresh.json" with { type: "json" };
|
|
8
7
|
import proactiveMaintenance from "../../assets/improve-strategies/proactive-maintenance.json" with { type: "json" };
|
|
9
8
|
import quick from "../../assets/improve-strategies/quick.json" with { type: "json" };
|
|
10
9
|
import reflectDistill from "../../assets/improve-strategies/reflect-distill.json" with { type: "json" };
|
|
11
10
|
import thorough from "../../assets/improve-strategies/thorough.json" with { type: "json" };
|
|
12
11
|
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
13
|
-
import { getIndexPassConfig, } from "../../core/config/config.js";
|
|
14
12
|
import { ImproveProfileConfigSchema } from "../../core/config/config-schema.js";
|
|
15
13
|
import { deepMergeConfig } from "../../core/config/deep-merge.js";
|
|
16
14
|
import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES, } from "../../core/config/engine-semantics.js";
|
|
@@ -72,7 +70,6 @@ const BUILTIN_STRATEGIES = {
|
|
|
72
70
|
default: defaultStrategy,
|
|
73
71
|
quick,
|
|
74
72
|
thorough,
|
|
75
|
-
"graph-refresh": graphRefresh,
|
|
76
73
|
consolidate,
|
|
77
74
|
catchup,
|
|
78
75
|
"reflect-distill": reflectDistill,
|
|
@@ -84,6 +81,29 @@ if (BUILTIN_IMPROVE_STRATEGY_NAMES.some((name) => !(name in BUILTIN_STRATEGIES))
|
|
|
84
81
|
export function resolveImproveStrategy(name, config) {
|
|
85
82
|
const selectedName = name ?? config.defaults?.improveStrategy ?? "default";
|
|
86
83
|
const userStrategies = config.improve?.strategies ?? {};
|
|
84
|
+
// graph-refresh named a specific, common retirement — refuse it
|
|
85
|
+
// unconditionally, even when `improve.strategies["graph-refresh"]` still
|
|
86
|
+
// has an override block from when it customized the (now-deleted) built-in
|
|
87
|
+
// strategy of the same name. That override is a partial patch (e.g. just
|
|
88
|
+
// `processes.graphExtraction.mode`), not a full strategy definition —
|
|
89
|
+
// falling through to the generic "resolve as a user strategy" path below
|
|
90
|
+
// would silently merge it onto `default` and run a full, unplanned improve
|
|
91
|
+
// pass instead of refusing. `akm migrate apply` drops the leftover block
|
|
92
|
+
// (src/core/config/config.ts, normalizeConfigFile) since it can never
|
|
93
|
+
// apply again.
|
|
94
|
+
if (selectedName === "graph-refresh") {
|
|
95
|
+
const hasLeftoverOverride = Boolean(userStrategies["graph-refresh"]);
|
|
96
|
+
throw new ConfigError(`Improve strategy "graph-refresh" was retired in 0.9.17-alpha.9 along with the LLM entity-graph extraction it ran.` +
|
|
97
|
+
(hasLeftoverOverride
|
|
98
|
+
? ' Your config still has a leftover `improve.strategies["graph-refresh"]` override for it — `akm migrate apply` removes it.'
|
|
99
|
+
: ""), "UNKNOWN_IMPROVE_STRATEGY",
|
|
100
|
+
// Override CONFIG_HINTS.UNKNOWN_IMPROVE_STRATEGY (src/core/errors.ts):
|
|
101
|
+
// its "listed strategy names" phrase does not apply here (this message
|
|
102
|
+
// names no strategies), and its "define it under improve.strategies"
|
|
103
|
+
// suggestion is actively wrong — that is exactly the leftover-override
|
|
104
|
+
// shape this refusal exists to reject, not a way around it.
|
|
105
|
+
"Choose a different strategy. `graph-refresh` cannot be redefined under `improve.strategies` — it always refuses.");
|
|
106
|
+
}
|
|
87
107
|
if (!(selectedName in BUILTIN_STRATEGIES) && !userStrategies[selectedName]) {
|
|
88
108
|
const valid = [...new Set([...Object.keys(BUILTIN_STRATEGIES), ...Object.keys(userStrategies)])].sort();
|
|
89
109
|
throw new ConfigError(`Improve strategy "${selectedName}" not found. Valid strategies: ${valid.join(", ")}.`, "UNKNOWN_IMPROVE_STRATEGY");
|
|
@@ -212,8 +232,6 @@ function buildImprovePlan(strategy, config, options) {
|
|
|
212
232
|
profile: strategy.config,
|
|
213
233
|
process: sourceProcessConfig,
|
|
214
234
|
processName,
|
|
215
|
-
// Graph extraction's standing engine, model, timeout and llm settings (GR-D15).
|
|
216
|
-
...(processName === "graphExtraction" ? { index: getIndexPassConfig(config.index, "graph") } : {}),
|
|
217
235
|
});
|
|
218
236
|
runner = resolved?.runner ?? null;
|
|
219
237
|
notices = resolved?.notices ?? [];
|
|
@@ -42,7 +42,7 @@ import { buildImproveUsageReport } from "./improve-usage-report.js";
|
|
|
42
42
|
import { lastAttemptByRef, loadLedgerSnapshot } from "./ledger.js";
|
|
43
43
|
import { improveLockPath, releaseImproveLock, tryAcquireImproveLock } from "./locks.js";
|
|
44
44
|
import { runImproveLoopStage, runImprovePostLoopStage } from "./loop-stages.js";
|
|
45
|
-
import { analyzeMemoryCleanup } from "./memory/memory-improve.js";
|
|
45
|
+
import { analyzeMemoryCleanup, purgeGracedArchive, RETIRE_GRACE_DAYS, } from "./memory/memory-improve.js";
|
|
46
46
|
import { buildImproveExecutionPlan } from "./planner.js";
|
|
47
47
|
import { CONSOLIDATION_CONFIG_KEYS, pickDefined, recordImproveSkip, runImprovePreparationStage } from "./preparation.js";
|
|
48
48
|
import { DEFAULT_DUE_DAYS, filterProactiveDue } from "./proactive-maintenance.js";
|
|
@@ -175,6 +175,21 @@ export async function akmImprove(options = {}) {
|
|
|
175
175
|
initialGitPaths =
|
|
176
176
|
syncRepoDir && isGitBackedStash(syncRepoDir) ? new Set(listGitChangedPaths(syncRepoDir)) : new Set();
|
|
177
177
|
journal = beginWriteProvenance();
|
|
178
|
+
// Step 8 (alpha.9 plan §5.4): the purge sweep. Deterministic, no LLM,
|
|
179
|
+
// and first — before anything else this run might touch the archive
|
|
180
|
+
// for. Git-backed bundles only; a bundle with no git history of its
|
|
181
|
+
// own is left untouched (akm health reports its size instead).
|
|
182
|
+
if (setup.primaryStashDir) {
|
|
183
|
+
try {
|
|
184
|
+
const purge = purgeGracedArchive(setup.primaryStashDir);
|
|
185
|
+
if (purge.purgedFiles > 0) {
|
|
186
|
+
info(`[improve] archive purge: ${purge.purgedFiles} file(s) across ${purge.purgedDirs} retirement(s) past the ${RETIRE_GRACE_DAYS}d grace period removed`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
preEnsureCleanupWarnings.push(`archive purge failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
178
193
|
// The index is made current BEFORE triage (R6): triage promotes into the
|
|
179
194
|
// stash, and a reindex after it would always find fresh work.
|
|
180
195
|
const bootstrap = await runIndexBootstrapPass(setup, budgetAbortController.signal);
|
|
@@ -564,7 +579,6 @@ function buildResultExecutionPlan(run, preparation, collected, dryRun) {
|
|
|
564
579
|
: undefined;
|
|
565
580
|
const belowMinPending = pendingMemories !== undefined && inferenceMinPending !== undefined && pendingMemories < inferenceMinPending;
|
|
566
581
|
const memoryInferenceEnabled = resolvedPlan.processes.memoryInference.enabled && !belowMinPending;
|
|
567
|
-
const graphExtractionEnabled = resolvedPlan.processes.graphExtraction.enabled && run.primaryStashDir !== undefined;
|
|
568
582
|
// Per-process routing, plus how many effective refs each ref-scoped process would act on (#947).
|
|
569
583
|
const processes = projectResolvedProcessRouting(resolvedPlan).map((row) => {
|
|
570
584
|
const eligibleRefs = eligibleRefCount(preparation.loopRefs, row.process, resolvedPlan.strategy.config);
|
|
@@ -608,16 +622,6 @@ function buildResultExecutionPlan(run, preparation, collected, dryRun) {
|
|
|
608
622
|
},
|
|
609
623
|
stageConfig: {
|
|
610
624
|
extract: { enabled: preparation.planning.extract.wouldRun, reason: preparation.planning.extract.reason },
|
|
611
|
-
graphExtraction: {
|
|
612
|
-
enabled: graphExtractionEnabled,
|
|
613
|
-
reason: !resolvedPlan.processes.graphExtraction.enabled
|
|
614
|
-
? "disabled"
|
|
615
|
-
: graphExtractionEnabled
|
|
616
|
-
? improveProfile.processes?.graphExtraction?.fullScan === true
|
|
617
|
-
? "enabled for a full-corpus scan"
|
|
618
|
-
: "enabled for refs touched by this run"
|
|
619
|
-
: "enabled but no primary source is available",
|
|
620
|
-
},
|
|
621
625
|
memoryInference: {
|
|
622
626
|
enabled: memoryInferenceEnabled,
|
|
623
627
|
reason: !resolvedPlan.processes.memoryInference.enabled
|
|
@@ -825,7 +829,7 @@ async function runImproveStageSequence(run, collected, preEnsureCleanupWarnings,
|
|
|
825
829
|
improveProfile,
|
|
826
830
|
resolvedPlan,
|
|
827
831
|
});
|
|
828
|
-
let postLoop = { allWarnings: [], memoryInferenceDurationMs: 0
|
|
832
|
+
let postLoop = { allWarnings: [], memoryInferenceDurationMs: 0 };
|
|
829
833
|
const remainingBudget = budgetAbortController.signal.remainingBudgetMs;
|
|
830
834
|
if (budgetAbortController.signal.aborted || (remainingBudget !== undefined && remainingBudget <= 0)) {
|
|
831
835
|
info("[improve] post-loop maintenance skipped (wall-clock budget exhausted)");
|
|
@@ -859,8 +863,8 @@ function finalizeImproveResult(args) {
|
|
|
859
863
|
const { options, startMs, resolvedPlan } = run;
|
|
860
864
|
const { memoryCleanupPlan, strategyFilteredRefs } = collected;
|
|
861
865
|
const consolidation = preparation.consolidation;
|
|
862
|
-
const { memoryInference,
|
|
863
|
-
const { memoryInferenceDurationMs
|
|
866
|
+
const { memoryInference, allWarnings, deadUrls, deadUrlCoverage, orphansPurged, proposalsExpired } = postLoop;
|
|
867
|
+
const { memoryInferenceDurationMs } = postLoop;
|
|
864
868
|
// The per-ref distill-skipped rows fold into a bounded aggregate before persistence (C1).
|
|
865
869
|
const { actions: persistedActions, aggregate: distillSkippedAggregate } = foldDistillSkipped(finalActions);
|
|
866
870
|
// This run's LLM accounting (#944): llm_usage rows carry no run id, so the
|
|
@@ -880,7 +884,6 @@ function finalizeImproveResult(args) {
|
|
|
880
884
|
consolidation,
|
|
881
885
|
...(preparation.extract ?? []).flatMap((extract) => [extract, ...(extract.sessions ?? [])]),
|
|
882
886
|
memoryInference,
|
|
883
|
-
graphExtraction,
|
|
884
887
|
triageDrain,
|
|
885
888
|
]);
|
|
886
889
|
const applied = preparation.appliedCleanup;
|
|
@@ -933,11 +936,9 @@ function finalizeImproveResult(args) {
|
|
|
933
936
|
...(deadUrlCoverage !== undefined ? { deadUrlCoverage } : {}),
|
|
934
937
|
...(reflectsWithErrorContext > 0 ? { reflectsWithErrorContext } : {}),
|
|
935
938
|
...(memoryInference ? { memoryInference } : {}),
|
|
936
|
-
...(graphExtraction ? { graphExtraction } : {}),
|
|
937
939
|
// Top-level phase durations feed health's wall-time buckets; a phase that
|
|
938
940
|
// did not run is omitted, not zero.
|
|
939
941
|
...(memoryInferenceDurationMs > 0 ? { memoryInferenceDurationMs } : {}),
|
|
940
|
-
...(graphExtractionDurationMs > 0 ? { graphExtractionDurationMs } : {}),
|
|
941
942
|
...(ensureIndexDurationMs !== undefined ? { ensureIndexDurationMs } : {}),
|
|
942
943
|
...(orphansPurged !== undefined ? { orphansPurged } : {}),
|
|
943
944
|
...(proposalsExpired !== undefined && proposalsExpired > 0 ? { proposalsExpired } : {}),
|
|
@@ -960,7 +961,6 @@ function finalizeImproveResult(args) {
|
|
|
960
961
|
};
|
|
961
962
|
emitImproveCompletedEvent(result, {
|
|
962
963
|
memoryInferenceDurationMs,
|
|
963
|
-
graphExtractionDurationMs,
|
|
964
964
|
totalDurationMs: Date.now() - startMs,
|
|
965
965
|
warningCount: allWarnings.length,
|
|
966
966
|
orphansPurged: orphansPurged ?? 0,
|
|
@@ -989,7 +989,6 @@ const ACTION_COUNTER = {
|
|
|
989
989
|
"distill-skipped": "distillSkippedActions",
|
|
990
990
|
"memory-prune": "memoryPruneActions",
|
|
991
991
|
"memory-inference": "memoryInferenceActions",
|
|
992
|
-
"graph-extraction": "graphExtractionActions",
|
|
993
992
|
error: "errorActions",
|
|
994
993
|
"reflect-failed": "reflectFailedActions",
|
|
995
994
|
"reflect-cooldown": "reflectCooldownActions",
|
|
@@ -1011,7 +1010,6 @@ function emitImproveCompletedEvent(result, durations, eventsCtx) {
|
|
|
1011
1010
|
counts.distillSkippedActions = (counts.distillSkippedActions ?? 0) + distillSkippedTotal;
|
|
1012
1011
|
classCounts.skipped += distillSkippedTotal;
|
|
1013
1012
|
const cleanup = result.memoryCleanup;
|
|
1014
|
-
const quality = result.graphExtraction?.quality;
|
|
1015
1013
|
appendEvent({
|
|
1016
1014
|
eventType: "improve_completed",
|
|
1017
1015
|
ref: result.scope.mode === "ref"
|
|
@@ -1043,21 +1041,12 @@ function emitImproveCompletedEvent(result, durations, eventsCtx) {
|
|
|
1043
1041
|
consolidationDurationMs: result.consolidation?.durationMs ?? 0,
|
|
1044
1042
|
memoryInferenceWrites: result.memoryInference?.writtenFacts ?? 0,
|
|
1045
1043
|
memoryInferenceDurationMs: durations.memoryInferenceDurationMs,
|
|
1046
|
-
graphExtractionExtractedFiles: quality?.extractedFiles ?? 0,
|
|
1047
|
-
graphExtractionDurationMs: durations.graphExtractionDurationMs,
|
|
1048
1044
|
proactiveSelected: result.proactiveMaintenance?.selected ?? 0,
|
|
1049
1045
|
proactiveDueTotal: result.proactiveMaintenance?.dueTotal ?? 0,
|
|
1050
1046
|
proactiveNeverReflected: result.proactiveMaintenance?.neverReflected ?? 0,
|
|
1051
1047
|
durationMs: durations.totalDurationMs,
|
|
1052
1048
|
warningCount: durations.warningCount,
|
|
1053
1049
|
orphansPurged: durations.orphansPurged,
|
|
1054
|
-
...(quality
|
|
1055
|
-
? {
|
|
1056
|
-
graphCoverage: quality.extractionCoverage,
|
|
1057
|
-
graphDensity: quality.density,
|
|
1058
|
-
graphEntities: quality.entityCount,
|
|
1059
|
-
}
|
|
1060
|
-
: {}),
|
|
1061
1050
|
},
|
|
1062
1051
|
}, eventsCtx);
|
|
1063
1052
|
}
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import { getStateDbPath, withImmediateTransaction, withStateDb } from "../../core/state-db.js";
|
|
11
11
|
import { warn } from "../../core/warn.js";
|
|
12
|
-
import { isLedgerBlocked, listImproveLedgerRows, recordImproveLedger, } from "../../storage/repositories/improve-ledger-repository.js";
|
|
12
|
+
import { isLedgerBlocked, listImproveLedgerRows, PAIR_PASS_LEDGER_SOURCE, recordImproveLedger, } from "../../storage/repositories/improve-ledger-repository.js";
|
|
13
13
|
import { openSqliteReadSnapshot } from "../../storage/sqlite-read-snapshot.js";
|
|
14
|
-
export { isLedgerBlocked };
|
|
14
|
+
export { isLedgerBlocked, PAIR_PASS_LEDGER_SOURCE };
|
|
15
15
|
/** An improve candidate's durable state key: its index item_ref, else its conceptId. */
|
|
16
16
|
export function stateKey(ref, itemRef) {
|
|
17
17
|
return itemRef ?? ref;
|
|
@@ -69,6 +69,7 @@ export function recordLedgerAttempt(access, inputs) {
|
|
|
69
69
|
at,
|
|
70
70
|
...(input.proposalId !== undefined ? { proposalId: input.proposalId } : {}),
|
|
71
71
|
...(input.detail !== undefined ? { detail: input.detail } : {}),
|
|
72
|
+
...(input.contentHash !== undefined ? { contentHash: input.contentHash } : {}),
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
}));
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/** The improve loop (reflect + distill per ref), the post-loop checks and the maintenance passes. */
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
-
import path from "node:path";
|
|
7
6
|
import { parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
8
7
|
import { daysToMs } from "../../core/common.js";
|
|
9
8
|
import { loadConfig } from "../../core/config/config.js";
|
|
@@ -13,12 +12,9 @@ import { openLogsDatabase, purgeOldTaskLogs } from "../../core/logs-db.js";
|
|
|
13
12
|
import { getDbPath, getTaskLogDir } from "../../core/paths.js";
|
|
14
13
|
import { withStateDb } from "../../core/state-db.js";
|
|
15
14
|
import { info } from "../../core/warn.js";
|
|
16
|
-
import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
|
|
17
15
|
import { indexWrittenAssets } from "../../indexer/index-written-assets.js";
|
|
18
|
-
import { deriveWritableBundleIds } from "../../indexer/installations.js";
|
|
19
16
|
import { collectPendingMemories, runMemoryInferencePass, } from "../../indexer/passes/memory-inference.js";
|
|
20
17
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
21
|
-
import { isProcessEnabled } from "../../llm/feature-gate.js";
|
|
22
18
|
import { purgeOldEvents } from "../../storage/repositories/events-repository.js";
|
|
23
19
|
import { purgeOldImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
|
|
24
20
|
import { closeDatabase, openIndexDatabase } from "../../storage/repositories/index-connection.js";
|
|
@@ -29,7 +25,7 @@ import { readFreelistInfo, STATE_DB_VACUUMED_EVENT, vacuumIfReclaimable } from "
|
|
|
29
25
|
import { purgeOldTaskLogFiles } from "../../tasks/run/task-log.js";
|
|
30
26
|
import { expireStaleProposals, purgeOrphanProposals } from "../proposal/repository.js";
|
|
31
27
|
import { checkDeadUrls } from "../url-checker.js";
|
|
32
|
-
import {
|
|
28
|
+
import { isDistillCandidateRef } from "./eligibility.js";
|
|
33
29
|
import { shouldSkipRef } from "./improve-strategies.js";
|
|
34
30
|
import { recordLedgerAttempt, stateKey, stripBundle } from "./ledger.js";
|
|
35
31
|
import { pushRecentError } from "./preparation.js";
|
|
@@ -327,23 +323,21 @@ export async function runImprovePostLoopStage(args) {
|
|
|
327
323
|
deadUrls,
|
|
328
324
|
...(deadUrlCoverage ? { deadUrlCoverage } : {}),
|
|
329
325
|
...(maintenance.memoryInference ? { memoryInference: maintenance.memoryInference } : {}),
|
|
330
|
-
...(maintenance.graphExtraction ? { graphExtraction: maintenance.graphExtraction } : {}),
|
|
331
326
|
...(maintenance.actions && maintenance.actions.length > 0 ? { maintenanceActions: maintenance.actions } : {}),
|
|
332
327
|
memoryInferenceDurationMs: maintenance.memoryInferenceDurationMs,
|
|
333
|
-
graphExtractionDurationMs: maintenance.graphExtractionDurationMs,
|
|
334
328
|
orphansPurged: maintenance.orphansPurged,
|
|
335
329
|
proposalsExpired: maintenance.proposalsExpired,
|
|
336
330
|
};
|
|
337
331
|
}
|
|
338
332
|
/**
|
|
339
|
-
* Memory inference → index what it wrote →
|
|
340
|
-
*
|
|
341
|
-
*
|
|
333
|
+
* Memory inference → index what it wrote → proposal hygiene (orphan purge,
|
|
334
|
+
* expiration) → orphan-state GC → retention purges. Warnings go to
|
|
335
|
+
* `allWarnings`.
|
|
342
336
|
*/
|
|
343
337
|
export async function runImproveMaintenancePasses(args) {
|
|
344
338
|
const { options, primaryStashDir, allWarnings, budgetSignal, eventsCtx } = args;
|
|
345
339
|
if (!primaryStashDir || budgetSignal?.aborted)
|
|
346
|
-
return { memoryInferenceDurationMs: 0
|
|
340
|
+
return { memoryInferenceDurationMs: 0 };
|
|
347
341
|
const config = options.config ?? loadConfig();
|
|
348
342
|
const ctx = {
|
|
349
343
|
config,
|
|
@@ -354,7 +348,6 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
354
348
|
improveProfile: args.improveProfile,
|
|
355
349
|
resolvedPlan: args.resolvedPlan,
|
|
356
350
|
memoryInferenceFn: options.memoryInferenceFn ?? runMemoryInferencePass,
|
|
357
|
-
graphExtractionFn: options.graphExtractionFn ?? runGraphExtractionPass,
|
|
358
351
|
};
|
|
359
352
|
const openIndexDb = () => openIndexDatabase(getDbPath());
|
|
360
353
|
const dbCell = {};
|
|
@@ -386,20 +379,14 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
386
379
|
allWarnings.push(`indexing after memory inference failed: ${errMessage(err)}`);
|
|
387
380
|
}
|
|
388
381
|
}
|
|
389
|
-
const graph = await runGraphExtractionMaintenancePass(ctx, dbCell, args);
|
|
390
|
-
if (graph.action)
|
|
391
|
-
actions.push(graph.action);
|
|
392
|
-
allWarnings.push(...graph.warnings);
|
|
393
382
|
const hygiene = runProposalHygienePass(ctx);
|
|
394
383
|
allWarnings.push(...hygiene.warnings);
|
|
395
384
|
allWarnings.push(...runOrphanStateGcPass(ctx, dbCell).warnings);
|
|
396
385
|
allWarnings.push(...runRetentionPurgePass(ctx).warnings);
|
|
397
386
|
return {
|
|
398
387
|
...(inference.memoryInference ? { memoryInference: inference.memoryInference } : {}),
|
|
399
|
-
...(graph.graphExtraction ? { graphExtraction: graph.graphExtraction } : {}),
|
|
400
388
|
...(actions.length > 0 ? { actions } : {}),
|
|
401
389
|
memoryInferenceDurationMs: inference.durationMs,
|
|
402
|
-
graphExtractionDurationMs: graph.durationMs,
|
|
403
390
|
orphansPurged: hygiene.orphansPurged,
|
|
404
391
|
proposalsExpired: hygiene.proposalsExpired,
|
|
405
392
|
};
|
|
@@ -467,72 +454,6 @@ export async function runMemoryInferenceMaintenancePass(ctx, dbCell, memoryRefsF
|
|
|
467
454
|
warnings: pass.warnings,
|
|
468
455
|
};
|
|
469
456
|
}
|
|
470
|
-
/**
|
|
471
|
-
* Graph extraction over the files this run touched, or the whole corpus when
|
|
472
|
-
* the profile sets `graphExtraction.fullScan` (the `graph-refresh` strategy).
|
|
473
|
-
* With nothing touched the pass still runs and extracts nothing.
|
|
474
|
-
*/
|
|
475
|
-
export async function runGraphExtractionMaintenancePass(ctx, dbCell, args) {
|
|
476
|
-
const { config, sources, primaryStashDir, resolvedPlan } = ctx;
|
|
477
|
-
const settings = ctx.improveProfile?.processes?.graphExtraction;
|
|
478
|
-
const graphEnabled = resolvedPlan ? true : isProcessEnabled("index", "graph_extraction", config);
|
|
479
|
-
if (settings?.enabled === false) {
|
|
480
|
-
info("[improve] graph extraction skipped (disabled by improve profile)");
|
|
481
|
-
return { durationMs: 0, warnings: [] };
|
|
482
|
-
}
|
|
483
|
-
if (sources.length === 0)
|
|
484
|
-
return { durationMs: 0, warnings: [] };
|
|
485
|
-
if (!graphEnabled) {
|
|
486
|
-
info("[improve] graph extraction skipped (features.index.graph_extraction is disabled)");
|
|
487
|
-
return { durationMs: 0, warnings: [] };
|
|
488
|
-
}
|
|
489
|
-
const fullScan = settings?.fullScan === true;
|
|
490
|
-
info(`[improve] graph extraction starting${fullScan ? " (full-corpus scan)" : ""}`);
|
|
491
|
-
const pass = await timedLlmPass("graph extraction", async () => {
|
|
492
|
-
let candidatePaths;
|
|
493
|
-
if (!fullScan) {
|
|
494
|
-
candidatePaths = new Set();
|
|
495
|
-
const touched = new Set([...args.actionableRefs.map((r) => r.ref), ...args.memoryRefsForInference]);
|
|
496
|
-
if (primaryStashDir && touched.size > 0) {
|
|
497
|
-
const writableBundleIds = deriveWritableBundleIds(resolveSourceEntries(primaryStashDir));
|
|
498
|
-
const resolved = await Promise.all([...touched].map((ref) => findAssetFilePath(ref, primaryStashDir, writableBundleIds).catch(() => null)));
|
|
499
|
-
for (const p of resolved)
|
|
500
|
-
if (typeof p === "string" && p.length > 0)
|
|
501
|
-
candidatePaths.add(p);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
return attributeStage(resolvedPlan, "graphExtraction", () => ctx.graphExtractionFn({
|
|
505
|
-
config,
|
|
506
|
-
...(resolvedPlan ? { llmRunner: resolvedPlan.processes.graphExtraction.runner } : {}),
|
|
507
|
-
sources,
|
|
508
|
-
signal: ctx.budgetSignal,
|
|
509
|
-
db: dbCell.current,
|
|
510
|
-
reEnrich: false,
|
|
511
|
-
onProgress: (event) => {
|
|
512
|
-
const current = event.currentPath ? ` ${path.basename(event.currentPath)}` : "";
|
|
513
|
-
info(`[improve] graph extraction ${event.processed}/${event.total}${current} (extracted ${event.extracted}, entities ${event.totalEntities}, relations ${event.totalRelations})`);
|
|
514
|
-
},
|
|
515
|
-
options: {
|
|
516
|
-
candidatePaths,
|
|
517
|
-
// Only what the strategy sets: the pass falls back to index.graph, then its defaults (GR-D15).
|
|
518
|
-
...(settings?.includeTypes ? { includeTypes: settings.includeTypes } : {}),
|
|
519
|
-
...(settings?.batchSize != null ? { batchSize: settings.batchSize } : {}),
|
|
520
|
-
...(settings?.topN != null ? { topN: settings.topN } : {}),
|
|
521
|
-
...(settings?.maxChunksPerAsset != null ? { maxChunksPerAsset: settings.maxChunksPerAsset } : {}),
|
|
522
|
-
},
|
|
523
|
-
}));
|
|
524
|
-
});
|
|
525
|
-
const graphExtraction = pass.result;
|
|
526
|
-
if (!graphExtraction)
|
|
527
|
-
return { durationMs: pass.durationMs, warnings: pass.warnings };
|
|
528
|
-
info(`[improve] graph extraction complete (${graphExtraction.quality.extractedFiles} files, ${graphExtraction.quality.entityCount} entities, ${graphExtraction.quality.relationCount} relations)`);
|
|
529
|
-
return {
|
|
530
|
-
graphExtraction,
|
|
531
|
-
durationMs: pass.durationMs,
|
|
532
|
-
action: { ref: "graph/_artifact", mode: "graph-extraction", result: graphExtraction },
|
|
533
|
-
warnings: pass.warnings,
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
457
|
/**
|
|
537
458
|
* Reject pending proposals whose target no longer exists, then expire pending
|
|
538
459
|
* proposals past the retention window; each emits a roll-up event.
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* frontmatter and demote its `beliefState`, metadata only. Idempotent (a
|
|
7
7
|
* present edge AND demotion is a no-op; an edge without its demotion is
|
|
8
8
|
* repaired) and never weakens a stronger demotion — severity is
|
|
9
|
-
* superseded > contradicted > archived
|
|
9
|
+
* superseded > contradicted > archived. (alpha.4 removed belief weights from
|
|
10
|
+
* ranking: search no longer reads `beliefState` at all, and `--belief
|
|
11
|
+
* current|historical` is the only reader, opt-in.)
|
|
10
12
|
* The SCC resolver in memory-improve.ts is a state-transition writer (it
|
|
11
13
|
* replaces and clears edges) and deliberately does not use this (#885).
|
|
12
14
|
*/
|