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
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { decodeImproveResult } from "../../core/improve-result.js";
|
|
4
5
|
import { queryImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
|
|
6
|
+
import { decodeTaskHistoryMetadata } from "../../storage/repositories/task-history-repository.js";
|
|
5
7
|
import { summarizeCalibration } from "../improve/calibration.js";
|
|
6
8
|
export function roundRate(value) {
|
|
7
9
|
return Number(value.toFixed(4));
|
|
8
10
|
}
|
|
9
11
|
export function parseTaskMetadata(row) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const metadata = decodeTaskHistoryMetadata(row.metadata_json);
|
|
13
|
+
return {
|
|
14
|
+
...(metadata.durationMs !== undefined ? { durationMs: metadata.durationMs } : {}),
|
|
15
|
+
...(metadata.detail ? { detail: metadata.detail } : {}),
|
|
16
|
+
...(metadata.metadataVersion === 2 && metadata.engine !== undefined ? { engine: metadata.engine } : {}),
|
|
17
|
+
...(metadata.metadataVersion === 1 && metadata.legacyProfile !== undefined
|
|
18
|
+
? { legacyProfile: metadata.legacyProfile }
|
|
19
|
+
: {}),
|
|
20
|
+
};
|
|
16
21
|
}
|
|
17
22
|
function createUnknownImproveMetrics() {
|
|
18
23
|
return {
|
|
@@ -21,7 +26,7 @@ function createUnknownImproveMetrics() {
|
|
|
21
26
|
skipped: 0,
|
|
22
27
|
skipReasons: {},
|
|
23
28
|
plannedRefs: 0,
|
|
24
|
-
|
|
29
|
+
strategyFilteredRefs: 0,
|
|
25
30
|
actions: {
|
|
26
31
|
reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0, guardRejected: 0, skippedByReason: {} },
|
|
27
32
|
distill: {
|
|
@@ -181,12 +186,14 @@ function projectRunMetrics(result) {
|
|
|
181
186
|
const plannedRefs = result.plannedRefs;
|
|
182
187
|
if (Array.isArray(plannedRefs))
|
|
183
188
|
metrics.plannedRefs += plannedRefs.length;
|
|
184
|
-
//
|
|
189
|
+
// strategyFilteredRefs (array of {ref, reason}) — 2026-05-27: pre-filter
|
|
185
190
|
// bucket from `collectEligibleRefs` so the metric reflects work the
|
|
186
191
|
// planner dropped before signal-delta / per-pass dispatch.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
// Health v3 reports strategy metrics only. Historical v1 profile filtering
|
|
193
|
+
// remains legacy data and must not be silently relabelled as a strategy metric.
|
|
194
|
+
const strategyFilteredRefs = result.schemaVersion === 2 ? result.strategyFilteredRefs : undefined;
|
|
195
|
+
if (Array.isArray(strategyFilteredRefs))
|
|
196
|
+
metrics.strategyFilteredRefs += strategyFilteredRefs.length;
|
|
190
197
|
// actions: split reflect / distill by outcome, count others.
|
|
191
198
|
const actions = result.actions;
|
|
192
199
|
if (Array.isArray(actions)) {
|
|
@@ -496,8 +503,8 @@ function finalizeImproveMetrics(metrics) {
|
|
|
496
503
|
*/
|
|
497
504
|
function mergeImproveMetrics(dst, src) {
|
|
498
505
|
dst.plannedRefs += src.plannedRefs;
|
|
499
|
-
//
|
|
500
|
-
// active
|
|
506
|
+
// strategyFilteredRefs is the count of refs the planner drops up-front for the
|
|
507
|
+
// active strategy — recomputed against the (stable) stash every run, so it is a
|
|
501
508
|
// snapshot, NOT a per-run increment. Summing it re-counts the same refs each
|
|
502
509
|
// run (the ~2.4M bug). Set from the most recent run in summarizeImproveRuns.
|
|
503
510
|
dst.actions.reflect.ok += src.actions.reflect.ok;
|
|
@@ -613,22 +620,16 @@ export function summarizeImproveRuns(db, since, until) {
|
|
|
613
620
|
// MOST RECENT run's snapshot (current state) — not a sum across runs.
|
|
614
621
|
let latestStartMs = Number.NEGATIVE_INFINITY;
|
|
615
622
|
let latestMemorySummary;
|
|
616
|
-
let
|
|
623
|
+
let latestStrategyFilteredRefs = 0;
|
|
617
624
|
for (const row of rows) {
|
|
618
|
-
|
|
619
|
-
try {
|
|
620
|
-
result = JSON.parse(row.result_json);
|
|
621
|
-
}
|
|
622
|
-
catch {
|
|
623
|
-
continue;
|
|
624
|
-
}
|
|
625
|
+
const result = decodeImproveResult(row.result_json).envelope;
|
|
625
626
|
const perRow = projectRunMetrics(result);
|
|
626
627
|
mergeImproveMetrics(accum, perRow);
|
|
627
628
|
const startMs = new Date(row.started_at).getTime();
|
|
628
629
|
if (Number.isFinite(startMs) && startMs >= latestStartMs) {
|
|
629
630
|
latestStartMs = startMs;
|
|
630
631
|
latestMemorySummary = perRow.memorySummary;
|
|
631
|
-
|
|
632
|
+
latestStrategyFilteredRefs = perRow.strategyFilteredRefs;
|
|
632
633
|
}
|
|
633
634
|
// Collect per-phase durations directly off the envelope. consolidation's
|
|
634
635
|
// duration lives inside the sub-object; memoryInference and graphExtraction
|
|
@@ -648,7 +649,7 @@ export function summarizeImproveRuns(db, since, until) {
|
|
|
648
649
|
finalizeImproveMetrics(accum);
|
|
649
650
|
if (latestMemorySummary)
|
|
650
651
|
accum.memorySummary = latestMemorySummary;
|
|
651
|
-
accum.
|
|
652
|
+
accum.strategyFilteredRefs = latestStrategyFilteredRefs;
|
|
652
653
|
accum.wallTime.byPhase = {
|
|
653
654
|
consolidation: summarizePhaseDurations(phaseDurations.consolidation),
|
|
654
655
|
memoryInference: summarizePhaseDurations(phaseDurations.memoryInference),
|
|
@@ -680,13 +681,8 @@ export function summarizePhaseDurations(samples) {
|
|
|
680
681
|
* Used by `akm health --detail per-run`.
|
|
681
682
|
*/
|
|
682
683
|
export function projectImproveRunSummary(row, wallTimeMs, taskId) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
result = JSON.parse(row.result_json);
|
|
686
|
-
}
|
|
687
|
-
catch {
|
|
688
|
-
// fall through with empty result so per-stage rollups are zeros
|
|
689
|
-
}
|
|
684
|
+
const decoded = decodeImproveResult(row.result_json);
|
|
685
|
+
const result = decoded.envelope;
|
|
690
686
|
const perRow = projectRunMetrics(result);
|
|
691
687
|
finalizeImproveMetrics(perRow);
|
|
692
688
|
const orphansPurged = toFiniteNumber(result.orphansPurged);
|
|
@@ -699,6 +695,8 @@ export function projectImproveRunSummary(row, wallTimeMs, taskId) {
|
|
|
699
695
|
completedAt: row.completed_at,
|
|
700
696
|
wallTimeMs,
|
|
701
697
|
ok: row.ok === 1,
|
|
698
|
+
strategy: row.strategy,
|
|
699
|
+
legacyProfile: row.legacyProfile,
|
|
702
700
|
scope: {
|
|
703
701
|
mode: row.scope_mode,
|
|
704
702
|
...(row.scope_value ? { value: row.scope_value } : {}),
|
|
@@ -744,10 +742,10 @@ export function buildImproveSkipSummary(events) {
|
|
|
744
742
|
// - Per-occurrence (no `count`): one event per skipped ref → SUM is correct.
|
|
745
743
|
// - Aggregated snapshot (carries `count`): a single per-run event whose count
|
|
746
744
|
// is the number of refs that hit a STABLE, whole-stash condition that run
|
|
747
|
-
// (`no_new_signal`, `
|
|
745
|
+
// (`no_new_signal`, `strategy_filtered_all_passes`). Each run re-counts the
|
|
748
746
|
// same stable set, so summing across the window re-counts it N times (the
|
|
749
747
|
// 2.7M / 3M inflation). For these we keep the MOST RECENT run's count — the
|
|
750
|
-
// current snapshot — matching how memorySummary/
|
|
748
|
+
// current snapshot — matching how memorySummary/strategyFilteredRefs are
|
|
751
749
|
// handled. Events arrive in chronological (offset) order, so the last
|
|
752
750
|
// count-bearing event per reason is the latest run's value.
|
|
753
751
|
const summed = {};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { readEvents } from "../../core/events.js";
|
|
9
9
|
import { LLM_USAGE_EVENT } from "../../llm/usage-persist.js";
|
|
10
|
-
import {
|
|
10
|
+
import { decodeLlmUsageRecord } from "../../llm/usage-telemetry.js";
|
|
11
11
|
/** Stage key used for `llm_usage` events recorded outside any stage scope. */
|
|
12
12
|
const UNATTRIBUTED_STAGE = "unattributed";
|
|
13
13
|
function emptyLlmUsageStageAggregate() {
|
|
@@ -21,7 +21,7 @@ function emptyLlmUsageStageAggregate() {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
function emptyLlmUsageAggregate() {
|
|
24
|
-
return { ...emptyLlmUsageStageAggregate(), byStage: {} };
|
|
24
|
+
return { ...emptyLlmUsageStageAggregate(), byStage: {}, byProcess: {}, byEngine: {} };
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Aggregate `llm_usage` events (#576) into a window total plus a per-stage
|
|
@@ -32,25 +32,25 @@ function emptyLlmUsageAggregate() {
|
|
|
32
32
|
export function summarizeLlmUsage(events) {
|
|
33
33
|
const aggregate = emptyLlmUsageAggregate();
|
|
34
34
|
for (const event of events) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
aggregate.byStage
|
|
35
|
+
const record = decodeLlmUsageRecord(event.metadata);
|
|
36
|
+
if (!record)
|
|
37
|
+
continue;
|
|
38
|
+
const dimensions = [];
|
|
39
|
+
for (const [groups, key] of [
|
|
40
|
+
[aggregate.byStage, record.stage ?? UNATTRIBUTED_STAGE],
|
|
41
|
+
[aggregate.byProcess, record.process ?? UNATTRIBUTED_STAGE],
|
|
42
|
+
[aggregate.byEngine, record.engine ?? UNATTRIBUTED_STAGE],
|
|
43
|
+
]) {
|
|
44
|
+
groups[key] ??= emptyLlmUsageStageAggregate();
|
|
45
|
+
dimensions.push(groups[key]);
|
|
41
46
|
}
|
|
42
|
-
const
|
|
43
|
-
const promptTokens = toFiniteNumber(meta.promptTokens);
|
|
44
|
-
const completionTokens = toFiniteNumber(meta.completionTokens);
|
|
45
|
-
const totalTokens = toFiniteNumber(meta.totalTokens);
|
|
46
|
-
const reasoningTokens = toFiniteNumber(meta.reasoningTokens);
|
|
47
|
-
for (const target of [aggregate, stage]) {
|
|
47
|
+
for (const target of [aggregate, ...dimensions]) {
|
|
48
48
|
target.calls += 1;
|
|
49
|
-
target.totalDurationMs += durationMs;
|
|
50
|
-
target.promptTokens += promptTokens;
|
|
51
|
-
target.completionTokens += completionTokens;
|
|
52
|
-
target.totalTokens += totalTokens;
|
|
53
|
-
target.reasoningTokens += reasoningTokens;
|
|
49
|
+
target.totalDurationMs += record.durationMs;
|
|
50
|
+
target.promptTokens += record.promptTokens ?? 0;
|
|
51
|
+
target.completionTokens += record.completionTokens ?? 0;
|
|
52
|
+
target.totalTokens += record.totalTokens ?? 0;
|
|
53
|
+
target.reasoningTokens += record.reasoningTokens ?? 0;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
return aggregate;
|
|
@@ -28,6 +28,8 @@ export function renderRunsDetailMd(runs) {
|
|
|
28
28
|
const headers = [
|
|
29
29
|
"ts",
|
|
30
30
|
"ok",
|
|
31
|
+
"strategy",
|
|
32
|
+
"legacy_profile",
|
|
31
33
|
"actions",
|
|
32
34
|
"refl_ok/fail/cd/skip",
|
|
33
35
|
"distill_q/llm-fail/qrej/cfg/skip",
|
|
@@ -54,6 +56,8 @@ export function renderRunsDetailMd(runs) {
|
|
|
54
56
|
return [
|
|
55
57
|
r.startedAt,
|
|
56
58
|
String(r.ok),
|
|
59
|
+
r.strategy ?? "",
|
|
60
|
+
r.legacyProfile ?? "",
|
|
57
61
|
String(totalActions),
|
|
58
62
|
`${r.actions.reflect.ok}/${r.actions.reflect.failed}/${r.actions.reflect.cooldown}/${r.actions.reflect.skipped}`,
|
|
59
63
|
`${r.actions.distill.queued}/${r.actions.distill.llmFailed}/${r.actions.distill.qualityRejected}/${r.actions.distill.configDisabled}/${r.actions.distill.skipped}`,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* minting rollup, and the WS-5 per-run degradation metrics.
|
|
8
8
|
*/
|
|
9
9
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
10
|
+
import { decodeImproveResult } from "../../core/improve-result.js";
|
|
10
11
|
import { queryImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
|
|
11
12
|
import { listProposalGateDecisions, listStateProposals } from "../../storage/repositories/proposals-repository.js";
|
|
12
13
|
import { gateDecisionsToSamples, summarizeCalibration } from "../improve/calibration.js";
|
|
@@ -221,7 +222,7 @@ export function computeDegradationMetrics(db, since, until) {
|
|
|
221
222
|
let totalProcessed = 0;
|
|
222
223
|
for (const row of runs) {
|
|
223
224
|
try {
|
|
224
|
-
const result =
|
|
225
|
+
const result = decodeImproveResult(row.result_json).envelope;
|
|
225
226
|
const cons = result.consolidation;
|
|
226
227
|
if (cons) {
|
|
227
228
|
totalContradicted += toFiniteNumber(cons.contradicted);
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { CURRENT_CONFIG_VERSION
|
|
21
|
+
import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
|
|
22
|
+
import { compareConfigVersion } from "../../core/config/config-version.js";
|
|
22
23
|
const GROUP_OTHER_BITS = 0o077;
|
|
23
24
|
const OFFENDER_EVIDENCE_CAP = 50;
|
|
24
25
|
function modeOctal(mode) {
|
|
@@ -182,10 +183,10 @@ export function collectEgressAdvisory(config) {
|
|
|
182
183
|
continue;
|
|
183
184
|
endpoints.push(`source ${source.name ?? "(unnamed)"} (${source.type ?? "?"}): ${source.url}`);
|
|
184
185
|
}
|
|
185
|
-
for (const [name,
|
|
186
|
-
if (!
|
|
186
|
+
for (const [name, engine] of Object.entries(config.engines ?? {})) {
|
|
187
|
+
if (engine?.kind !== "llm" || !engine.endpoint)
|
|
187
188
|
continue;
|
|
188
|
-
endpoints.push(`llm ${name}: ${
|
|
189
|
+
endpoints.push(`llm ${name}: ${engine.endpoint}`);
|
|
189
190
|
}
|
|
190
191
|
if (config.embedding?.endpoint)
|
|
191
192
|
endpoints.push(`embedding: ${config.embedding.endpoint}`);
|
package/dist/commands/health.js
CHANGED
|
@@ -33,9 +33,9 @@ const PROMPT_OVERHEAD_TOKENS = 2_000;
|
|
|
33
33
|
* chunk content.
|
|
34
34
|
* - When the HTTP path is used (an LLM profile is selected), only the akm
|
|
35
35
|
* system prompt and user prompt are sent, so the budget can be set to the
|
|
36
|
-
* model's actual context length via
|
|
36
|
+
* model's actual context length via the selected LLM engine's contextLength.
|
|
37
37
|
*
|
|
38
|
-
* Set
|
|
38
|
+
* Set the selected LLM engine's contextLength in your config file to the
|
|
39
39
|
* model's actual context window to allow larger chunks on the HTTP path.
|
|
40
40
|
*/
|
|
41
41
|
export const DEFAULT_CONTEXT_LENGTH_TOKENS = 4_096;
|
|
@@ -29,12 +29,13 @@ import { openStateDatabase, withStateDb } from "../../core/state-db.js";
|
|
|
29
29
|
import { warn } from "../../core/warn.js";
|
|
30
30
|
import { commitWriteTargetBoundary, deleteAssetFromSource, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
|
|
31
31
|
import { closeDatabase, findEntryIdByRef, getAllEntries, getEntryById, getNeighborsByEntryId, openExistingDatabase, } from "../../indexer/db/db.js";
|
|
32
|
-
import {
|
|
32
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../integrations/agent/runner.js";
|
|
33
33
|
import { chatCompletion } from "../../llm/client.js";
|
|
34
34
|
import { cosineSimilarity, embedBatch, resolveEmbeddingModelId } from "../../llm/embedder.js";
|
|
35
|
-
import {
|
|
35
|
+
import { tryLlmFeature } from "../../llm/feature-gate.js";
|
|
36
36
|
import { getConsolidationJudgedMap, upsertConsolidationJudged, } from "../../storage/repositories/consolidation-repository.js";
|
|
37
37
|
import { getBodyEmbeddings, upsertBodyEmbeddings } from "../../storage/repositories/embeddings-repository.js";
|
|
38
|
+
import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
|
|
38
39
|
// Chunk sizing + per-chunk prompt assembly live in ./consolidate/chunking.
|
|
39
40
|
// Imported for internal use by the orchestrator and re-exported for importers.
|
|
40
41
|
import { buildChunkPrompt, computeSafeChunkSize, DEFAULT_CONTEXT_LENGTH_TOKENS } from "./consolidate/chunking.js";
|
|
@@ -493,13 +494,13 @@ function archiveMemory(filePath, stashDir, ref, reason, opIndex, supersededBy, w
|
|
|
493
494
|
*
|
|
494
495
|
* Priority order (mirrors extract / reflect / distill — see
|
|
495
496
|
* `src/commands/extract.ts:421-438` and the canonical
|
|
496
|
-
* `
|
|
497
|
+
* `resolveImproveProcessRunner` pattern):
|
|
497
498
|
*
|
|
498
|
-
* 1. `
|
|
499
|
-
* via {@link
|
|
499
|
+
* 1. `improve.strategies.<name>.processes.consolidate.engine`
|
|
500
|
+
* via {@link resolveImproveProcessRunner}. Lets the user pin
|
|
500
501
|
* a dedicated model (e.g. `ministral-3b`) for consolidation instead of
|
|
501
|
-
* whatever `defaults.
|
|
502
|
-
* 2. `getDefaultLlmConfig(config)` — the baseline default LLM
|
|
502
|
+
* whatever `defaults.llmEngine` happens to be.
|
|
503
|
+
* 2. `getDefaultLlmConfig(config)` — the baseline default LLM engine.
|
|
503
504
|
*
|
|
504
505
|
* Regression guard (2026-05-26): before this resolver, `akmConsolidate`
|
|
505
506
|
* called `getDefaultLlmConfig` directly and silently ignored a configured
|
|
@@ -509,13 +510,9 @@ function archiveMemory(filePath, stashDir, ref, reason, opIndex, supersededBy, w
|
|
|
509
510
|
* `/tmp/akm-health-investigations/consolidation-no-op.md`.
|
|
510
511
|
*/
|
|
511
512
|
function resolveConsolidateLlmConfig(config, activeProfile) {
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
return runnerSpec.connection;
|
|
516
|
-
}
|
|
517
|
-
// Non-LLM runner modes (agent/sdk) don't apply to consolidate's HTTP path;
|
|
518
|
-
// fall back to the default LLM profile rather than disabling the pass.
|
|
513
|
+
const runnerSpec = resolveImproveProcessRunner(activeProfile, "consolidate", config);
|
|
514
|
+
if (runnerSpec)
|
|
515
|
+
return materializeLlmRunnerConnection(runnerSpec);
|
|
519
516
|
return getDefaultLlmConfig(config);
|
|
520
517
|
}
|
|
521
518
|
// ── Judged-state cache (#581) ────────────────────────────────────────────────
|
|
@@ -578,8 +575,9 @@ export async function akmConsolidate(opts = {}) {
|
|
|
578
575
|
// standalone `akm consolidate` gets a self-contained token.
|
|
579
576
|
const sourceRun = opts.sourceRun ?? `consolidate-${startMs}`;
|
|
580
577
|
const config = opts.config ?? loadConfig();
|
|
578
|
+
opts = { ...opts, improveProfile: opts.improveProfile ?? resolveImproveStrategy(undefined, config).config };
|
|
581
579
|
const stashDir = opts.stashDir ?? resolveStashDir();
|
|
582
|
-
if (!
|
|
580
|
+
if (!resolveProcessEnabled("consolidate", opts.improveProfile ?? resolveImproveStrategy(undefined, config).config)) {
|
|
583
581
|
return makeConsolidateResult({
|
|
584
582
|
dryRun: opts.dryRun ?? false,
|
|
585
583
|
target: opts.target ?? stashDir,
|
|
@@ -868,9 +866,11 @@ async function planConsolidation(opts, config, stashDir, startMs, memories, warn
|
|
|
868
866
|
// CLI. The agent CLI is for interactive agent sessions (reflect, propose);
|
|
869
867
|
// structured JSON generation works better and faster via HTTP.
|
|
870
868
|
//
|
|
871
|
-
//
|
|
872
|
-
//
|
|
873
|
-
const llmConfig =
|
|
869
|
+
// Improve supplies a frozen connection; standalone consolidate resolves its
|
|
870
|
+
// selected strategy/default engine here.
|
|
871
|
+
const llmConfig = Object.hasOwn(opts, "llmConfig")
|
|
872
|
+
? (opts.llmConfig ?? undefined)
|
|
873
|
+
: resolveConsolidateLlmConfig(config, opts.improveProfile);
|
|
874
874
|
const isHttpPath = !!llmConfig;
|
|
875
875
|
// Chunk sizing: derive a safe chunk size from the configured model context
|
|
876
876
|
// window so that the full prompt (system prompt + chunk user prompt) never
|
|
@@ -1087,7 +1087,7 @@ async function planConsolidation(opts, config, stashDir, startMs, memories, warn
|
|
|
1087
1087
|
catch (e) {
|
|
1088
1088
|
return { ok: false, error: String(e) };
|
|
1089
1089
|
}
|
|
1090
|
-
}, { ok: false, error: fallbackError });
|
|
1090
|
+
}, { ok: false, error: fallbackError }, { enabled: true });
|
|
1091
1091
|
let raw = await callChunkLlm(`chunk ${chunkIdx + 1} failed`);
|
|
1092
1092
|
if (!raw.ok) {
|
|
1093
1093
|
// Single retry with 2s backoff before recording chunk as lost.
|
|
@@ -1230,7 +1230,7 @@ async function planConsolidation(opts, config, stashDir, startMs, memories, warn
|
|
|
1230
1230
|
* the op-handlers' `pushSkipReason`. Behavior-identical to the former inlined
|
|
1231
1231
|
* write block. Never invoked on the dry-run or aborted-confirm paths.
|
|
1232
1232
|
*/
|
|
1233
|
-
async function applyConsolidationPlan(config, stashDir, sourceRun, memories, warnings, allOps, accounting, dedupCollapsed, activeProfile) {
|
|
1233
|
+
async function applyConsolidationPlan(config, stashDir, sourceRun, memories, warnings, allOps, accounting, dedupCollapsed, activeProfile, llmConfig) {
|
|
1234
1234
|
// -- Phase B + writes -------------------------------------------------------
|
|
1235
1235
|
const target = resolveWriteTarget(config);
|
|
1236
1236
|
const timestamp = timestampForFilename();
|
|
@@ -1258,6 +1258,7 @@ async function applyConsolidationPlan(config, stashDir, sourceRun, memories, war
|
|
|
1258
1258
|
const opCtx = {
|
|
1259
1259
|
config,
|
|
1260
1260
|
improveProfile: activeProfile,
|
|
1261
|
+
llmConfig: llmConfig ?? null,
|
|
1261
1262
|
stashDir,
|
|
1262
1263
|
sourceRun,
|
|
1263
1264
|
target,
|
|
@@ -1380,7 +1381,9 @@ async function akmConsolidateInner(opts, config, stashDir, startMs, sourceRun, w
|
|
|
1380
1381
|
}
|
|
1381
1382
|
}
|
|
1382
1383
|
// -- Pass 3: execute the plan against the filesystem ------------------------
|
|
1383
|
-
const { merged, deleted, contradicted, mergeFloorViolations, mergedSecondaries, promoted } = await applyConsolidationPlan(config, stashDir, sourceRun, memories, warnings, allOps, accounting, dedupCollapsed, opts.improveProfile)
|
|
1384
|
+
const { merged, deleted, contradicted, mergeFloorViolations, mergedSecondaries, promoted } = await applyConsolidationPlan(config, stashDir, sourceRun, memories, warnings, allOps, accounting, dedupCollapsed, opts.improveProfile, Object.hasOwn(opts, "llmConfig")
|
|
1385
|
+
? (opts.llmConfig ?? null)
|
|
1386
|
+
: (resolveConsolidateLlmConfig(config, opts.improveProfile) ?? null));
|
|
1384
1387
|
const runDurationMs = Date.now() - startMs;
|
|
1385
1388
|
const budgetFraction = opts.runBudgetMs !== undefined && opts.runBudgetMs > 0 ? runDurationMs / opts.runBudgetMs : undefined;
|
|
1386
1389
|
return {
|
|
@@ -1502,7 +1505,7 @@ export async function handleMergeOp(op, opIndex, ctx) {
|
|
|
1502
1505
|
emitMergeFailureSkips("merge_read_failed");
|
|
1503
1506
|
return;
|
|
1504
1507
|
}
|
|
1505
|
-
const mergeResult = await generateMergedContent(config, op.primary, primaryBody, op.secondaries, memoryByRef, ctx.improveProfile);
|
|
1508
|
+
const mergeResult = await generateMergedContent(config, op.primary, primaryBody, op.secondaries, memoryByRef, ctx.improveProfile, ctx.llmConfig);
|
|
1506
1509
|
if ("error" in mergeResult) {
|
|
1507
1510
|
warnings.push(`Merge: ${mergeResult.error} for ${mergeResult.detail}.`);
|
|
1508
1511
|
emitMergeFailureSkips(mergeResult.error);
|
|
@@ -2147,7 +2150,7 @@ function loadMemoriesForSource(source, stashDir, warnings) {
|
|
|
2147
2150
|
}
|
|
2148
2151
|
return memories;
|
|
2149
2152
|
}
|
|
2150
|
-
async function generateMergedContent(config, primaryRef, primaryBody, secondaryRefs, memoryByRef, activeProfile) {
|
|
2153
|
+
async function generateMergedContent(config, primaryRef, primaryBody, secondaryRefs, memoryByRef, activeProfile, resolvedLlmConfig) {
|
|
2151
2154
|
// Only handle single-secondary merges per design (one call per merge op)
|
|
2152
2155
|
const secRef = secondaryRefs[0];
|
|
2153
2156
|
const secEntry = memoryByRef.get(secRef);
|
|
@@ -2191,7 +2194,7 @@ async function generateMergedContent(config, primaryRef, primaryBody, secondaryR
|
|
|
2191
2194
|
.join("\n");
|
|
2192
2195
|
// Use the same per-process profile resolution as the chunk-plan call above
|
|
2193
2196
|
// so the merge generation step doesn't silently revert to the default LLM.
|
|
2194
|
-
const llmConfig = resolveConsolidateLlmConfig(config, activeProfile);
|
|
2197
|
+
const llmConfig = resolvedLlmConfig === null ? undefined : (resolvedLlmConfig ?? resolveConsolidateLlmConfig(config, activeProfile));
|
|
2195
2198
|
const result = await tryLlmFeature("memory_consolidation", config, async () => {
|
|
2196
2199
|
if (!llmConfig)
|
|
2197
2200
|
return { ok: false, error: "No LLM configured for consolidation" };
|
|
@@ -2204,7 +2207,7 @@ async function generateMergedContent(config, primaryRef, primaryBody, secondaryR
|
|
|
2204
2207
|
catch (e) {
|
|
2205
2208
|
return { ok: false, error: String(e) };
|
|
2206
2209
|
}
|
|
2207
|
-
}, { ok: false, error: `merge content generation failed for ${primaryRef}` });
|
|
2210
|
+
}, { ok: false, error: `merge content generation failed for ${primaryRef}` }, { enabled: true });
|
|
2208
2211
|
if (!result.ok) {
|
|
2209
2212
|
return {
|
|
2210
2213
|
error: "merge_transport_failed",
|
|
@@ -18,11 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
21
|
-
import { getDefaultLlmConfig } from "../../../core/config/config.js";
|
|
22
|
-
import { ConfigError } from "../../../core/errors.js";
|
|
23
21
|
import { appendEvent } from "../../../core/events.js";
|
|
24
22
|
import { parseEmbeddedJsonResponse } from "../../../llm/client.js";
|
|
25
|
-
import { isLlmFeatureEnabled } from "../../../llm/feature-gate.js";
|
|
26
23
|
import { createProposal, isProposalSkipped } from "../../proposal/repository.js";
|
|
27
24
|
import { assessMemoryKnowledgePromotionCandidate } from "../distill-promotion-policy.js";
|
|
28
25
|
import { persistOutputEncodingSalience, runLessonQualityJudge, writeQualityRejection } from "./quality-gate.js";
|
|
@@ -62,7 +59,7 @@ export async function promoteMemoryToKnowledge(ctx) {
|
|
|
62
59
|
}
|
|
63
60
|
})()
|
|
64
61
|
: null;
|
|
65
|
-
if (existingKnowledgeContent &&
|
|
62
|
+
if (existingKnowledgeContent && ctx.llmConfig) {
|
|
66
63
|
// Existing content found: call LLM for contradiction-resolution merge.
|
|
67
64
|
const mergePrompt = [
|
|
68
65
|
"You are merging two versions of a knowledge document.",
|
|
@@ -81,11 +78,7 @@ export async function promoteMemoryToKnowledge(ctx) {
|
|
|
81
78
|
"```",
|
|
82
79
|
].join("\n");
|
|
83
80
|
try {
|
|
84
|
-
const
|
|
85
|
-
if (!mergeLlm) {
|
|
86
|
-
throw new ConfigError("LLM is not configured for distillation merge.", "LLM_NOT_CONFIGURED");
|
|
87
|
-
}
|
|
88
|
-
const mergeResponse = await chat(mergeLlm, [
|
|
81
|
+
const mergeResponse = await chat(ctx.llmConfig, [
|
|
89
82
|
{ role: "system", content: "Return only valid JSON. No prose." },
|
|
90
83
|
{ role: "user", content: mergePrompt },
|
|
91
84
|
]);
|
|
@@ -122,7 +115,7 @@ export async function promoteMemoryToKnowledge(ctx) {
|
|
|
122
115
|
// The reviewer will see both versions in the proposal diff.
|
|
123
116
|
}
|
|
124
117
|
}
|
|
125
|
-
else if (existingKnowledgeContent
|
|
118
|
+
else if (existingKnowledgeContent) {
|
|
126
119
|
// No LLM configured: include existing content as context in the proposal
|
|
127
120
|
// so the reviewer can do the contradiction resolution manually.
|
|
128
121
|
resolvedPromotionContent = [
|
|
@@ -141,10 +134,10 @@ export async function promoteMemoryToKnowledge(ctx) {
|
|
|
141
134
|
// D-5 / #388: Three-band system — review_needed band queues to proposal
|
|
142
135
|
// queue with review_needed outcome rather than auto-rejecting.
|
|
143
136
|
let knowledgeJudgeConfidence;
|
|
144
|
-
if (
|
|
137
|
+
if (ctx.strategy?.processes?.distill?.qualityGate?.enabled ?? true) {
|
|
145
138
|
// D-4 / #390: retrieve top-3 similar lessons for dedup check in judge.
|
|
146
139
|
const similarLessons = await fetchSimilarLessonsFn(resolvedPromotionContent.slice(0, 500), 3);
|
|
147
|
-
const judgeResult = await runLessonQualityJudge(config, resolvedPromotionContent, assetContent ?? "", chat, similarLessons.length > 0 ? similarLessons : undefined);
|
|
140
|
+
const judgeResult = await runLessonQualityJudge(config, resolvedPromotionContent, assetContent ?? "", chat, similarLessons.length > 0 ? similarLessons : undefined, ctx.llmConfig);
|
|
148
141
|
if (!judgeResult.pass) {
|
|
149
142
|
if (judgeResult.reviewNeeded) {
|
|
150
143
|
// Uncertainty band (2.5–3.5): queue as review_needed instead of rejecting.
|
|
@@ -102,10 +102,8 @@ export function buildJudgePrompt(lessonContent, sourceContent, similarLessons) {
|
|
|
102
102
|
* Run the LLM-as-judge quality gate on a proposal's content.
|
|
103
103
|
*
|
|
104
104
|
* Exported so reflect.ts can apply the same gate to reflect proposals (R-5 / #374).
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* `profiles.improve.default.processes.distill.qualityGate.enabled` (and the
|
|
108
|
-
* corresponding `.reflect.qualityGate.enabled` for proposals).
|
|
105
|
+
* The selected strategy's distill/reflect quality-gate setting is resolved by
|
|
106
|
+
* the caller before this function runs.
|
|
109
107
|
*
|
|
110
108
|
* Fail-CLOSED (07 P0-2): returns `pass: false` (score -1) on timeout, parse
|
|
111
109
|
* failure, or missing LLM. Minted content that cannot be judged is rejected,
|
|
@@ -114,12 +112,12 @@ export function buildJudgePrompt(lessonContent, sourceContent, similarLessons) {
|
|
|
114
112
|
*/
|
|
115
113
|
export async function runLessonQualityJudge(config, lessonContent, sourceContent, chat,
|
|
116
114
|
/** D-4 / #390: top-3 similar existing lessons for dedup check. */
|
|
117
|
-
similarLessons) {
|
|
118
|
-
const llmConfig = getDefaultLlmConfig(config);
|
|
115
|
+
similarLessons, llmConfigOverride) {
|
|
116
|
+
const llmConfig = llmConfigOverride ?? getDefaultLlmConfig(config);
|
|
119
117
|
if (!llmConfig) {
|
|
120
118
|
return { pass: false, score: -1, reason: "no LLM configured — cannot judge, failing closed" };
|
|
121
119
|
}
|
|
122
|
-
const judgeLlmConfig = llmConfig
|
|
120
|
+
const judgeLlmConfig = llmConfig;
|
|
123
121
|
const JUDGE_TIMEOUT_MS = 8_000;
|
|
124
122
|
try {
|
|
125
123
|
const raw = await Promise.race([
|
|
@@ -69,8 +69,9 @@ import { withStateDb } from "../../core/state-db.js";
|
|
|
69
69
|
import { warnVerbose } from "../../core/warn.js";
|
|
70
70
|
import { closeDatabase, getAllEntries, openIndexDatabase } from "../../indexer/db/db.js";
|
|
71
71
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
72
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../integrations/agent/runner.js";
|
|
72
73
|
import { chatCompletion, parseEmbeddedJsonResponse } from "../../llm/client.js";
|
|
73
|
-
import {
|
|
74
|
+
import { tryLlmFeature } from "../../llm/feature-gate.js";
|
|
74
75
|
import { createProposal, isProposalSkipped, listProposals, } from "../proposal/repository.js";
|
|
75
76
|
import { stripFrontmatterBody as stripBodyForFidelity } from "./dedup.js";
|
|
76
77
|
import { autoRepairLessonFrontmatter, autoSwapDescriptionWhenToUse, collectLessonQualityFindings, repairLessonDescriptionTruncation, } from "./distill/content-repair.js";
|
|
@@ -79,6 +80,7 @@ import { fetchTopSimilarLessons, persistOutputEncodingSalience, runLessonQuality
|
|
|
79
80
|
import { buildClsContext, checkDistillFidelity } from "./distill-guards.js";
|
|
80
81
|
import { deriveKnowledgeRef } from "./distill-promotion-policy.js";
|
|
81
82
|
import { buildRefVocabulary, scoreEncodingSalience } from "./encoding-salience.js";
|
|
83
|
+
import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
|
|
82
84
|
import { computeSalience, upsertAssetSalience } from "./salience.js";
|
|
83
85
|
// Re-exported for `reflect.ts`, which applies the same LLM-as-judge gate to
|
|
84
86
|
// reflect proposals (R-5 / #374).
|
|
@@ -474,8 +476,15 @@ export async function akmDistill(options) {
|
|
|
474
476
|
};
|
|
475
477
|
}
|
|
476
478
|
const config = options.config ?? loadConfig();
|
|
479
|
+
options = { ...options, improveProfile: options.improveProfile ?? resolveImproveStrategy(undefined, config).config };
|
|
477
480
|
const stash = options.stashDir ?? resolveStashDir();
|
|
478
481
|
const chat = options.chat ?? chatCompletion;
|
|
482
|
+
const distillLlm = Object.hasOwn(options, "llmConfig")
|
|
483
|
+
? (options.llmConfig ?? undefined)
|
|
484
|
+
: (() => {
|
|
485
|
+
const runner = resolveImproveProcessRunner(options.improveProfile, "distill", config);
|
|
486
|
+
return runner ? materializeLlmRunnerConnection(runner) : getDefaultLlmConfig(config);
|
|
487
|
+
})();
|
|
479
488
|
const lookup = options.lookupFn ?? defaultLookup;
|
|
480
489
|
const readEventsImpl = options.readEventsFn ?? readEvents;
|
|
481
490
|
// R1 opt-out must flow into every computeSalience call this command makes so
|
|
@@ -602,6 +611,8 @@ export async function akmDistill(options) {
|
|
|
602
611
|
assetContent,
|
|
603
612
|
filteredEvents,
|
|
604
613
|
config,
|
|
614
|
+
strategy: options.improveProfile,
|
|
615
|
+
llmConfig: distillLlm,
|
|
605
616
|
chat,
|
|
606
617
|
stash,
|
|
607
618
|
lookup,
|
|
@@ -684,12 +695,11 @@ export async function akmDistill(options) {
|
|
|
684
695
|
const distillSchema = effectiveProposalKind === "knowledge" ? DISTILL_KNOWLEDGE_JSON_SCHEMA : DISTILL_LESSON_JSON_SCHEMA;
|
|
685
696
|
let fallbackReason;
|
|
686
697
|
const raw = await tryLlmFeature("distill", config, async () => {
|
|
687
|
-
const distillLlm = getDefaultLlmConfig(config);
|
|
688
698
|
if (!distillLlm) {
|
|
689
699
|
// No LLM connection configured — treat as gate-disabled. Throwing
|
|
690
700
|
// here lets `tryLlmFeature` route us through the "error" fallback,
|
|
691
701
|
// which is the same graceful skipped path.
|
|
692
|
-
throw new ConfigError("No LLM
|
|
702
|
+
throw new ConfigError("No LLM engine configured. Set defaults.llmEngine or improve.strategies.<name>.processes.distill.engine.", "LLM_NOT_CONFIGURED");
|
|
693
703
|
}
|
|
694
704
|
// Production path: pass the JSON schema so providers that honour
|
|
695
705
|
// `response_format: json_schema` enforce shape upstream. Providers that
|
|
@@ -701,6 +711,7 @@ export async function akmDistill(options) {
|
|
|
701
711
|
// functions (which return markdown strings) continue to work.
|
|
702
712
|
return chat(distillLlm, messages);
|
|
703
713
|
}, null, {
|
|
714
|
+
enabled: resolveProcessEnabled("distill", options.improveProfile ?? resolveImproveStrategy(undefined, config).config),
|
|
704
715
|
onFallback: (evt) => {
|
|
705
716
|
fallbackReason = evt.reason;
|
|
706
717
|
// Log the fallback reason; the caller (raw === null path) handles
|
|
@@ -822,10 +833,10 @@ export async function akmDistill(options) {
|
|
|
822
833
|
// D-5 / #388: Three-band system — review_needed band queues a proposal
|
|
823
834
|
// with review_needed outcome rather than auto-rejecting.
|
|
824
835
|
let lessonJudgeConfidence;
|
|
825
|
-
if (
|
|
836
|
+
if (options.improveProfile?.processes?.distill?.qualityGate?.enabled ?? true) {
|
|
826
837
|
// D-4 / #390: retrieve top-3 similar lessons for dedup check in judge.
|
|
827
838
|
const similarLessons = await fetchSimilarLessonsFn(content.slice(0, 500), 3);
|
|
828
|
-
const judgeResult = await runLessonQualityJudge(config, content, assetContent ?? "", chat, similarLessons.length > 0 ? similarLessons : undefined);
|
|
839
|
+
const judgeResult = await runLessonQualityJudge(config, content, assetContent ?? "", chat, similarLessons.length > 0 ? similarLessons : undefined, distillLlm);
|
|
829
840
|
if (!judgeResult.pass) {
|
|
830
841
|
if (judgeResult.reviewNeeded) {
|
|
831
842
|
return writeQualityRejection(stash, inputRef, effectiveLessonRef, content, judgeResult.score, judgeResult.reason, {
|