akm-cli 0.9.11 → 0.9.13
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 +227 -0
- package/STABILITY.md +6 -1
- package/dist/assets/hints/cli-hints-full.md +1 -1
- package/dist/assets/improve-strategies/consolidate.json +1 -1
- package/dist/assets/improve-strategies/default.json +1 -1
- package/dist/assets/improve-strategies/thorough.json +1 -2
- package/dist/assets/workflows/workflow-template.md +4 -0
- package/dist/cli/shared.js +16 -4
- package/dist/cli.js +15 -13
- package/dist/commands/agent/agent-dispatch.js +8 -0
- package/dist/commands/command/execution-source-loader.js +25 -22
- package/dist/commands/command/portable-template.js +4 -26
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env-binding.js +10 -3
- package/dist/commands/env/env-cli.js +7 -0
- package/dist/commands/env/secret-cli.js +15 -4
- package/dist/commands/health/checks.js +186 -71
- package/dist/commands/health.js +16 -4
- package/dist/commands/improve/distill/quality-gate.js +2 -2
- package/dist/commands/improve/distill.js +28 -12
- package/dist/commands/improve/execution.js +1 -2
- package/dist/commands/improve/extract.js +82 -56
- package/dist/commands/improve/improve-strategies.js +26 -8
- package/dist/commands/improve/improve.js +14 -0
- package/dist/commands/improve/preparation.js +9 -6
- package/dist/commands/improve/reflect.js +61 -77
- package/dist/commands/lint/base-linter.js +10 -0
- package/dist/commands/lint/index.js +3 -1
- package/dist/commands/migrate-cli.js +6 -4
- package/dist/commands/proposal/drain-policies.js +22 -2
- package/dist/commands/proposal/drain.js +48 -6
- package/dist/commands/proposal/proposal-cli.js +1 -0
- package/dist/commands/proposal/repository.js +4 -4
- package/dist/commands/proposal/validators/proposal-quality-validators.js +23 -2
- package/dist/commands/proposal/validators/proposals.js +10 -19
- package/dist/commands/read/show.js +42 -31
- package/dist/commands/registry-cli.js +4 -2
- package/dist/commands/sources/init.js +4 -8
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-clone.js +5 -7
- package/dist/commands/sources/sources-cli.js +3 -5
- package/dist/commands/tasks/tasks-cli.js +4 -12
- package/dist/commands/tasks/tasks.js +38 -35
- package/dist/commands/workflow-cli.js +17 -15
- package/dist/core/activation-policy.js +31 -3
- package/dist/core/adapter/execution-source.js +39 -11
- package/dist/core/asset/stash-meta.js +7 -41
- package/dist/core/common.js +8 -17
- package/dist/core/config/config-schema.js +3 -23
- package/dist/core/config/config-walker.js +56 -6
- package/dist/core/config/config.js +42 -17
- package/dist/core/config/legacy-source-shape-shim.js +79 -0
- package/dist/core/config/schema/embedding.js +2 -2
- package/dist/core/config/schema/engines.js +2 -2
- package/dist/core/config/schema/index-config.js +19 -21
- package/dist/core/config/schema/primitives.js +27 -10
- package/dist/core/config/schema/sources-bundles.js +1 -6
- package/dist/core/errors.js +4 -3
- package/dist/core/improve-types.js +17 -0
- package/dist/core/json-schema.js +1 -11
- package/dist/core/maintenance-barrier.js +17 -2
- package/dist/core/paths.js +12 -15
- package/dist/core/state/migrations.js +28 -0
- package/dist/core/state-db.js +28 -1
- package/dist/core/write-source.js +6 -6
- package/dist/indexer/bundle-identity-guard.js +3 -0
- package/dist/indexer/ensure-index.js +5 -0
- package/dist/indexer/indexer.js +11 -3
- package/dist/indexer/lookup/adapter-concept-owner.js +14 -3
- package/dist/indexer/passes/metadata.js +16 -5
- package/dist/indexer/search/search-fields.js +1 -30
- package/dist/integrations/agent/engine-resolution.js +15 -1
- package/dist/integrations/agent/model-map.js +16 -10
- package/dist/integrations/agent/prompts.js +13 -6
- package/dist/integrations/lockfile.js +22 -7
- package/dist/llm/client.js +28 -8
- package/dist/llm/embedders/remote.js +3 -2
- package/dist/llm/index-passes.js +3 -2
- package/dist/output/shapes/passthrough.js +9 -3
- package/dist/output/shapes.js +50 -3
- package/dist/output/text/proposal-format.js +5 -0
- package/dist/output/text/workflow-format.js +8 -1
- package/dist/scripts/akm-migrate-node.js +1737 -1392
- package/dist/scripts/akm-migrate.js +1736 -1391
- package/dist/setup/setup.js +14 -21
- package/dist/sources/include.js +150 -20
- package/dist/sources/providers/git-install.js +14 -12
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/snapshot-fetchers/website-ingest.js +54 -16
- package/dist/sources/website-url.js +12 -4
- package/dist/storage/engines/sqlite-migrations.js +40 -10
- package/dist/storage/like-pattern.js +7 -0
- package/dist/storage/repositories/extract-sessions-repository.js +23 -0
- package/dist/storage/repositories/index-connection.js +27 -10
- package/dist/storage/repositories/index-entry-schema.js +19 -2
- package/dist/storage/repositories/index-schema.js +30 -9
- package/dist/storage/repositories/proposals-repository.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +14 -7
- package/dist/storage/repositories/workflow-runs-repository.js +133 -11
- package/dist/storage/sqlite-read-snapshot.js +11 -9
- package/dist/tasks/backends/cron.js +34 -5
- package/dist/tasks/backends/launchd.js +23 -26
- package/dist/tasks/backends/schtasks.js +50 -3
- package/dist/tasks/frozen-script.js +2 -0
- package/dist/tasks/prepare/prepare.js +2 -7
- package/dist/tasks/prepare/script-capture.js +38 -6
- package/dist/tasks/schedule.js +154 -13
- package/dist/tasks/source/task-source-v3-frozen.js +0 -1
- package/dist/tasks/source/task-source-v4.js +0 -1
- package/dist/workflows/exec/child-workflow.js +2 -3
- package/dist/workflows/exec/exec-unit.js +3 -4
- package/dist/workflows/exec/run-workflow.js +20 -11
- package/dist/workflows/exec/step-work.js +76 -56
- package/dist/workflows/freeze/resolve-steps.js +19 -11
- package/dist/workflows/freeze/source-freeze.js +7 -0
- package/dist/workflows/freeze/targets/child-workflow.js +12 -18
- package/dist/workflows/freeze/targets/command.js +14 -2
- package/dist/workflows/ir/environment-v4.js +4 -2
- package/dist/workflows/ir/freeze-v4.js +2 -5
- package/dist/workflows/ir/plan-hash.js +0 -3
- package/dist/workflows/ir/schema-v4.js +14 -9
- package/dist/workflows/ir/schema.js +1 -3
- package/dist/workflows/parser.js +1 -1
- package/dist/workflows/resource-limits.js +35 -48
- package/dist/workflows/runtime/plan-classifier.js +89 -41
- package/dist/workflows/runtime/run-outputs.js +1 -21
- package/dist/workflows/runtime/runs.js +104 -154
- package/dist/workflows/source-files.js +28 -54
- package/dist/workflows/source-ir/program.js +2 -2
- package/dist/workflows/source-ir/semantics.js +5 -23
- package/docs/migration/v0.9.1-to-v0.9.2.md +20 -0
- package/docs/reference/cli.md +92 -17
- package/package.json +1 -1
- package/schemas/akm-config.json +5 -10
|
@@ -31,6 +31,7 @@ import { getImproveProcessConfig, loadConfig } from "../../core/config/config.js
|
|
|
31
31
|
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
32
32
|
import { appendEvent } from "../../core/events.js";
|
|
33
33
|
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync, } from "../../core/file-lock.js";
|
|
34
|
+
import { EXTRACT_INFRASTRUCTURE_SKIP_REASONS } from "../../core/improve-types.js";
|
|
34
35
|
import { tryAcquireMaintenanceBarrier } from "../../core/maintenance-barrier.js";
|
|
35
36
|
import { redactErrorBody } from "../../core/redaction.js";
|
|
36
37
|
import { resolveStashStandards } from "../../core/standards/resolve-stash-standards.js";
|
|
@@ -214,6 +215,24 @@ export function resolveStandaloneExtractPlan(config, selection) {
|
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
218
|
+
/** An extract envelope for a run that processed no sessions; `engine`/`engineKind` only once a runner is resolved. */
|
|
219
|
+
function emptyExtractResult(args) {
|
|
220
|
+
return {
|
|
221
|
+
schemaVersion: 1,
|
|
222
|
+
ok: args.ok,
|
|
223
|
+
shape: "extract-result",
|
|
224
|
+
dryRun: args.dryRun,
|
|
225
|
+
type: args.type,
|
|
226
|
+
sessionsProcessed: 0,
|
|
227
|
+
sessionsSkipped: 0,
|
|
228
|
+
candidatesCreated: 0,
|
|
229
|
+
proposals: [],
|
|
230
|
+
sessions: [],
|
|
231
|
+
warnings: [args.warning],
|
|
232
|
+
durationMs: Date.now() - args.startMs,
|
|
233
|
+
...(args.llmRunner ? { engine: args.llmRunner.engine, engineKind: args.llmRunner.kind } : {}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
217
236
|
/**
|
|
218
237
|
* Parse a since-string into an absolute ms-epoch cutoff. Accepts:
|
|
219
238
|
* - ISO timestamps (parsed via Date.parse)
|
|
@@ -853,6 +872,7 @@ function recordExtractSessionOutcome(args) {
|
|
|
853
872
|
preFilterInputCount: result.preFilter.inputCount,
|
|
854
873
|
preFilterOutputCount: result.preFilter.outputCount,
|
|
855
874
|
preFilterTruncatedCount: result.preFilter.truncatedCount,
|
|
875
|
+
engine: result.engine,
|
|
856
876
|
...(result.skipReason ? { skipReason: result.skipReason } : {}),
|
|
857
877
|
...(result.sessionLogPath ? { logPath: result.sessionLogPath } : {}),
|
|
858
878
|
...(result.sessionAssetRef ? { sessionAssetRef: result.sessionAssetRef } : {}),
|
|
@@ -864,7 +884,8 @@ function recordExtractSessionOutcome(args) {
|
|
|
864
884
|
}
|
|
865
885
|
}
|
|
866
886
|
function accountExtractSessionResult(result, triageEnabled, output) {
|
|
867
|
-
output.
|
|
887
|
+
const stamped = { ...result, engine: output.engine };
|
|
888
|
+
output.sessions.push(stamped);
|
|
868
889
|
if (triageEnabled) {
|
|
869
890
|
const preempted = result.skipReason === "read_failed" ||
|
|
870
891
|
result.skipReason === "too_short" ||
|
|
@@ -883,6 +904,7 @@ function accountExtractSessionResult(result, triageEnabled, output) {
|
|
|
883
904
|
else
|
|
884
905
|
output.processedCount += 1;
|
|
885
906
|
output.allProposalIds.push(...result.proposalIds);
|
|
907
|
+
return stamped;
|
|
886
908
|
}
|
|
887
909
|
/**
|
|
888
910
|
* Iterate the discovered candidate sessions: enforce the per-run cap, take the
|
|
@@ -913,6 +935,7 @@ async function runExtractSessionLoop(args) {
|
|
|
913
935
|
standardsContext: extractStandardsContext,
|
|
914
936
|
};
|
|
915
937
|
const output = {
|
|
938
|
+
engine: llmRunner.engine,
|
|
916
939
|
sessions: [],
|
|
917
940
|
processedCount: 0,
|
|
918
941
|
skippedCount: 0,
|
|
@@ -947,14 +970,14 @@ async function runExtractSessionLoop(args) {
|
|
|
947
970
|
break;
|
|
948
971
|
const { summary } = plan;
|
|
949
972
|
if (plan.kind === "skip") {
|
|
950
|
-
accountExtractSessionResult(plan.result, triage.enabled, output);
|
|
973
|
+
const accounted = accountExtractSessionResult(plan.result, triage.enabled, output);
|
|
951
974
|
recordExtractSessionOutcome({
|
|
952
975
|
stateDb,
|
|
953
976
|
trackingEnabled,
|
|
954
977
|
dryRun,
|
|
955
978
|
harness: harness.name,
|
|
956
979
|
summary,
|
|
957
|
-
result:
|
|
980
|
+
result: accounted,
|
|
958
981
|
sourceRun,
|
|
959
982
|
});
|
|
960
983
|
continue;
|
|
@@ -988,14 +1011,14 @@ async function runExtractSessionLoop(args) {
|
|
|
988
1011
|
triage,
|
|
989
1012
|
});
|
|
990
1013
|
if ("skip" in executionGate) {
|
|
991
|
-
accountExtractSessionResult(executionGate.skip, triage.enabled, output);
|
|
1014
|
+
const accounted = accountExtractSessionResult(executionGate.skip, triage.enabled, output);
|
|
992
1015
|
recordExtractSessionOutcome({
|
|
993
1016
|
stateDb,
|
|
994
1017
|
trackingEnabled,
|
|
995
1018
|
dryRun,
|
|
996
1019
|
harness: harness.name,
|
|
997
1020
|
summary,
|
|
998
|
-
result:
|
|
1021
|
+
result: accounted,
|
|
999
1022
|
sourceRun,
|
|
1000
1023
|
});
|
|
1001
1024
|
refillModelSlot();
|
|
@@ -1009,14 +1032,14 @@ async function runExtractSessionLoop(args) {
|
|
|
1009
1032
|
for (const warning of result.warnings)
|
|
1010
1033
|
topLevelWarnings.push(`session ${summary.sessionId}: ${warning}`);
|
|
1011
1034
|
}
|
|
1012
|
-
accountExtractSessionResult(result, triage.enabled, output);
|
|
1035
|
+
const accounted = accountExtractSessionResult(result, triage.enabled, output);
|
|
1013
1036
|
recordExtractSessionOutcome({
|
|
1014
1037
|
stateDb,
|
|
1015
1038
|
trackingEnabled,
|
|
1016
1039
|
dryRun,
|
|
1017
1040
|
harness: harness.name,
|
|
1018
1041
|
summary,
|
|
1019
|
-
result,
|
|
1042
|
+
result: accounted,
|
|
1020
1043
|
sourceRun,
|
|
1021
1044
|
});
|
|
1022
1045
|
}
|
|
@@ -1098,9 +1121,11 @@ function resolveExtractRunConfig(options, config, extractProcess, activeProfile)
|
|
|
1098
1121
|
// Cap on NEW sessions LLM-processed per run; 0 disables. Absent = default.
|
|
1099
1122
|
// Bounds per-run wall time / LLM cost so a backlog can't push a run past its
|
|
1100
1123
|
// task timeout — the overflow stays unseen and is picked up by later runs.
|
|
1101
|
-
const maxSessionsPerRun =
|
|
1102
|
-
?
|
|
1103
|
-
:
|
|
1124
|
+
const maxSessionsPerRun = options.since
|
|
1125
|
+
? 0
|
|
1126
|
+
: typeof extractProcess?.maxSessionsPerRun === "number"
|
|
1127
|
+
? extractProcess.maxSessionsPerRun
|
|
1128
|
+
: DEFAULT_MAX_SESSIONS_PER_RUN;
|
|
1104
1129
|
// Default discovery window — process config can override the built-in 24h.
|
|
1105
1130
|
const effectiveSince = options.since ?? extractProcess?.defaultSince;
|
|
1106
1131
|
// #626 — resolve the triage gate config once per run. Default-off → the
|
|
@@ -1166,7 +1191,7 @@ function resolveExtractRunConfig(options, config, extractProcess, activeProfile)
|
|
|
1166
1191
|
* not-found envelope) or the discovery-window listing. Extracted verbatim from
|
|
1167
1192
|
* `akmExtract`; the 48h default-since floor and location filter are unchanged.
|
|
1168
1193
|
*/
|
|
1169
|
-
function discoverExtractCandidates(options, harness, effectiveSince, startMs, dryRun) {
|
|
1194
|
+
function discoverExtractCandidates(options, harness, effectiveSince, startMs, dryRun, llmRunner) {
|
|
1170
1195
|
if (options.sessionId) {
|
|
1171
1196
|
const all = harness.listSessions({
|
|
1172
1197
|
...(options.location ? { location: options.location } : {}),
|
|
@@ -1174,20 +1199,14 @@ function discoverExtractCandidates(options, harness, effectiveSince, startMs, dr
|
|
|
1174
1199
|
const target = all.find((s) => s.sessionId === options.sessionId);
|
|
1175
1200
|
if (!target) {
|
|
1176
1201
|
return {
|
|
1177
|
-
notFound: {
|
|
1178
|
-
schemaVersion: 1,
|
|
1202
|
+
notFound: emptyExtractResult({
|
|
1179
1203
|
ok: false,
|
|
1180
|
-
shape: "extract-result",
|
|
1181
1204
|
dryRun,
|
|
1182
1205
|
type: options.type,
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
sessions: [],
|
|
1188
|
-
warnings: [`session ${options.sessionId} not found for harness ${options.type}`],
|
|
1189
|
-
durationMs: Date.now() - startMs,
|
|
1190
|
-
},
|
|
1206
|
+
warning: `session ${options.sessionId} not found for harness ${options.type}`,
|
|
1207
|
+
startMs,
|
|
1208
|
+
llmRunner,
|
|
1209
|
+
}),
|
|
1191
1210
|
};
|
|
1192
1211
|
}
|
|
1193
1212
|
return { candidates: [target] };
|
|
@@ -1286,6 +1305,28 @@ function emitExtractTriageEvent(args) {
|
|
|
1286
1305
|
},
|
|
1287
1306
|
}, eventsCtx);
|
|
1288
1307
|
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Count every session's `skipReason` (#912) and push one warning line per
|
|
1310
|
+
* infrastructure reason in {@link EXTRACT_INFRASTRUCTURE_SKIP_REASONS}.
|
|
1311
|
+
* `undefined` when nothing was skipped, so the envelope carries no key.
|
|
1312
|
+
*/
|
|
1313
|
+
function buildExtractSkipAggregate(sessions, engine, warnings) {
|
|
1314
|
+
const counts = {};
|
|
1315
|
+
for (const session of sessions) {
|
|
1316
|
+
if (!session.skipReason)
|
|
1317
|
+
continue;
|
|
1318
|
+
counts[session.skipReason] = (counts[session.skipReason] ?? 0) + 1;
|
|
1319
|
+
}
|
|
1320
|
+
if (Object.keys(counts).length === 0)
|
|
1321
|
+
return undefined;
|
|
1322
|
+
const total = sessions.length;
|
|
1323
|
+
for (const reason of EXTRACT_INFRASTRUCTURE_SKIP_REASONS) {
|
|
1324
|
+
const n = counts[reason];
|
|
1325
|
+
if (n)
|
|
1326
|
+
warnings.push(`${n} of ${total} sessions skipped: ${reason} (engine "${engine}")`);
|
|
1327
|
+
}
|
|
1328
|
+
return counts;
|
|
1329
|
+
}
|
|
1289
1330
|
export async function akmExtract(options) {
|
|
1290
1331
|
const startMs = Date.now();
|
|
1291
1332
|
if (!options.type || options.type.trim() === "") {
|
|
@@ -1310,20 +1351,13 @@ export async function akmExtract(options) {
|
|
|
1310
1351
|
(options.improveProfile ? resolveProcessEnabled("extract", options.improveProfile) : true);
|
|
1311
1352
|
// Feature-gate early so we get a clean "skipped because disabled" envelope.
|
|
1312
1353
|
if (!extractEnabled) {
|
|
1313
|
-
return {
|
|
1314
|
-
schemaVersion: 1,
|
|
1354
|
+
return emptyExtractResult({
|
|
1315
1355
|
ok: true,
|
|
1316
|
-
shape: "extract-result",
|
|
1317
1356
|
dryRun,
|
|
1318
1357
|
type: options.type,
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
proposals: [],
|
|
1323
|
-
sessions: [],
|
|
1324
|
-
warnings: ["extract is disabled by the selected improve strategy"],
|
|
1325
|
-
durationMs: Date.now() - startMs,
|
|
1326
|
-
};
|
|
1358
|
+
warning: "extract is disabled by the selected improve strategy",
|
|
1359
|
+
startMs,
|
|
1360
|
+
});
|
|
1327
1361
|
}
|
|
1328
1362
|
const { timeoutMs, llmRunner, onNotices, getNotices, maxTotalChars, minContentChars, maxSessionsPerRun, effectiveSince, triage, sessionIndexing, } = resolveExtractRunConfig(options, config, extractProcess, activeProfile);
|
|
1329
1363
|
// WI-9.10: construct this run's RunContext (extracted to
|
|
@@ -1331,39 +1365,27 @@ export async function akmExtract(options) {
|
|
|
1331
1365
|
const ctx = buildExtractRunContext({ options, config, stashDir, dryRun, sourceRun, llmRunner });
|
|
1332
1366
|
const harness = resolveHarness(options.type, options.harnesses);
|
|
1333
1367
|
if (!harness) {
|
|
1334
|
-
return {
|
|
1335
|
-
schemaVersion: 1,
|
|
1368
|
+
return emptyExtractResult({
|
|
1336
1369
|
ok: false,
|
|
1337
|
-
shape: "extract-result",
|
|
1338
1370
|
dryRun,
|
|
1339
1371
|
type: options.type,
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
sessions: [],
|
|
1345
|
-
warnings: [`no available harness matches type "${options.type}" (check that the platform is installed)`],
|
|
1346
|
-
durationMs: Date.now() - startMs,
|
|
1347
|
-
};
|
|
1372
|
+
warning: `no available harness matches type "${options.type}" (check that the platform is installed)`,
|
|
1373
|
+
startMs,
|
|
1374
|
+
llmRunner,
|
|
1375
|
+
});
|
|
1348
1376
|
}
|
|
1349
1377
|
if (!harness.isAvailable()) {
|
|
1350
|
-
return {
|
|
1351
|
-
schemaVersion: 1,
|
|
1378
|
+
return emptyExtractResult({
|
|
1352
1379
|
ok: false,
|
|
1353
|
-
shape: "extract-result",
|
|
1354
1380
|
dryRun,
|
|
1355
1381
|
type: options.type,
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
sessions: [],
|
|
1361
|
-
warnings: [`harness ${options.type} is registered but reports not-available (no session data on this machine)`],
|
|
1362
|
-
durationMs: Date.now() - startMs,
|
|
1363
|
-
};
|
|
1382
|
+
warning: `harness ${options.type} is registered but reports not-available (no session data on this machine)`,
|
|
1383
|
+
startMs,
|
|
1384
|
+
llmRunner,
|
|
1385
|
+
});
|
|
1364
1386
|
}
|
|
1365
1387
|
// Decide which sessions to process: explicit sessionId OR discovery via since.
|
|
1366
|
-
const discovery = discoverExtractCandidates(options, harness, effectiveSince, startMs, dryRun);
|
|
1388
|
+
const discovery = discoverExtractCandidates(options, harness, effectiveSince, startMs, dryRun, llmRunner);
|
|
1367
1389
|
if ("notFound" in discovery)
|
|
1368
1390
|
return discovery.notFound;
|
|
1369
1391
|
const candidates = discovery.candidates;
|
|
@@ -1449,6 +1471,7 @@ export async function akmExtract(options) {
|
|
|
1449
1471
|
if (loopResult.deferred > 0) {
|
|
1450
1472
|
topLevelWarnings.push(`Reached maxSessionsPerRun=${maxSessionsPerRun}; ${loopResult.deferred} session(s) deferred to a later run.`);
|
|
1451
1473
|
}
|
|
1474
|
+
const skipReasons = buildExtractSkipAggregate(sessions, llmRunner.engine, topLevelWarnings);
|
|
1452
1475
|
emitExtractTriageEvent({
|
|
1453
1476
|
modelPlanCount,
|
|
1454
1477
|
triageEnabled: triage.enabled,
|
|
@@ -1475,6 +1498,9 @@ export async function akmExtract(options) {
|
|
|
1475
1498
|
warnings: topLevelWarnings,
|
|
1476
1499
|
durationMs: Date.now() - startMs,
|
|
1477
1500
|
...(getNotices().length > 0 ? { notices: getNotices() } : {}),
|
|
1501
|
+
...(skipReasons ? { skipReasons } : {}),
|
|
1502
|
+
engine: llmRunner.engine,
|
|
1503
|
+
engineKind: llmRunner.kind,
|
|
1478
1504
|
};
|
|
1479
1505
|
}
|
|
1480
1506
|
/**
|
|
@@ -88,39 +88,56 @@ export function resolveImprovePlan(name, config, options = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
function buildImprovePlan(strategy, config, options) {
|
|
90
90
|
const processes = {};
|
|
91
|
+
const engineUnavailable = [];
|
|
91
92
|
for (const processName of Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES)) {
|
|
92
|
-
const
|
|
93
|
-
const enabled =
|
|
93
|
+
const sourceProcessConfig = strategy.config.processes?.[processName] ?? {};
|
|
94
|
+
const enabled = sourceProcessConfig.enabled === true;
|
|
94
95
|
let runner = null;
|
|
95
96
|
let notices = [];
|
|
96
97
|
if (IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName] !== "llm" || !enabled) {
|
|
97
|
-
processes[processName] = Object.freeze({ enabled, config:
|
|
98
|
+
processes[processName] = Object.freeze({ enabled, config: cloneAndFreeze(sourceProcessConfig), runner });
|
|
98
99
|
continue;
|
|
99
100
|
}
|
|
100
101
|
// Validation itself is structural and always runs. Only its optional repair
|
|
101
102
|
// step needs a model, so disabling repair must not create an LLM preflight.
|
|
102
|
-
|
|
103
|
+
const skipsRepairEngine = processName === "validation" && options.repairValidationFailures === false;
|
|
104
|
+
if (!skipsRepairEngine) {
|
|
103
105
|
const resolved = resolveImproveLlmExecution({
|
|
104
106
|
config,
|
|
105
107
|
profile: strategy.config,
|
|
106
|
-
process:
|
|
108
|
+
process: sourceProcessConfig,
|
|
107
109
|
processName,
|
|
108
110
|
});
|
|
109
111
|
runner = resolved?.runner ?? null;
|
|
110
112
|
notices = resolved?.notices ?? [];
|
|
111
113
|
}
|
|
112
|
-
if (!runner && !
|
|
113
|
-
|
|
114
|
+
if (!runner && !skipsRepairEngine) {
|
|
115
|
+
const configKey = `improve.strategies.${strategy.name}.processes.${processName}.engine`;
|
|
116
|
+
engineUnavailable.push({
|
|
117
|
+
process: processName,
|
|
118
|
+
configKey,
|
|
119
|
+
reason: `requires an LLM engine that is not configured. Set defaults.llmEngine or ${configKey}`,
|
|
120
|
+
});
|
|
121
|
+
processes[processName] = Object.freeze({
|
|
122
|
+
enabled: false,
|
|
123
|
+
config: cloneAndFreeze({ ...sourceProcessConfig, enabled: false }),
|
|
124
|
+
runner: null,
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
114
127
|
}
|
|
115
128
|
if (runner)
|
|
116
129
|
runner = cloneAndFreeze(runner);
|
|
117
130
|
processes[processName] = Object.freeze({
|
|
118
131
|
enabled,
|
|
119
|
-
config:
|
|
132
|
+
config: cloneAndFreeze(sourceProcessConfig),
|
|
120
133
|
runner,
|
|
121
134
|
...(notices.length > 0 ? { notices: cloneAndFreeze(notices) } : {}),
|
|
122
135
|
});
|
|
123
136
|
}
|
|
137
|
+
if (engineUnavailable.length > 0 && !Object.values(processes).some((process) => process.enabled)) {
|
|
138
|
+
const names = engineUnavailable.map((item) => `"${item.process}"`).join(", ");
|
|
139
|
+
throw new ConfigError(`No improve process can run: ${names} ${engineUnavailable.length === 1 ? "requires" : "require"} an LLM engine that is not configured. Set defaults.llmEngine, or the per-process engine key named for each.`, "LLM_NOT_CONFIGURED");
|
|
140
|
+
}
|
|
124
141
|
const triage = strategy.config.processes?.triage;
|
|
125
142
|
const judgmentEnabled = triage?.judgment?.enabled === true;
|
|
126
143
|
const triageJudgmentResolution = processes.triage.enabled && judgmentEnabled
|
|
@@ -156,5 +173,6 @@ function buildImprovePlan(strategy, config, options) {
|
|
|
156
173
|
...(triageJudgmentResolution?.notices.length
|
|
157
174
|
? { triageJudgmentNotices: cloneAndFreeze(triageJudgmentResolution.notices) }
|
|
158
175
|
: {}),
|
|
176
|
+
engineUnavailable: Object.freeze(engineUnavailable),
|
|
159
177
|
});
|
|
160
178
|
}
|
|
@@ -1132,6 +1132,19 @@ async function runImproveStageSequence(args) {
|
|
|
1132
1132
|
},
|
|
1133
1133
|
}, eventsCtx);
|
|
1134
1134
|
}
|
|
1135
|
+
for (const item of resolvedPlan.engineUnavailable) {
|
|
1136
|
+
warn(`[improve] ${item.process} skipped — it ${item.reason}.`);
|
|
1137
|
+
appendEvent({
|
|
1138
|
+
eventType: "improve_skipped",
|
|
1139
|
+
ref: undefined,
|
|
1140
|
+
metadata: {
|
|
1141
|
+
strategy: selectedStrategy.name,
|
|
1142
|
+
reason: "engine_unavailable",
|
|
1143
|
+
process: item.process,
|
|
1144
|
+
configKey: item.configKey,
|
|
1145
|
+
},
|
|
1146
|
+
}, eventsCtx);
|
|
1147
|
+
}
|
|
1135
1148
|
// Single prep->loop->post-loop pass, run under the invocation's lock.
|
|
1136
1149
|
// Accumulators are direct assignments from the single pass's results.
|
|
1137
1150
|
let preparation;
|
|
@@ -1367,6 +1380,7 @@ function finalizeImproveResult(args) {
|
|
|
1367
1380
|
promoted: triageDrain.promoted.length,
|
|
1368
1381
|
rejected: triageDrain.rejected.length,
|
|
1369
1382
|
deferred: triageDrain.deferred.length,
|
|
1383
|
+
failed: triageDrain.failed.length,
|
|
1370
1384
|
skippedByCap: triageDrain.skippedByCap.length,
|
|
1371
1385
|
},
|
|
1372
1386
|
}
|
|
@@ -25,7 +25,7 @@ import { isAutonomyLaneAllowed } from "./autonomy-gate.js";
|
|
|
25
25
|
import { akmConsolidate, inspectConsolidationPool } from "./consolidate.js";
|
|
26
26
|
import { computeSafeChunkSize, DEFAULT_CONTEXT_LENGTH_TOKENS } from "./consolidate/chunking.js";
|
|
27
27
|
// Eligibility / candidate-selection predicates live in ./eligibility.
|
|
28
|
-
import { buildLatestFeedbackTsMap, buildLatestProposalTsMap, buildUtilityMap, dedupeRefs, findAssetFilePath, isDistillCandidateRef, isLessonCandidate, isSignalDeltaEligible, } from "./eligibility.js";
|
|
28
|
+
import { buildLatestFeedbackTsMap, buildLatestProposalTsMap, buildUtilityMap, dedupeRefs, findAssetFilePath, isDistillCandidateRef, isLessonCandidate, isSignalDeltaEligible, resolveImproveScope, } from "./eligibility.js";
|
|
29
29
|
import { akmExtract, countNewExtractCandidates } from "./extract.js";
|
|
30
30
|
import { computeValenceScore, FEEDBACK_WEIGHT, UTILITY_WEIGHT } from "./feedback-valence.js";
|
|
31
31
|
import { applyMemoryCleanup } from "./memory/memory-improve.js";
|
|
@@ -178,17 +178,20 @@ function evaluateConsolidationEligibility(args) {
|
|
|
178
178
|
// #553 minPoolSize guard: skip consolidation when the eligible memory pool is
|
|
179
179
|
// below a minimum size, rather than spending an LLM pass on a handful of
|
|
180
180
|
// memories. This is an INDEPENDENT skip condition from #551's mtime pool-delta
|
|
181
|
-
// gate — either can skip. Default
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
|
|
181
|
+
// gate — either can skip. Default 0 (disabled) — every built-in strategy
|
|
182
|
+
// used to ship 500, which meant `akm improve --strategy consolidate`, typed
|
|
183
|
+
// by a human, silently did nothing on almost every real install. Evaluated
|
|
184
|
+
// against the eligible-pool count BEFORE entering the LLM loop so a skip
|
|
185
|
+
// costs ZERO LLM calls when an operator opts back into a floor.
|
|
186
|
+
const CONSOLIDATE_DEFAULT_MIN_POOL_SIZE = 0;
|
|
185
187
|
const configuredMinPoolSize = improveProfile?.processes?.consolidate?.minPoolSize;
|
|
186
188
|
const minPoolSize = typeof configuredMinPoolSize === "number" ? configuredMinPoolSize : CONSOLIDATE_DEFAULT_MIN_POOL_SIZE;
|
|
187
189
|
const eligiblePoolSize = typeof memorySummary.eligible === "number" ? memorySummary.eligible : 0;
|
|
190
|
+
const userNamedStrategyOrScope = options.strategy !== undefined || resolveImproveScope(options.scope).mode === "ref";
|
|
188
191
|
// volumeTriggered means the pool already exceeds the volume threshold (100),
|
|
189
192
|
// so a force-triggered run never trips the pool-size guard. The guard only
|
|
190
193
|
// engages when minPoolSize > 0 and the eligible pool is strictly below it.
|
|
191
|
-
const poolBelowMinSize = !volumeTriggered && minPoolSize > 0 && eligiblePoolSize < minPoolSize;
|
|
194
|
+
const poolBelowMinSize = !volumeTriggered && !userNamedStrategyOrScope && minPoolSize > 0 && eligiblePoolSize < minPoolSize;
|
|
192
195
|
return {
|
|
193
196
|
volumeTriggered,
|
|
194
197
|
consolidationOnCooldown,
|