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
|
@@ -8,16 +8,24 @@ import { output, runWithJsonErrors } from "../../cli/shared.js";
|
|
|
8
8
|
import { loadConfig } from "../../core/config/config.js";
|
|
9
9
|
import { UsageError } from "../../core/errors.js";
|
|
10
10
|
import { getCacheDir } from "../../core/paths.js";
|
|
11
|
+
import { redactSensitiveText } from "../../core/redaction.js";
|
|
11
12
|
import { withStateDb } from "../../core/state-db.js";
|
|
12
13
|
import { clearLogFile, setLogFile } from "../../core/warn.js";
|
|
13
14
|
import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
|
|
14
15
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
16
|
+
import { collectEngineCredentialValues } from "../../integrations/agent/engine-resolution.js";
|
|
15
17
|
import { parseFlagValue } from "../../output/context.js";
|
|
16
18
|
import { getActiveCanaries, queryRecentCycleMetrics } from "../../storage/repositories/canaries-repository.js";
|
|
17
19
|
import { refreshCanarySet } from "./collapse-detector.js";
|
|
18
20
|
import { akmImprove } from "./improve.js";
|
|
19
21
|
import { buildImproveRunId, recordTerminatedImproveRun, relativeImproveResultPath, writeImproveResultFile, } from "./improve-result-file.js";
|
|
20
22
|
import { runImproveSession } from "./improve-session.js";
|
|
23
|
+
import { resolveImprovePlan } from "./improve-strategies.js";
|
|
24
|
+
let akmImproveForRun = akmImprove;
|
|
25
|
+
/** Swap the CLI's improve work implementation in deterministic subprocess tests. */
|
|
26
|
+
export function _setAkmImproveForTests(fake) {
|
|
27
|
+
akmImproveForRun = fake ?? akmImprove;
|
|
28
|
+
}
|
|
21
29
|
// R5 — collapse-detector canary set inspection / explicit refresh. The
|
|
22
30
|
// detector NEVER auto-refreshes the canary set (silent re-baselining is how a
|
|
23
31
|
// slow collapse hides); this verb is the only refresh path.
|
|
@@ -72,7 +80,7 @@ async function runCanaryInspection(refresh) {
|
|
|
72
80
|
export const improveCommand = defineCommand({
|
|
73
81
|
meta: {
|
|
74
82
|
name: "improve",
|
|
75
|
-
description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when
|
|
83
|
+
description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when the selected strategy enables consolidate. `akm improve canary [--refresh]` inspects the collapse-detector canary set.",
|
|
76
84
|
},
|
|
77
85
|
args: {
|
|
78
86
|
scope: {
|
|
@@ -120,9 +128,9 @@ export const improveCommand = defineCommand({
|
|
|
120
128
|
description: "If another improve run already holds the lock, skip gracefully (exit 0) instead of failing with 'already running' (exit 78). Use for high-frequency scheduled runs so they don't pile up failures while a longer run is in progress.",
|
|
121
129
|
default: false,
|
|
122
130
|
},
|
|
123
|
-
|
|
131
|
+
strategy: {
|
|
124
132
|
type: "string",
|
|
125
|
-
description: "Named improve
|
|
133
|
+
description: "Named improve strategy from improve.strategies or built-in strategies (default, quick, thorough, memory-focus, graph-refresh). Controls which sub-processes run and which asset types are processed.",
|
|
126
134
|
},
|
|
127
135
|
sync: {
|
|
128
136
|
type: "boolean",
|
|
@@ -166,7 +174,13 @@ export const improveCommand = defineCommand({
|
|
|
166
174
|
const minRetrievalCount = parseNonNegativeIntFlag(minRetrievalCountRaw, "--min-retrieval-count");
|
|
167
175
|
const requireFeedbackSignal = args["require-feedback-signal"];
|
|
168
176
|
const skipIfLocked = args["skip-if-locked"];
|
|
169
|
-
const
|
|
177
|
+
const strategyArg = getStringArg(args, "strategy");
|
|
178
|
+
const effectiveConfig = loadConfig();
|
|
179
|
+
// Resolve every enabled model-backed process before logging, signal
|
|
180
|
+
// lifecycle setup, or any filesystem/database side effect.
|
|
181
|
+
const resolvedPlan = resolveImprovePlan(strategyArg, effectiveConfig);
|
|
182
|
+
const selectedStrategyName = resolvedPlan.strategy.name;
|
|
183
|
+
const sensitiveValues = collectEngineCredentialValues(effectiveConfig);
|
|
170
184
|
// Only set the keys the user actually passed (citty leaves the flag
|
|
171
185
|
// undefined unless `--sync`/`--no-sync` / `--push`/`--no-push` appears),
|
|
172
186
|
// so the resolved profile `sync` block wins by default.
|
|
@@ -177,8 +191,10 @@ export const improveCommand = defineCommand({
|
|
|
177
191
|
syncOverride.enabled = syncFlag;
|
|
178
192
|
if (pushFlag !== undefined)
|
|
179
193
|
syncOverride.push = pushFlag;
|
|
180
|
-
|
|
181
|
-
|
|
194
|
+
if (!dryRun) {
|
|
195
|
+
const improveLogFile = path.join(getCacheDir(), "logs", "improve", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
|
|
196
|
+
setLogFile(improveLogFile);
|
|
197
|
+
}
|
|
182
198
|
const startedAtMs = Date.now();
|
|
183
199
|
const startedAtIso = new Date(startedAtMs).toISOString();
|
|
184
200
|
// Mint the run-id up front so signal handlers can persist a partial
|
|
@@ -186,7 +202,7 @@ export const improveCommand = defineCommand({
|
|
|
186
202
|
// was minted at end-of-run, so SIGTERM'd runs (cron timeout) left no
|
|
187
203
|
// row in improve_runs and effectively disappeared from `akm health`.
|
|
188
204
|
const runId = buildImproveRunId();
|
|
189
|
-
const primaryStashDir = resolveSourceEntries(undefined,
|
|
205
|
+
const primaryStashDir = resolveSourceEntries(undefined, effectiveConfig)[0]?.path;
|
|
190
206
|
const scopeArg = getStringArg(args, "scope");
|
|
191
207
|
const inferredScopeMode = (scopeArg ?? "").includes(":") ? "ref" : scopeArg ? "type" : "all";
|
|
192
208
|
// Signal handler + exception path both flow through this helper so
|
|
@@ -194,6 +210,8 @@ export const improveCommand = defineCommand({
|
|
|
194
210
|
// reason in metadata.terminated.
|
|
195
211
|
let runRecorded = false;
|
|
196
212
|
const persistTerminated = (reason, errorMessage) => {
|
|
213
|
+
if (dryRun)
|
|
214
|
+
return;
|
|
197
215
|
if (runRecorded)
|
|
198
216
|
return;
|
|
199
217
|
if (!primaryStashDir)
|
|
@@ -204,8 +222,9 @@ export const improveCommand = defineCommand({
|
|
|
204
222
|
scopeMode: inferredScopeMode,
|
|
205
223
|
scopeValue: scopeArg ?? null,
|
|
206
224
|
dryRun: Boolean(dryRun),
|
|
207
|
-
|
|
208
|
-
...(errorMessage ? { errorMessage } : {}),
|
|
225
|
+
strategy: selectedStrategyName,
|
|
226
|
+
...(errorMessage ? { errorMessage: redactSensitiveText(errorMessage, sensitiveValues) } : {}),
|
|
227
|
+
sensitiveValues,
|
|
209
228
|
});
|
|
210
229
|
}
|
|
211
230
|
catch (err) {
|
|
@@ -223,10 +242,11 @@ export const improveCommand = defineCommand({
|
|
|
223
242
|
let improveResult;
|
|
224
243
|
try {
|
|
225
244
|
improveResult = await runImproveSession({
|
|
226
|
-
runWork: () =>
|
|
245
|
+
runWork: () => akmImproveForRun({
|
|
227
246
|
scope: scopeArg,
|
|
228
247
|
task: taskArg,
|
|
229
248
|
dryRun,
|
|
249
|
+
resolvedPlan,
|
|
230
250
|
target: targetArg,
|
|
231
251
|
autoAccept,
|
|
232
252
|
...(runId !== undefined ? { runId } : {}),
|
|
@@ -235,7 +255,7 @@ export const improveCommand = defineCommand({
|
|
|
235
255
|
...(minRetrievalCount !== undefined ? { minRetrievalCount } : {}),
|
|
236
256
|
...(requireFeedbackSignal ? { requireFeedbackSignal } : {}),
|
|
237
257
|
...(skipIfLocked ? { skipIfLocked } : {}),
|
|
238
|
-
...(
|
|
258
|
+
...(strategyArg !== undefined ? { strategy: strategyArg } : {}),
|
|
239
259
|
...(Object.keys(syncOverride).length > 0 ? { sync: syncOverride } : {}),
|
|
240
260
|
consolidateOptions: {
|
|
241
261
|
target: targetArg,
|
|
@@ -249,7 +269,9 @@ export const improveCommand = defineCommand({
|
|
|
249
269
|
signalSource: process,
|
|
250
270
|
exit: process.exit,
|
|
251
271
|
onTerminate: (reason) => persistTerminated(reason),
|
|
252
|
-
ack: (message) => process.stderr.write(
|
|
272
|
+
ack: (message) => process.stderr.write(dryRun
|
|
273
|
+
? `[improve] ${message}; dry-run state was not persisted\n`
|
|
274
|
+
: `[improve] ${message}; recorded terminated run ${runId}\n`),
|
|
253
275
|
});
|
|
254
276
|
}
|
|
255
277
|
catch (err) {
|
|
@@ -264,9 +286,9 @@ export const improveCommand = defineCommand({
|
|
|
264
286
|
clearLogFile();
|
|
265
287
|
}
|
|
266
288
|
const durationMs = Date.now() - startedAtMs;
|
|
267
|
-
if (jsonToStdout) {
|
|
268
|
-
//
|
|
269
|
-
//
|
|
289
|
+
if (dryRun || jsonToStdout) {
|
|
290
|
+
// A dry-run never persists its result, so stdout is its only result
|
|
291
|
+
// channel. --json-to-stdout remains the live-run escape hatch.
|
|
270
292
|
output("improve", improveResult);
|
|
271
293
|
process.exit(0);
|
|
272
294
|
}
|
|
@@ -288,7 +310,7 @@ export const improveCommand = defineCommand({
|
|
|
288
310
|
runRecorded = true; // Suppress any late signal-handler write — the success path owns the row now.
|
|
289
311
|
if (primaryStashDir) {
|
|
290
312
|
try {
|
|
291
|
-
writeImproveResultFile(primaryStashDir, runId, improveResult, startedAtIso,
|
|
313
|
+
writeImproveResultFile(primaryStashDir, runId, improveResult, startedAtIso, sensitiveValues);
|
|
292
314
|
}
|
|
293
315
|
catch (err) {
|
|
294
316
|
// Stderr warning on the failure path is preferable to crashing
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import crypto from "node:crypto";
|
|
29
29
|
import path from "node:path";
|
|
30
|
+
import { decodeImproveResult } from "../../core/improve-result.js";
|
|
31
|
+
import { redactSensitiveValue } from "../../core/redaction.js";
|
|
30
32
|
import { withStateDb } from "../../core/state-db.js";
|
|
31
33
|
import { recordImproveRun } from "../../storage/repositories/improve-runs-repository.js";
|
|
32
34
|
/**
|
|
@@ -74,14 +76,10 @@ export function relativeImproveResultPath(runId) {
|
|
|
74
76
|
* (closes the dry-run/real-run artifact-trap recorded in MEMORY.md
|
|
75
77
|
* `feedback_akm_dryrun_artifact_trap`).
|
|
76
78
|
*
|
|
77
|
-
* @param profile - The `--profile` value passed to this invocation (e.g.
|
|
78
|
-
* `quick`, `reflect-distill`), or `null`/`undefined` when no profile was
|
|
79
|
-
* given. Mirrors {@link recordTerminatedImproveRun}'s `ctx.profile`
|
|
80
|
-
* convention so successful and terminated runs are equally queryable by
|
|
81
|
-
* profile. Previously hardcoded to `null` here, which meant only
|
|
82
|
-
* abnormally-terminated runs recorded their profile in state.db.
|
|
83
79
|
*/
|
|
84
|
-
export function writeImproveResultFile(stashDir, runId, result, startedAt,
|
|
80
|
+
export function writeImproveResultFile(stashDir, runId, result, startedAt, sensitiveValues = []) {
|
|
81
|
+
const decoded = decodeImproveResult(result);
|
|
82
|
+
const persistedResult = redactSensitiveValue(result, sensitiveValues);
|
|
85
83
|
withStateDb((db) => {
|
|
86
84
|
const completedAt = new Date().toISOString();
|
|
87
85
|
// startedAt is the ISO timestamp captured at process launch (passed from the
|
|
@@ -95,12 +93,13 @@ export function writeImproveResultFile(stashDir, runId, result, startedAt, profi
|
|
|
95
93
|
completedAt,
|
|
96
94
|
stashDir,
|
|
97
95
|
dryRun: Boolean(result.dryRun),
|
|
98
|
-
|
|
96
|
+
legacyProfile: redactSensitiveValue(decoded.legacyProfile, sensitiveValues),
|
|
97
|
+
strategy: redactSensitiveValue(decoded.strategy, sensitiveValues),
|
|
99
98
|
scopeMode: result.scope?.mode ?? "all",
|
|
100
|
-
scopeValue:
|
|
101
|
-
guidance:
|
|
99
|
+
scopeValue: persistedResult.scope?.value ?? null,
|
|
100
|
+
guidance: persistedResult.guidance ?? null,
|
|
102
101
|
ok: Boolean(result.ok),
|
|
103
|
-
result,
|
|
102
|
+
result: persistedResult,
|
|
104
103
|
});
|
|
105
104
|
});
|
|
106
105
|
return relativeImproveResultPath(runId);
|
|
@@ -124,37 +123,44 @@ export function writeImproveResultFile(stashDir, runId, result, startedAt, profi
|
|
|
124
123
|
*/
|
|
125
124
|
export function recordTerminatedImproveRun(stashDir, runId, startedAt, reason, ctx) {
|
|
126
125
|
const completedAt = new Date().toISOString();
|
|
127
|
-
const
|
|
128
|
-
|
|
126
|
+
const persistedReason = redactSensitiveValue(reason, ctx.sensitiveValues ?? []);
|
|
127
|
+
const persistedScopeValue = redactSensitiveValue(ctx.scopeValue, ctx.sensitiveValues ?? []);
|
|
128
|
+
const persistedStrategy = redactSensitiveValue(ctx.strategy, ctx.sensitiveValues ?? []);
|
|
129
|
+
const minimalResult = redactSensitiveValue({
|
|
130
|
+
schemaVersion: 2,
|
|
129
131
|
ok: false,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
strategy: persistedStrategy,
|
|
133
|
+
scope: { mode: ctx.scopeMode ?? "all", ...(persistedScopeValue ? { value: persistedScopeValue } : {}) },
|
|
134
|
+
dryRun: Boolean(ctx.dryRun),
|
|
135
|
+
memorySummary: { eligible: 0, derived: 0 },
|
|
132
136
|
actions: [],
|
|
133
137
|
plannedRefs: [],
|
|
134
138
|
terminated: {
|
|
135
|
-
reason,
|
|
139
|
+
reason: persistedReason,
|
|
136
140
|
at: completedAt,
|
|
137
|
-
...(ctx
|
|
141
|
+
...(ctx.errorMessage ? { errorMessage: ctx.errorMessage } : {}),
|
|
138
142
|
},
|
|
139
|
-
};
|
|
143
|
+
}, ctx.sensitiveValues ?? []);
|
|
140
144
|
withStateDb((db) => {
|
|
141
145
|
recordImproveRun(db, {
|
|
142
146
|
id: runId,
|
|
143
147
|
startedAt,
|
|
144
148
|
completedAt,
|
|
145
149
|
stashDir,
|
|
146
|
-
dryRun: Boolean(ctx
|
|
147
|
-
|
|
148
|
-
scopeMode: ctx
|
|
149
|
-
scopeValue:
|
|
150
|
+
dryRun: Boolean(ctx.dryRun),
|
|
151
|
+
strategy: persistedStrategy,
|
|
152
|
+
scopeMode: ctx.scopeMode ?? "all",
|
|
153
|
+
scopeValue: persistedScopeValue ?? null,
|
|
150
154
|
guidance: null,
|
|
151
155
|
ok: false,
|
|
152
156
|
result: minimalResult,
|
|
153
157
|
metadata: {
|
|
154
158
|
terminated: {
|
|
155
|
-
reason,
|
|
159
|
+
reason: persistedReason,
|
|
156
160
|
at: completedAt,
|
|
157
|
-
...(ctx
|
|
161
|
+
...(ctx.errorMessage
|
|
162
|
+
? { errorMessage: redactSensitiveValue(ctx.errorMessage, ctx.sensitiveValues ?? []) }
|
|
163
|
+
: {}),
|
|
158
164
|
},
|
|
159
165
|
},
|
|
160
166
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import catchup from "../../assets/improve-strategies/catchup.json" with { type: "json" };
|
|
5
|
+
import consolidate from "../../assets/improve-strategies/consolidate.json" with { type: "json" };
|
|
6
|
+
import defaultStrategy from "../../assets/improve-strategies/default.json" with { type: "json" };
|
|
7
|
+
import frequent from "../../assets/improve-strategies/frequent.json" with { type: "json" };
|
|
8
|
+
import graphRefresh from "../../assets/improve-strategies/graph-refresh.json" with { type: "json" };
|
|
9
|
+
import memoryFocus from "../../assets/improve-strategies/memory-focus.json" with { type: "json" };
|
|
10
|
+
import proactiveMaintenance from "../../assets/improve-strategies/proactive-maintenance.json" with { type: "json" };
|
|
11
|
+
import quick from "../../assets/improve-strategies/quick.json" with { type: "json" };
|
|
12
|
+
import recombineOnly from "../../assets/improve-strategies/recombine-only.json" with { type: "json" };
|
|
13
|
+
import reflectDistill from "../../assets/improve-strategies/reflect-distill.json" with { type: "json" };
|
|
14
|
+
import synthesize from "../../assets/improve-strategies/synthesize.json" with { type: "json" };
|
|
15
|
+
import thorough from "../../assets/improve-strategies/thorough.json" with { type: "json" };
|
|
16
|
+
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
17
|
+
import { deepMergeConfig } from "../../core/config/deep-merge.js";
|
|
18
|
+
import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES, } from "../../core/config/engine-semantics.js";
|
|
19
|
+
import { ConfigError } from "../../core/errors.js";
|
|
20
|
+
import { resolveImproveProcessRunner, resolveTriageJudgmentRunner, } from "../../integrations/agent/runner.js";
|
|
21
|
+
export const DEFAULT_ALLOWED_TYPES = {
|
|
22
|
+
reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"],
|
|
23
|
+
distill: ["memory"],
|
|
24
|
+
consolidate: ["memory"],
|
|
25
|
+
};
|
|
26
|
+
/** Resolve process enablement from the selected strategy, the sole improve authority. */
|
|
27
|
+
export function resolveProcessEnabled(processName, strategy) {
|
|
28
|
+
const processes = strategy.processes;
|
|
29
|
+
return processes?.[processName]?.enabled === true;
|
|
30
|
+
}
|
|
31
|
+
export function shouldSkipRef(ref, processName, strategy) {
|
|
32
|
+
const process = strategy.processes?.[processName];
|
|
33
|
+
if (process?.enabled === false)
|
|
34
|
+
return { skip: true, reason: "process-disabled" };
|
|
35
|
+
const parsed = parseAssetRef(ref);
|
|
36
|
+
const allowed = process?.allowedTypes ?? DEFAULT_ALLOWED_TYPES[processName];
|
|
37
|
+
if (!allowed.includes(parsed.type))
|
|
38
|
+
return { skip: true, reason: "type-filter" };
|
|
39
|
+
if (parsed.type === "wiki" && parsed.name.split("/")[1] === "raw")
|
|
40
|
+
return { skip: true, reason: "raw-wiki" };
|
|
41
|
+
return { skip: false, reason: "" };
|
|
42
|
+
}
|
|
43
|
+
export function isStrategyFilteredForAllPasses(ref, strategy) {
|
|
44
|
+
return shouldSkipRef(ref, "reflect", strategy).skip && shouldSkipRef(ref, "distill", strategy).skip;
|
|
45
|
+
}
|
|
46
|
+
const BUILTIN_STRATEGIES = {
|
|
47
|
+
default: defaultStrategy,
|
|
48
|
+
quick: quick,
|
|
49
|
+
thorough: thorough,
|
|
50
|
+
"memory-focus": memoryFocus,
|
|
51
|
+
"graph-refresh": graphRefresh,
|
|
52
|
+
frequent: frequent,
|
|
53
|
+
consolidate: consolidate,
|
|
54
|
+
catchup: catchup,
|
|
55
|
+
synthesize: synthesize,
|
|
56
|
+
"reflect-distill": reflectDistill,
|
|
57
|
+
"proactive-maintenance": proactiveMaintenance,
|
|
58
|
+
"recombine-only": recombineOnly,
|
|
59
|
+
};
|
|
60
|
+
if (BUILTIN_IMPROVE_STRATEGY_NAMES.some((name) => !(name in BUILTIN_STRATEGIES))) {
|
|
61
|
+
throw new Error("Built-in improve strategy names are out of sync with their assets");
|
|
62
|
+
}
|
|
63
|
+
export function resolveImproveStrategy(name, config) {
|
|
64
|
+
const selectedName = name ?? config.defaults?.improveStrategy ?? "default";
|
|
65
|
+
const userStrategies = config.improve?.strategies ?? {};
|
|
66
|
+
if (!(selectedName in BUILTIN_STRATEGIES) && !userStrategies[selectedName]) {
|
|
67
|
+
const valid = [...new Set([...Object.keys(BUILTIN_STRATEGIES), ...Object.keys(userStrategies)])].sort();
|
|
68
|
+
throw new ConfigError(`Improve strategy "${selectedName}" not found. Valid strategies: ${valid.join(", ")}.`, "UNKNOWN_IMPROVE_STRATEGY");
|
|
69
|
+
}
|
|
70
|
+
const baseStrategy = BUILTIN_STRATEGIES[selectedName] ?? BUILTIN_STRATEGIES.default;
|
|
71
|
+
const resolved = deepMergeConfig(baseStrategy, (userStrategies[selectedName] ?? {}));
|
|
72
|
+
return { name: selectedName, config: resolved };
|
|
73
|
+
}
|
|
74
|
+
export function resolveStrategyProcessEnabled(strategy, processName) {
|
|
75
|
+
return resolveProcessEnabled(processName, strategy.config);
|
|
76
|
+
}
|
|
77
|
+
function cloneAndFreeze(value) {
|
|
78
|
+
const clone = structuredClone(value);
|
|
79
|
+
const freeze = (item) => {
|
|
80
|
+
if (typeof item !== "object" || item === null || Object.isFrozen(item))
|
|
81
|
+
return;
|
|
82
|
+
for (const child of Object.values(item))
|
|
83
|
+
freeze(child);
|
|
84
|
+
Object.freeze(item);
|
|
85
|
+
};
|
|
86
|
+
freeze(clone);
|
|
87
|
+
return clone;
|
|
88
|
+
}
|
|
89
|
+
/** Resolve every enabled process structurally before improve emits signals or performs I/O. */
|
|
90
|
+
export function resolveImprovePlan(name, config, options = {}) {
|
|
91
|
+
const strategy = resolveImproveStrategy(name, config);
|
|
92
|
+
return buildImprovePlan(strategy, config, options);
|
|
93
|
+
}
|
|
94
|
+
function buildImprovePlan(strategy, config, options) {
|
|
95
|
+
const processes = {};
|
|
96
|
+
for (const processName of Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES)) {
|
|
97
|
+
const processConfig = cloneAndFreeze(strategy.config.processes?.[processName] ?? {});
|
|
98
|
+
const enabled = processConfig.enabled === true;
|
|
99
|
+
let runner = null;
|
|
100
|
+
if (IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName] !== "llm" || !enabled) {
|
|
101
|
+
processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// Validation itself is structural and always runs. Only its optional repair
|
|
105
|
+
// step needs a model, so disabling repair must not create an LLM preflight.
|
|
106
|
+
if (processName !== "validation" || options.repairValidationFailures !== false) {
|
|
107
|
+
runner = resolveImproveProcessRunner(strategy.config, processName, config);
|
|
108
|
+
}
|
|
109
|
+
if (!runner && !(processName === "validation" && options.repairValidationFailures === false)) {
|
|
110
|
+
throw new ConfigError(`Enabled improve process "${processName}" requires an LLM engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.${processName}.engine.`, "LLM_NOT_CONFIGURED");
|
|
111
|
+
}
|
|
112
|
+
if (runner)
|
|
113
|
+
runner = cloneAndFreeze(runner);
|
|
114
|
+
processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
|
|
115
|
+
}
|
|
116
|
+
const triage = strategy.config.processes?.triage;
|
|
117
|
+
const judgmentOptedIn = triage !== undefined && Object.hasOwn(triage, "judgment");
|
|
118
|
+
const triageJudgment = processes.triage.enabled && judgmentOptedIn
|
|
119
|
+
? resolveTriageJudgmentRunner(triage.judgment, config, triage, strategy.config)
|
|
120
|
+
: null;
|
|
121
|
+
if (processes.triage.enabled && judgmentOptedIn && !triageJudgment) {
|
|
122
|
+
throw new ConfigError(`Enabled improve triage judgment requires an engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.triage.judgment.engine.`, "LLM_NOT_CONFIGURED");
|
|
123
|
+
}
|
|
124
|
+
const frozenProcesses = Object.freeze(processes);
|
|
125
|
+
const frozenStrategy = Object.freeze({
|
|
126
|
+
name: strategy.name,
|
|
127
|
+
config: cloneAndFreeze({
|
|
128
|
+
...strategy.config,
|
|
129
|
+
processes: Object.fromEntries(Object.entries(frozenProcesses).map(([name, process]) => [name, process.config])),
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
strategy: frozenStrategy,
|
|
134
|
+
processes: frozenProcesses,
|
|
135
|
+
triageJudgment: triageJudgment ? cloneAndFreeze(triageJudgment) : null,
|
|
136
|
+
});
|
|
137
|
+
}
|