akm-cli 0.9.0-rc.1 → 0.9.0-rc.2
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 +223 -9
- package/SECURITY.md +23 -24
- package/dist/assets/help/help-improve.md +10 -10
- package/dist/assets/hints/cli-hints-full.md +44 -10
- package/dist/assets/hints/cli-hints-short.md +6 -2
- package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
- package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
- package/dist/assets/stash-skeleton/README.md +28 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
- package/dist/assets/tasks/core/backup.yml +1 -0
- package/dist/assets/tasks/core/extract.yml +1 -0
- package/dist/assets/tasks/core/improve.yml +1 -0
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +1 -0
- package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
- package/dist/assets/templates/html/health.html +5 -1
- package/dist/cli/config-migrate.js +31 -138
- package/dist/cli/config-validate.js +10 -8
- package/dist/cli.js +48 -14
- package/dist/commands/agent/agent-dispatch.js +17 -16
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +5 -15
- package/dist/commands/backup-cli.js +54 -0
- package/dist/commands/config-cli.js +45 -159
- package/dist/commands/env/secret.js +8 -5
- package/dist/commands/health/checks.js +130 -83
- package/dist/commands/health/html-report.js +4 -0
- package/dist/commands/health/improve-metrics.js +30 -32
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +4 -0
- package/dist/commands/health/metrics.js +2 -1
- package/dist/commands/health/surfaces.js +5 -4
- package/dist/commands/health.js +1 -1
- package/dist/commands/improve/consolidate/chunking.js +2 -2
- package/dist/commands/improve/consolidate.js +28 -25
- package/dist/commands/improve/distill/promote-memory.js +5 -12
- package/dist/commands/improve/distill/quality-gate.js +5 -7
- package/dist/commands/improve/distill.js +16 -5
- package/dist/commands/improve/eligibility.js +22 -12
- package/dist/commands/improve/extract-cli.js +47 -19
- package/dist/commands/improve/extract.js +110 -62
- package/dist/commands/improve/improve-cli.js +38 -16
- package/dist/commands/improve/improve-result-file.js +30 -24
- package/dist/commands/improve/improve-strategies.js +137 -0
- package/dist/commands/improve/improve.js +60 -30
- package/dist/commands/improve/locks.js +66 -45
- package/dist/commands/improve/loop-stages.js +75 -33
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
- package/dist/commands/improve/preparation.js +71 -73
- package/dist/commands/improve/procedural.js +3 -2
- package/dist/commands/improve/recombine.js +2 -1
- package/dist/commands/improve/reflect.js +119 -214
- package/dist/commands/improve/shared.js +11 -5
- package/dist/commands/lint/base-linter.js +152 -42
- package/dist/commands/mv-cli.js +809 -0
- package/dist/commands/proposal/proposal-cli.js +18 -8
- package/dist/commands/proposal/propose.js +64 -69
- package/dist/commands/read/knowledge.js +436 -4
- package/dist/commands/read/remember-cli.js +39 -2
- package/dist/commands/read/search-cli.js +6 -1
- package/dist/commands/registry-cli.js +29 -14
- package/dist/commands/remember.js +2 -0
- package/dist/commands/sources/init.js +13 -14
- package/dist/commands/sources/schema-repair.js +2 -4
- package/dist/commands/sources/source-add.js +62 -73
- package/dist/commands/sources/source-manage.js +50 -46
- package/dist/commands/sources/stash-cli.js +41 -4
- package/dist/commands/tasks/default-tasks.js +12 -12
- package/dist/commands/tasks/tasks-cli.js +7 -3
- package/dist/commands/tasks/tasks.js +113 -18
- package/dist/commands/wiki-cli.js +9 -10
- package/dist/core/asset/frontmatter.js +12 -2
- package/dist/core/common.js +5 -3
- package/dist/core/config/config-io.js +28 -17
- package/dist/core/config/config-schema.js +358 -66
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +98 -27
- package/dist/core/config/config.js +132 -266
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +32 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/extra-params.js +61 -0
- package/dist/core/file-lock.js +201 -56
- package/dist/core/improve-result.js +178 -0
- package/dist/core/maintenance-barrier.js +119 -0
- package/dist/core/migration-backup.js +416 -0
- package/dist/core/paths.js +3 -0
- package/dist/core/redaction.js +358 -0
- package/dist/core/state/migrations.js +17 -2
- package/dist/core/state-db.js +44 -1
- package/dist/indexer/db/db.js +116 -1
- package/dist/indexer/graph/graph-extraction.js +28 -16
- package/dist/indexer/index-writer-lock.js +31 -24
- package/dist/indexer/index-written-assets.js +15 -6
- package/dist/indexer/indexer.js +47 -2
- package/dist/indexer/passes/memory-inference.js +10 -6
- package/dist/indexer/passes/metadata.js +250 -0
- package/dist/indexer/search/db-search.js +111 -44
- package/dist/indexer/search/fts-query.js +41 -0
- package/dist/indexer/search/ranking-contributors.js +48 -0
- package/dist/indexer/search/ranking.js +36 -23
- package/dist/indexer/search/search-fields.js +11 -1
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -52
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +202 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/runner-dispatch.js +76 -13
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +4 -6
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +0 -2
- package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +1 -24
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +0 -6
- package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/llm/client.js +48 -14
- package/dist/llm/feature-gate.js +15 -47
- package/dist/llm/graph-extract.js +1 -1
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer-impl.js +1 -1
- package/dist/llm/usage-persist.js +4 -0
- package/dist/llm/usage-telemetry.js +35 -5
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/helpers.js +3 -1
- package/dist/schemas/akm-config.json +11013 -8600
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +57 -13
- package/dist/scripts/migrate-storage.js +8591 -509
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8623 -484
- package/dist/setup/detected-engines.js +142 -0
- package/dist/setup/engine-config.js +89 -0
- package/dist/setup/setup.js +236 -132
- package/dist/setup/steps/connection.js +61 -32
- package/dist/setup/steps/platforms.js +4 -4
- package/dist/setup/steps.js +3 -2
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +1 -0
- package/dist/storage/repositories/improve-runs-repository.js +5 -5
- package/dist/storage/repositories/task-history-repository.js +78 -0
- package/dist/storage/repositories/workflow-runs-repository.js +9 -8
- package/dist/tasks/parser.js +138 -52
- package/dist/tasks/runner.js +71 -75
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/validator.js +11 -6
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +65 -13
- package/dist/workflows/exec/brief.js +18 -24
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +124 -65
- package/dist/workflows/exec/report.js +93 -33
- package/dist/workflows/exec/run-workflow.js +38 -25
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +91 -35
- package/dist/workflows/ir/compile.js +13 -26
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +542 -1
- package/dist/workflows/parser.js +7 -0
- package/dist/workflows/program/parser.js +132 -23
- package/dist/workflows/program/project.js +3 -4
- package/dist/workflows/program/schema.js +2 -2
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +54 -47
- package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
- package/dist/workflows/validator.js +25 -0
- package/docs/data-and-telemetry.md +2 -2
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +5 -4
- package/docs/migration/v0.8-to-v0.9.md +401 -0
- package/package.json +4 -2
- package/schemas/akm-config.json +16638 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +372 -0
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/llm/call-ai.js +0 -62
- package/dist/setup/legacy-config.js +0 -106
- package/docs/README.md +0 -104
- /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
6
6
|
import { daysToMs } from "../../core/common.js";
|
|
7
|
-
import { loadConfig } from "../../core/config/config.js";
|
|
7
|
+
import { DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE, loadConfig } from "../../core/config/config.js";
|
|
8
8
|
import { UsageError } from "../../core/errors.js";
|
|
9
9
|
import { appendEvent } from "../../core/events.js";
|
|
10
10
|
import { openLogsDatabase, purgeOldTaskLogs } from "../../core/logs-db.js";
|
|
@@ -12,11 +12,11 @@ import { getDbPath } from "../../core/paths.js";
|
|
|
12
12
|
import { withStateDb } from "../../core/state-db.js";
|
|
13
13
|
import { info, warn } from "../../core/warn.js";
|
|
14
14
|
import { closeDatabase, openIndexDatabase } from "../../indexer/db/db.js";
|
|
15
|
-
import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
|
|
15
|
+
import { DEFAULT_GRAPH_EXTRACTION_INCLUDE_TYPES, runGraphExtractionPass, } from "../../indexer/graph/graph-extraction.js";
|
|
16
16
|
import { withIndexWriterLease } from "../../indexer/index-writer-lock.js";
|
|
17
17
|
import { collectPendingMemories, runMemoryInferencePass, } from "../../indexer/passes/memory-inference.js";
|
|
18
18
|
import { getWritableStashDirs, resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
19
|
-
import {
|
|
19
|
+
import { materializeLlmRunnerConnection } from "../../integrations/agent/runner.js";
|
|
20
20
|
import { isProcessEnabled } from "../../llm/feature-gate.js";
|
|
21
21
|
import { withLlmStage } from "../../llm/usage-telemetry.js";
|
|
22
22
|
import { purgeOldCycleMetrics } from "../../storage/repositories/canaries-repository.js";
|
|
@@ -31,7 +31,7 @@ import { deriveKnowledgeRef } from "./distill-promotion-policy.js";
|
|
|
31
31
|
import { findAssetFilePath, isDistillCandidateRef } from "./eligibility.js";
|
|
32
32
|
import { writeEvalCase } from "./eval-cases.js";
|
|
33
33
|
import { makeGateConfig, runAutoAcceptGate } from "./improve-auto-accept.js";
|
|
34
|
-
import {
|
|
34
|
+
import { shouldSkipRef } from "./improve-strategies.js";
|
|
35
35
|
// The pre-loop preparation pipeline lives in ./preparation.
|
|
36
36
|
import { maybeAutoTuneThreshold } from "./preparation.js";
|
|
37
37
|
import { akmProcedural } from "./procedural.js";
|
|
@@ -41,7 +41,7 @@ import { errMessage, refSlug } from "./shared.js";
|
|
|
41
41
|
// ── improve loop / post-loop / maintenance stages ───────────────────
|
|
42
42
|
// The cycle stages run by akmImprove, extracted from improve.ts.
|
|
43
43
|
export async function runImproveLoopStage(args) {
|
|
44
|
-
const { scope, options, primaryStashDir, reflectFn, distillFn, loopRefs, actions, signalBearingSet, distillCooledRefs, distillOnlyRefs, recentErrors, rejectedProposalsByRef, utilityMap, startMs, budgetMs, eventsCtx, improveProfile, } = args;
|
|
44
|
+
const { scope, options, primaryStashDir, reflectFn, distillFn, loopRefs, actions, signalBearingSet, distillCooledRefs, distillOnlyRefs, recentErrors, rejectedProposalsByRef, utilityMap, startMs, budgetMs, eventsCtx, improveProfile, resolvedPlan, } = args;
|
|
45
45
|
// O-1 (#364): compute remaining budget at call time so each sub-call
|
|
46
46
|
// receives only its fair share of the wall-clock budget.
|
|
47
47
|
const remainingBudgetMs = () => Math.max(0, budgetMs - (Date.now() - startMs));
|
|
@@ -206,26 +206,22 @@ export async function runImproveLoopStage(args) {
|
|
|
206
206
|
// O-1 (#364): pass remaining budget as timeoutMs so the agent spawn is
|
|
207
207
|
// bounded by the wall-clock deadline rather than the default per-profile timeout.
|
|
208
208
|
const reflectBudgetMs = remainingBudgetMs();
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
// (profiles.improve.<name>.processes.reflect → defaults.llm) when the profile
|
|
212
|
-
// does not specify.
|
|
213
|
-
const reflectProfileRunner = resolveImproveProcessRunnerFromProfile(improveProfile.processes?.reflect, options.config ?? loadConfig());
|
|
209
|
+
// Use the runner frozen in the invocation plan; no leaf re-resolution.
|
|
210
|
+
const reflectProfileRunner = resolvedPlan.processes.reflect.runner;
|
|
214
211
|
const reflectCallArgs = {
|
|
215
212
|
ref: planned.ref,
|
|
216
213
|
task: options.task,
|
|
217
|
-
// Active
|
|
214
|
+
// Active strategy supplies non-engine process tuning.
|
|
218
215
|
...(improveProfile ? { improveProfile } : {}),
|
|
216
|
+
config: options.config,
|
|
219
217
|
...(options.stashDir ? { stashDir: options.stashDir } : {}),
|
|
220
218
|
...(reflectErrors.length > 0 ? { avoidPatterns: [...reflectErrors] } : {}),
|
|
221
|
-
agentProcess: options.agentProcess ?? "reflect",
|
|
222
219
|
eventSource: "improve",
|
|
223
220
|
// #639 — resolve the low-value filter from the ACTIVE improve profile
|
|
224
|
-
// (default off when unset), so the running
|
|
225
|
-
// a hardcoded profiles.improve.default path.
|
|
221
|
+
// (default off when unset), so the running strategy decides.
|
|
226
222
|
lowValueFilter: improveProfile.processes?.reflect?.lowValueFilter?.enabled === true,
|
|
227
223
|
...(reflectBudgetMs > 0 ? { timeoutMs: reflectBudgetMs } : {}),
|
|
228
|
-
|
|
224
|
+
runner: reflectProfileRunner ?? null,
|
|
229
225
|
// Attribution: carry the eligibility lane so reflect stamps it on
|
|
230
226
|
// the reflect_invoked event and the persisted proposal.
|
|
231
227
|
...(planned.eligibilitySource ? { eligibilitySource: planned.eligibilitySource } : {}),
|
|
@@ -241,11 +237,19 @@ export async function runImproveLoopStage(args) {
|
|
|
241
237
|
if (remainingBudgetMs() <= 0)
|
|
242
238
|
break;
|
|
243
239
|
// draftMode: skip DB write so each sample doesn't create a proposal.
|
|
244
|
-
samples.push(await withLlmStage("reflect", () => reflectFn({ ...reflectCallArgs, draftMode: true })
|
|
240
|
+
samples.push(await withLlmStage("reflect", () => reflectFn({ ...reflectCallArgs, draftMode: true }), {
|
|
241
|
+
engine: resolvedPlan.processes.reflect.runner?.engine,
|
|
242
|
+
process: "reflect",
|
|
243
|
+
}));
|
|
245
244
|
}
|
|
246
245
|
const winner = pickMajorityVote(samples.length > 0
|
|
247
246
|
? samples
|
|
248
|
-
: [
|
|
247
|
+
: [
|
|
248
|
+
await withLlmStage("reflect", () => reflectFn({ ...reflectCallArgs, draftMode: true }), {
|
|
249
|
+
engine: resolvedPlan.processes.reflect.runner?.engine,
|
|
250
|
+
process: "reflect",
|
|
251
|
+
}),
|
|
252
|
+
]);
|
|
249
253
|
// Persist only the majority-vote winner as a single real proposal.
|
|
250
254
|
if (winner.ok && primaryStashDir) {
|
|
251
255
|
const persistResult = createProposal(primaryStashDir, {
|
|
@@ -259,11 +263,12 @@ export async function runImproveLoopStage(args) {
|
|
|
259
263
|
});
|
|
260
264
|
reflectResult = isProposalSkipped(persistResult)
|
|
261
265
|
? {
|
|
262
|
-
schemaVersion:
|
|
266
|
+
schemaVersion: 2,
|
|
263
267
|
ok: false,
|
|
264
268
|
reason: "cooldown",
|
|
265
269
|
error: `SC proposal skipped: ${persistResult.message}`,
|
|
266
270
|
ref: winner.ref,
|
|
271
|
+
engine: winner.engine,
|
|
267
272
|
exitCode: null,
|
|
268
273
|
}
|
|
269
274
|
: { ...winner, proposal: persistResult };
|
|
@@ -273,7 +278,10 @@ export async function runImproveLoopStage(args) {
|
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
else {
|
|
276
|
-
reflectResult = await withLlmStage("reflect", () => reflectFn(reflectCallArgs)
|
|
281
|
+
reflectResult = await withLlmStage("reflect", () => reflectFn(reflectCallArgs), {
|
|
282
|
+
engine: resolvedPlan.processes.reflect.runner?.engine,
|
|
283
|
+
process: "reflect",
|
|
284
|
+
});
|
|
277
285
|
}
|
|
278
286
|
const isCooldown = !reflectResult.ok && reflectResult.reason === "cooldown";
|
|
279
287
|
// Content-policy guard hits (reflect size-rail rejections) are NOT
|
|
@@ -327,7 +335,7 @@ export async function runImproveLoopStage(args) {
|
|
|
327
335
|
metadata: {
|
|
328
336
|
ok: reflectResult.ok,
|
|
329
337
|
durationMs: reflectResult.ok ? reflectResult.durationMs : undefined,
|
|
330
|
-
|
|
338
|
+
engine: reflectResult.engine,
|
|
331
339
|
reason: reflectResult.ok ? undefined : reflectResult.reason,
|
|
332
340
|
},
|
|
333
341
|
}, eventsCtx);
|
|
@@ -478,10 +486,14 @@ export async function runImproveLoopStage(args) {
|
|
|
478
486
|
...(options.stashDir ? { stashDir: options.stashDir } : {}),
|
|
479
487
|
// Active profile so distill's per-process reads honor `--profile`.
|
|
480
488
|
...(improveProfile ? { improveProfile } : {}),
|
|
489
|
+
config: options.config,
|
|
490
|
+
llmConfig: resolvedPlan.processes.distill.runner
|
|
491
|
+
? materializeLlmRunnerConnection(resolvedPlan.processes.distill.runner)
|
|
492
|
+
: null,
|
|
481
493
|
// Attribution: carry the eligibility lane so distill stamps it on the
|
|
482
494
|
// distill_invoked event and the persisted proposal.
|
|
483
495
|
...(planned.eligibilitySource ? { eligibilitySource: planned.eligibilitySource } : {}),
|
|
484
|
-
}));
|
|
496
|
+
}), { engine: resolvedPlan.processes.distill.runner?.engine, process: "distill" });
|
|
485
497
|
actions.push({ ref: planned.ref, mode: "distill", result: distillResult });
|
|
486
498
|
if (distillResult.outcome === "queued" && distillResult.proposal) {
|
|
487
499
|
const distillGr = await runAutoAcceptGate([{ proposalId: distillResult.proposal.id, confidence: distillResult.proposal.confidence }], distillGateCfg);
|
|
@@ -593,7 +605,7 @@ export async function runImproveLoopStage(args) {
|
|
|
593
605
|
return { reflectsWithErrorContext, memoryRefsForInference, gateAutoAcceptedCount, gateAutoAcceptFailedCount };
|
|
594
606
|
}
|
|
595
607
|
export async function runImprovePostLoopStage(args) {
|
|
596
|
-
const { scope, options, primaryStashDir, actionableRefs, appliedCleanup, cleanupWarnings, memoryRefsForInference, reindexFn, eventsCtx, budgetSignal, improveProfile, consolidationRan, } = args;
|
|
608
|
+
const { scope, options, primaryStashDir, actionableRefs, appliedCleanup, cleanupWarnings, memoryRefsForInference, reindexFn, eventsCtx, budgetSignal, improveProfile, resolvedPlan, consolidationRan, } = args;
|
|
597
609
|
const allWarnings = [...cleanupWarnings, ...(appliedCleanup?.warnings ?? [])];
|
|
598
610
|
info("[improve] post-loop maintenance starting");
|
|
599
611
|
const maintenanceResult = await runImproveMaintenancePasses({
|
|
@@ -608,6 +620,7 @@ export async function runImprovePostLoopStage(args) {
|
|
|
608
620
|
budgetSignal,
|
|
609
621
|
eventsCtx,
|
|
610
622
|
improveProfile,
|
|
623
|
+
resolvedPlan,
|
|
611
624
|
});
|
|
612
625
|
let deadUrls;
|
|
613
626
|
if (scope.mode === "all" && primaryStashDir && actionableRefs.length > 0) {
|
|
@@ -641,15 +654,18 @@ export async function runImprovePostLoopStage(args) {
|
|
|
641
654
|
let recombination;
|
|
642
655
|
if (primaryStashDir &&
|
|
643
656
|
improveProfile &&
|
|
644
|
-
|
|
657
|
+
resolvedPlan?.processes.recombine.enabled === true &&
|
|
645
658
|
scope.mode !== "ref" &&
|
|
646
659
|
!options.dryRun) {
|
|
647
660
|
const recombineFn = options.recombineFn ?? akmRecombine;
|
|
648
661
|
try {
|
|
649
|
-
recombination = await recombineFn({
|
|
662
|
+
recombination = await withLlmStage("recombine", () => recombineFn({
|
|
650
663
|
stashDir: primaryStashDir,
|
|
651
664
|
config: options.config ?? loadConfig(),
|
|
652
665
|
improveProfile,
|
|
666
|
+
llmConfig: resolvedPlan?.processes.recombine.runner
|
|
667
|
+
? materializeLlmRunnerConnection(resolvedPlan.processes.recombine.runner)
|
|
668
|
+
: null,
|
|
653
669
|
...(options.runId ? { sourceRun: options.runId } : {}),
|
|
654
670
|
...(budgetSignal ? { signal: budgetSignal } : {}),
|
|
655
671
|
eligibilitySource: "recombine",
|
|
@@ -662,7 +678,7 @@ export async function runImprovePostLoopStage(args) {
|
|
|
662
678
|
maxClusterSize: improveProfile.processes?.recombine?.maxClusterSize,
|
|
663
679
|
excludeTags: improveProfile.processes?.recombine?.excludeTags,
|
|
664
680
|
excludeEntities: improveProfile.processes?.recombine?.excludeEntities,
|
|
665
|
-
});
|
|
681
|
+
}), { engine: resolvedPlan?.processes.recombine.runner?.engine, process: "recombine" });
|
|
666
682
|
}
|
|
667
683
|
catch (e) {
|
|
668
684
|
allWarnings.push(`recombine: ${String(e)}`);
|
|
@@ -675,22 +691,25 @@ export async function runImprovePostLoopStage(args) {
|
|
|
675
691
|
let proceduralCompilation;
|
|
676
692
|
if (primaryStashDir &&
|
|
677
693
|
improveProfile &&
|
|
678
|
-
|
|
694
|
+
resolvedPlan?.processes.procedural.enabled === true &&
|
|
679
695
|
scope.mode !== "ref" &&
|
|
680
696
|
!options.dryRun) {
|
|
681
697
|
const proceduralFn = options.proceduralFn ?? akmProcedural;
|
|
682
698
|
try {
|
|
683
|
-
proceduralCompilation = await proceduralFn({
|
|
699
|
+
proceduralCompilation = await withLlmStage("procedural", () => proceduralFn({
|
|
684
700
|
stashDir: primaryStashDir,
|
|
685
701
|
config: options.config ?? loadConfig(),
|
|
686
702
|
...(improveProfile ? { improveProfile } : {}),
|
|
703
|
+
llmConfig: resolvedPlan?.processes.procedural.runner
|
|
704
|
+
? materializeLlmRunnerConnection(resolvedPlan.processes.procedural.runner)
|
|
705
|
+
: null,
|
|
687
706
|
...(options.runId ? { sourceRun: options.runId } : {}),
|
|
688
707
|
...(budgetSignal ? { signal: budgetSignal } : {}),
|
|
689
708
|
eligibilitySource: "procedural",
|
|
690
709
|
...(eventsCtx ? { ctx: eventsCtx } : {}),
|
|
691
710
|
minRecurrence: improveProfile.processes?.procedural?.minRecurrence,
|
|
692
711
|
maxProposalsPerRun: improveProfile.processes?.procedural?.maxProposalsPerRun,
|
|
693
|
-
});
|
|
712
|
+
}), { engine: resolvedPlan?.processes.procedural.runner?.engine, process: "procedural" });
|
|
694
713
|
}
|
|
695
714
|
catch (e) {
|
|
696
715
|
allWarnings.push(`procedural: ${String(e)}`);
|
|
@@ -746,7 +765,7 @@ export async function runImprovePostLoopStage(args) {
|
|
|
746
765
|
// Exported for tests (#584/#585 DB-locking regression coverage); production
|
|
747
766
|
// callers reach it only through akmImprove → runImprovePostLoopStage.
|
|
748
767
|
export async function runImproveMaintenancePasses(args) {
|
|
749
|
-
const { options, primaryStashDir, memoryRefsForInference, allWarnings, reindexFn, consolidationRan, budgetSignal, eventsCtx, improveProfile, } = args;
|
|
768
|
+
const { options, primaryStashDir, memoryRefsForInference, allWarnings, reindexFn, consolidationRan, budgetSignal, eventsCtx, improveProfile, resolvedPlan, } = args;
|
|
750
769
|
if (!primaryStashDir)
|
|
751
770
|
return { memoryInferenceDurationMs: 0, graphExtractionDurationMs: 0 };
|
|
752
771
|
const config = options.config ?? loadConfig();
|
|
@@ -825,6 +844,13 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
825
844
|
// O-1 (#364): pass budget signal so a hung inference call is cancelled.
|
|
826
845
|
memoryInference = await withLlmStage("memory-inference", () => memoryInferenceFn({
|
|
827
846
|
config,
|
|
847
|
+
...(resolvedPlan
|
|
848
|
+
? {
|
|
849
|
+
llmConfig: resolvedPlan.processes.memoryInference.runner
|
|
850
|
+
? materializeLlmRunnerConnection(resolvedPlan.processes.memoryInference.runner)
|
|
851
|
+
: null,
|
|
852
|
+
}
|
|
853
|
+
: {}),
|
|
828
854
|
sources,
|
|
829
855
|
signal: budgetSignal,
|
|
830
856
|
db,
|
|
@@ -833,7 +859,7 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
833
859
|
const current = event.currentRef ? ` ${event.currentRef}` : "";
|
|
834
860
|
info(`[improve] memory inference ${event.processed}/${event.total}${current} (written ${event.writtenFacts}, skipped ${event.skippedNoFacts})`);
|
|
835
861
|
},
|
|
836
|
-
}));
|
|
862
|
+
}), { engine: resolvedPlan?.processes.memoryInference.runner?.engine, process: "memoryInference" });
|
|
837
863
|
memoryInferenceDurationMs = Date.now() - inferenceStart;
|
|
838
864
|
actions.push({ ref: "memory:_inference", mode: "memory-inference", result: memoryInference });
|
|
839
865
|
info(`[improve] memory inference complete (${memoryInference.writtenFacts} facts written from ${memoryInference.splitParents} parents)`);
|
|
@@ -854,12 +880,16 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
854
880
|
allWarnings.push(`reindex after memory inference failed: ${errMessage(err)}`);
|
|
855
881
|
}
|
|
856
882
|
}
|
|
857
|
-
const graphEnabled = isProcessEnabled("index", "graph_extraction", config);
|
|
883
|
+
const graphEnabled = resolvedPlan ? true : isProcessEnabled("index", "graph_extraction", config);
|
|
858
884
|
const graphExtractionDisabledByProfile = improveProfile?.processes?.graphExtraction?.enabled === false;
|
|
859
885
|
const graphExtractionFullScan = improveProfile?.processes?.graphExtraction?.fullScan === true;
|
|
860
886
|
// #624 P2: optional incremental high-signal-first cap. Unset = process all
|
|
861
887
|
// eligible (byte-identical to today; no ranking/slice).
|
|
862
888
|
const graphExtractionTopN = improveProfile?.processes?.graphExtraction?.topN;
|
|
889
|
+
const graphExtractionIncludeTypes = improveProfile?.processes?.graphExtraction?.includeTypes ?? [
|
|
890
|
+
...DEFAULT_GRAPH_EXTRACTION_INCLUDE_TYPES,
|
|
891
|
+
];
|
|
892
|
+
const graphExtractionBatchSize = improveProfile?.processes?.graphExtraction?.batchSize ?? DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE;
|
|
863
893
|
// Build the set of refs actually touched this run.
|
|
864
894
|
const touchedRefs = new Set();
|
|
865
895
|
for (const r of args.actionableRefs)
|
|
@@ -916,13 +946,25 @@ export async function runImproveMaintenancePasses(args) {
|
|
|
916
946
|
// O-1 (#364): pass budget signal so a hung graph extraction call is cancelled.
|
|
917
947
|
graphExtraction = await withLlmStage("graph-extraction", () => graphExtractionFn({
|
|
918
948
|
config,
|
|
949
|
+
...(resolvedPlan
|
|
950
|
+
? {
|
|
951
|
+
llmConfig: resolvedPlan.processes.graphExtraction.runner
|
|
952
|
+
? materializeLlmRunnerConnection(resolvedPlan.processes.graphExtraction.runner)
|
|
953
|
+
: null,
|
|
954
|
+
}
|
|
955
|
+
: {}),
|
|
919
956
|
sources,
|
|
920
957
|
signal: budgetSignal,
|
|
921
958
|
db,
|
|
922
959
|
reEnrich: false,
|
|
923
960
|
onProgress: progressHandler,
|
|
924
|
-
options: {
|
|
925
|
-
|
|
961
|
+
options: {
|
|
962
|
+
candidatePaths,
|
|
963
|
+
includeTypes: graphExtractionIncludeTypes,
|
|
964
|
+
batchSize: graphExtractionBatchSize,
|
|
965
|
+
...(graphExtractionTopN != null ? { topN: graphExtractionTopN } : {}),
|
|
966
|
+
},
|
|
967
|
+
}), { engine: resolvedPlan?.processes.graphExtraction.runner?.engine, process: "graphExtraction" });
|
|
926
968
|
graphExtractionDurationMs = Date.now() - extractionStart;
|
|
927
969
|
actions.push({ ref: "graph:_artifact", mode: "graph-extraction", result: graphExtraction });
|
|
928
970
|
info(`[improve] graph extraction complete (${graphExtraction.quality.extractedFiles} files, ${graphExtraction.quality.entityCount} entities, ${graphExtraction.quality.relationCount} relations)`);
|
|
@@ -28,6 +28,25 @@
|
|
|
28
28
|
* - MemOS (arXiv:2507.03724) — formal archive/merge/transition with shared state model
|
|
29
29
|
*/
|
|
30
30
|
import { mutateFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
31
|
+
// ── Shared edge-list reader ───────────────────────────────────────────────────
|
|
32
|
+
/**
|
|
33
|
+
* Read a frontmatter edge value (`supersededBy` / `contradictedBy`) as a
|
|
34
|
+
* string list, promoting a scalar string to a one-element list.
|
|
35
|
+
*
|
|
36
|
+
* Scalar edges are LIVE data: the indexer's `normalizeNonEmptyStringList`
|
|
37
|
+
* accepts them and lint deliberately never flags them. An Array.isArray-only
|
|
38
|
+
* read treats a scalar as "no existing edges" and silently destroys the edge
|
|
39
|
+
* on the next merge — mirror `mergeXrefsIntoContent`'s scalar promotion
|
|
40
|
+
* instead.
|
|
41
|
+
*/
|
|
42
|
+
function readEdgeList(value) {
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
return value.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === "string" && value.trim())
|
|
47
|
+
return [value.trim()];
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
31
50
|
// ── Contradiction edge writer ─────────────────────────────────────────────────
|
|
32
51
|
/**
|
|
33
52
|
* Write `contradictedBy` and `beliefState: contradicted` edges to a memory
|
|
@@ -38,24 +57,77 @@ import { mutateFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
|
38
57
|
* - `memory-contradiction-detect.ts` for the M-1 automated contradiction pass
|
|
39
58
|
* - `resolveFamilyContradictions` in `memory-improve.ts` for SCC resolution
|
|
40
59
|
*
|
|
41
|
-
* Idempotent: if the `contradictedByRef` is already in `contradictedBy
|
|
42
|
-
* the file is not rewritten.
|
|
60
|
+
* Idempotent: if the `contradictedByRef` is already in `contradictedBy` AND
|
|
61
|
+
* the file already carries the demotion state, the file is not rewritten. The
|
|
62
|
+
* guard is state-aware like its sibling {@link writeSupersededEdge}: an edge
|
|
63
|
+
* present WITHOUT the demotion (e.g. a hand-written `contradictedBy:` line,
|
|
64
|
+
* or a beliefState lost to a partial edit) is repaired, not skipped — an
|
|
65
|
+
* edge-only guard would make such a file a permanent no-op while
|
|
66
|
+
* consolidate's handleContradictOp reports the contradiction as applied.
|
|
67
|
+
*
|
|
68
|
+
* Never weakens a stronger demotion: `archived` ranks BELOW `contradicted`
|
|
69
|
+
* (see `BELIEF_STATE_SCORE_CEILINGS` in
|
|
70
|
+
* src/indexer/search/ranking-contributors.ts), so contradicting an archived
|
|
71
|
+
* memory keeps `archived` and only appends the edge.
|
|
43
72
|
*
|
|
44
73
|
* @param filePath - Absolute path to the memory markdown file.
|
|
45
74
|
* @param contradictedByRef - The ref that contradicts this memory.
|
|
46
75
|
*/
|
|
47
76
|
export function writeContradictEdge(filePath, contradictedByRef) {
|
|
48
77
|
mutateFrontmatter(filePath, (parsed) => {
|
|
49
|
-
const existing =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (existing.includes(contradictedByRef))
|
|
78
|
+
const existing = readEdgeList(parsed.data.contradictedBy);
|
|
79
|
+
const currentState = parsed.data.beliefState;
|
|
80
|
+
const nextState = currentState === "archived" ? currentState : "contradicted";
|
|
81
|
+
if (existing.includes(contradictedByRef) && currentState === nextState) {
|
|
53
82
|
return null; // Already written — idempotent.
|
|
83
|
+
}
|
|
54
84
|
const nextContradictedBy = [...new Set([...existing, contradictedByRef])].sort();
|
|
55
85
|
return {
|
|
56
86
|
...parsed.data,
|
|
57
87
|
contradictedBy: nextContradictedBy,
|
|
58
|
-
beliefState:
|
|
88
|
+
beliefState: nextState,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// ── Supersession edge writer ─────────────────────────────────────────────────
|
|
93
|
+
/**
|
|
94
|
+
* Write `supersededBy` and `beliefState: superseded` edges to an asset file's
|
|
95
|
+
* frontmatter (SPEC-5, stash-conventions-code-spec.md).
|
|
96
|
+
*
|
|
97
|
+
* Sibling of {@link writeContradictEdge}: the shared primitive for the
|
|
98
|
+
* conventions' corrections pattern — when a new asset supersedes an old one,
|
|
99
|
+
* the old asset gets a METADATA-ONLY demotion edit (every other frontmatter
|
|
100
|
+
* key and the body are preserved) so the ranker's beliefStateBoost demotes the
|
|
101
|
+
* stale incumbent and `--belief current` hides it. Used by
|
|
102
|
+
* `akm remember --supersedes` / `akm import --supersedes`.
|
|
103
|
+
*
|
|
104
|
+
* Idempotent: if `supersededByRef` is already in `supersededBy` and the file
|
|
105
|
+
* is already demoted, the file is not rewritten. Multiple corrections
|
|
106
|
+
* sorted-set-append their refs.
|
|
107
|
+
*
|
|
108
|
+
* Never WEAKENS an existing demotion: `contradicted` and `archived` rank
|
|
109
|
+
* BELOW `superseded` (severity order deprecated > superseded > contradicted >
|
|
110
|
+
* archived — see `BELIEF_STATE_SCORE_CEILINGS` in
|
|
111
|
+
* src/indexer/search/ranking-contributors.ts), so superseding an already
|
|
112
|
+
* contradicted/archived asset keeps the stronger state and only appends the
|
|
113
|
+
* `supersededBy` edge.
|
|
114
|
+
*
|
|
115
|
+
* @param filePath - Absolute path to the asset markdown file.
|
|
116
|
+
* @param supersededByRef - The ref of the correction that supersedes this asset.
|
|
117
|
+
*/
|
|
118
|
+
export function writeSupersededEdge(filePath, supersededByRef) {
|
|
119
|
+
mutateFrontmatter(filePath, (parsed) => {
|
|
120
|
+
const existing = readEdgeList(parsed.data.supersededBy);
|
|
121
|
+
const currentState = parsed.data.beliefState;
|
|
122
|
+
const nextState = currentState === "contradicted" || currentState === "archived" ? currentState : "superseded";
|
|
123
|
+
if (existing.includes(supersededByRef) && currentState === nextState) {
|
|
124
|
+
return null; // Already written — idempotent.
|
|
125
|
+
}
|
|
126
|
+
const nextSupersededBy = [...new Set([...existing, supersededByRef])].sort();
|
|
127
|
+
return {
|
|
128
|
+
...parsed.data,
|
|
129
|
+
supersededBy: nextSupersededBy,
|
|
130
|
+
beliefState: nextState,
|
|
59
131
|
};
|
|
60
132
|
});
|
|
61
133
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* # LLM Feature Gate
|
|
24
24
|
*
|
|
25
|
-
* The pass is gated
|
|
25
|
+
* The pass is gated by the selected strategy's consolidate contradiction setting.
|
|
26
26
|
* When the gate is disabled or no LLM is configured,
|
|
27
27
|
* the pass is a no-op and `analyzeMemoryCleanup` proceeds with only manually
|
|
28
28
|
* annotated edges.
|
|
@@ -38,6 +38,7 @@ import path from "node:path";
|
|
|
38
38
|
import contradictionJudgeTemplate from "../../../assets/prompts/contradiction-judge.md" with { type: "text" };
|
|
39
39
|
import { mutateFrontmatter, parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
40
40
|
import { getDefaultLlmConfig } from "../../../core/config/config.js";
|
|
41
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../../integrations/agent/runner.js";
|
|
41
42
|
import { chatCompletion, parseEmbeddedJsonResponse } from "../../../llm/client.js";
|
|
42
43
|
import { tryLlmFeature } from "../../../llm/feature-gate.js";
|
|
43
44
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
@@ -173,14 +174,20 @@ function pickContradictionLoser(a, b) {
|
|
|
173
174
|
* @param config - Loaded AKM config (used to access LLM settings).
|
|
174
175
|
* @param chat - Optional chat seam for testing (defaults to chatCompletion).
|
|
175
176
|
*/
|
|
176
|
-
export async function detectAndWriteContradictions(stashDir, config, chat = chatCompletion) {
|
|
177
|
+
export async function detectAndWriteContradictions(stashDir, config, chat = chatCompletion, strategy, resolvedLlmConfig) {
|
|
177
178
|
const result = {
|
|
178
179
|
familiesExamined: 0,
|
|
179
180
|
pairsChecked: 0,
|
|
180
181
|
edgesWritten: 0,
|
|
181
182
|
warnings: [],
|
|
182
183
|
};
|
|
183
|
-
const contradictionLlm =
|
|
184
|
+
const contradictionLlm = resolvedLlmConfig === null
|
|
185
|
+
? undefined
|
|
186
|
+
: (resolvedLlmConfig ??
|
|
187
|
+
(() => {
|
|
188
|
+
const runner = resolveImproveProcessRunner(strategy, "consolidate", config);
|
|
189
|
+
return runner ? materializeLlmRunnerConnection(runner) : getDefaultLlmConfig(config);
|
|
190
|
+
})());
|
|
184
191
|
if (!contradictionLlm)
|
|
185
192
|
return result;
|
|
186
193
|
// Collect derived memories grouped by parent.
|
|
@@ -254,7 +261,8 @@ export async function detectAndWriteContradictions(stashDir, config, chat = chat
|
|
|
254
261
|
{ role: "system", content: "Return only valid JSON. No prose." },
|
|
255
262
|
{ role: "user", content: prompt },
|
|
256
263
|
]);
|
|
257
|
-
}, null
|
|
264
|
+
}, null, // Fallback: null means "skip" — gate disabled or LLM call failed.
|
|
265
|
+
{ enabled: strategy?.processes?.consolidate?.contradictionDetection?.enabled ?? false });
|
|
258
266
|
totalPairsChecked++;
|
|
259
267
|
result.pairsChecked++;
|
|
260
268
|
if (!judgeResult)
|