akm-cli 0.9.0-rc.0 → 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 +339 -2
- 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/prompts/workflow-unit-preamble.md +26 -0
- 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/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +8 -65
- 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/migration-help.js +7 -4
- 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/commands/workflow-cli.js +276 -12
- package/dist/core/asset/asset-spec.js +58 -1
- 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 +379 -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/json-schema.js +142 -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 +118 -2
- 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/indexer/walk/matchers.js +39 -0
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -50
- 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 +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/runner-dispatch.js +91 -4
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +141 -20
- package/dist/integrations/harnesses/aider/agent-builder.js +112 -0
- package/dist/integrations/harnesses/aider/index.js +57 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +152 -0
- package/dist/integrations/harnesses/amazonq/index.js +58 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/index.js +25 -25
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +136 -0
- package/dist/integrations/harnesses/codex/index.js +62 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +121 -0
- package/dist/integrations/harnesses/copilot/index.js +59 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +59 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +27 -28
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +15 -22
- package/dist/integrations/harnesses/opencode-sdk/harness.js +60 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +8 -32
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +125 -0
- package/dist/integrations/harnesses/openhands/index.js +57 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +103 -0
- package/dist/integrations/harnesses/pi/index.js +57 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/integrations/session-logs/index.js +24 -11
- 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/renderers.js +3 -2
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +6 -0
- package/dist/output/text/helpers.js +215 -2
- package/dist/output/text/workflow.js +3 -1
- package/dist/schemas/akm-config.json +16638 -0
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +372 -0
- package/dist/scripts/migrate-storage.js +10944 -8801
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9247 -350
- 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 +190 -1
- 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/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +4 -0
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +200 -13
- package/dist/workflows/exec/brief.js +478 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1034 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1355 -0
- package/dist/workflows/exec/run-workflow.js +609 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1212 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +375 -0
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +545 -0
- package/dist/workflows/parser.js +10 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +869 -0
- package/dist/workflows/program/project.js +104 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +246 -69
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +42 -1
- package/dist/workflows/validate-summary.js +24 -3
- package/dist/workflows/validator.js +26 -1
- package/docs/data-and-telemetry.md +4 -3
- 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/release-notes/0.9.0-beta.60.md +19 -0
- 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
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* never touch a real platform.
|
|
16
16
|
* - Bounded LLM call wrapped by {@link tryLlmFeature} under the
|
|
17
17
|
* `session_extraction` gate (default-on; opt out via
|
|
18
|
-
* `
|
|
18
|
+
* `improve.strategies.<name>.processes.extract.enabled: false`).
|
|
19
19
|
* - Proposals routed via `createProposal({ source: "extract", ... })` — the
|
|
20
20
|
* same review queue as reflect / distill / consolidate. Never direct-write.
|
|
21
21
|
* - Per-candidate body assembly merges description (+ when_to_use for lessons)
|
|
@@ -27,16 +27,18 @@ import fs from "node:fs";
|
|
|
27
27
|
import path from "node:path";
|
|
28
28
|
import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
29
29
|
import { resolveStashDir, timestampForFilename } from "../../core/common.js";
|
|
30
|
-
import {
|
|
30
|
+
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
|
-
import { probeLock, releaseLock, tryAcquireLockSync } from "../../core/file-lock.js";
|
|
33
|
+
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync, } from "../../core/file-lock.js";
|
|
34
|
+
import { tryAcquireMaintenanceBarrier } from "../../core/maintenance-barrier.js";
|
|
34
35
|
import { resolveStashStandards } from "../../core/standards/resolve-stash-standards.js";
|
|
35
36
|
import { getStateDbPath, openStateDatabase, withStateDb } from "../../core/state-db.js";
|
|
36
37
|
import { repairTruncatedDescription } from "../../core/text-truncation.js";
|
|
37
38
|
import { warn } from "../../core/warn.js";
|
|
38
39
|
import { indexWrittenAssets } from "../../indexer/index-written-assets.js";
|
|
39
|
-
import {
|
|
40
|
+
import { resolveLlmEngineUse } from "../../integrations/agent/engine-resolution.js";
|
|
41
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner, } from "../../integrations/agent/runner.js";
|
|
40
42
|
import { normalizeHarnessId } from "../../integrations/harnesses/index.js";
|
|
41
43
|
import { getAvailableHarnesses } from "../../integrations/session-logs/index.js";
|
|
42
44
|
import { preFilterSession } from "../../integrations/session-logs/pre-filter.js";
|
|
@@ -48,7 +50,7 @@ import { getExtractedSessionsMap, getLastExtractRunAt, shouldSkipAlreadyExtracte
|
|
|
48
50
|
import { createProposal, isProposalSkipped } from "../proposal/repository.js";
|
|
49
51
|
import { buildExtractPrompt, EXTRACT_JSON_SCHEMA, parseExtractPayload } from "./extract-prompt.js";
|
|
50
52
|
import { buildHotProbationFrontmatter } from "./hot-probation.js";
|
|
51
|
-
import { resolveProcessEnabled } from "./improve-
|
|
53
|
+
import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
|
|
52
54
|
import { applySchemaSimilarityPenalty, loadDerivedLayerEmbeddings, } from "./schema-similarity-gate.js";
|
|
53
55
|
import { buildSessionSummaryPrompt, parseSessionSummary, SESSION_SUMMARY_JSON_SCHEMA, sessionMeetsDurationGate, writeSessionAsset, } from "./session-asset.js";
|
|
54
56
|
import { resolveTriageConfig, scoreSessionTriage } from "./triage.js";
|
|
@@ -123,20 +125,75 @@ function getExtractSessionLockPath(harness, sessionId, stateDbPath) {
|
|
|
123
125
|
* extraction outright.
|
|
124
126
|
*/
|
|
125
127
|
function acquireExtractSessionLock(lockPath) {
|
|
128
|
+
const releaseBarrier = tryAcquireMaintenanceBarrier();
|
|
129
|
+
if (!releaseBarrier)
|
|
130
|
+
return { proceed: false };
|
|
126
131
|
try {
|
|
127
132
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
let ownership = tryAcquireLockSync(lockPath, createLockPayload());
|
|
134
|
+
if (ownership)
|
|
135
|
+
return { proceed: true, ownership };
|
|
130
136
|
const probe = probeLock(lockPath, { staleAfterMs: EXTRACT_SESSION_LOCK_STALE_MS });
|
|
131
137
|
if (probe.state === "held")
|
|
132
|
-
return false;
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
return { proceed: false };
|
|
139
|
+
// Absent (released between attempt + probe) or successfully reclaimed stale lock → retry once.
|
|
140
|
+
if (probe.state === "stale" && !reclaimStaleLock(lockPath, probe))
|
|
141
|
+
return { proceed: false };
|
|
142
|
+
ownership = tryAcquireLockSync(lockPath, createLockPayload());
|
|
143
|
+
return ownership ? { proceed: true, ownership } : { proceed: false };
|
|
136
144
|
}
|
|
137
145
|
catch {
|
|
138
|
-
return true;
|
|
146
|
+
return { proceed: true };
|
|
139
147
|
}
|
|
148
|
+
finally {
|
|
149
|
+
releaseBarrier();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function cloneAndFreeze(value) {
|
|
153
|
+
const clone = structuredClone(value);
|
|
154
|
+
const freeze = (item) => {
|
|
155
|
+
if (typeof item !== "object" || item === null || Object.isFrozen(item))
|
|
156
|
+
return;
|
|
157
|
+
for (const child of Object.values(item))
|
|
158
|
+
freeze(child);
|
|
159
|
+
Object.freeze(item);
|
|
160
|
+
};
|
|
161
|
+
freeze(clone);
|
|
162
|
+
return clone;
|
|
163
|
+
}
|
|
164
|
+
/** Resolve standalone extract selection once before discovery, auto iteration, or watch startup. */
|
|
165
|
+
export function resolveStandaloneExtractPlan(config, selection) {
|
|
166
|
+
if (selection.engine && selection.strategy) {
|
|
167
|
+
throw new UsageError("--engine and --strategy are mutually exclusive. Pick one.", "INVALID_FLAG_VALUE");
|
|
168
|
+
}
|
|
169
|
+
const selected = resolveImproveStrategy(selection.strategy, config);
|
|
170
|
+
const process = cloneAndFreeze(getImproveProcessConfig(config, "extract", selected.config) ?? {});
|
|
171
|
+
const invocation = {
|
|
172
|
+
...(selection.engine ? { engine: selection.engine } : {}),
|
|
173
|
+
...(Object.hasOwn(selection, "timeoutMs") ? { timeoutMs: selection.timeoutMs ?? null } : {}),
|
|
174
|
+
};
|
|
175
|
+
const resolved = resolveLlmEngineUse(config, [selected.config, process, invocation], { optional: true });
|
|
176
|
+
if (!resolved) {
|
|
177
|
+
throw new ConfigError("No LLM engine configured for extract. Set defaults.llmEngine, pass --engine, or select an improve strategy with processes.extract.engine.", "LLM_NOT_CONFIGURED");
|
|
178
|
+
}
|
|
179
|
+
const runner = {
|
|
180
|
+
kind: "llm",
|
|
181
|
+
engine: resolved.engine,
|
|
182
|
+
connection: resolved.connection,
|
|
183
|
+
...(resolved.credential ? { credential: resolved.credential } : {}),
|
|
184
|
+
timeoutMs: resolved.timeoutMs,
|
|
185
|
+
};
|
|
186
|
+
return Object.freeze({
|
|
187
|
+
strategy: selected.name,
|
|
188
|
+
engine: resolved.engine,
|
|
189
|
+
// `akm extract` is an explicit operation. The strategy supplies behavior,
|
|
190
|
+
// but its improve-stage enablement gate does not disable this command.
|
|
191
|
+
enabled: true,
|
|
192
|
+
process,
|
|
193
|
+
runner: cloneAndFreeze(runner),
|
|
194
|
+
timeoutMs: resolved.timeoutMs,
|
|
195
|
+
embeddingConfig: cloneAndFreeze(config.embedding),
|
|
196
|
+
});
|
|
140
197
|
}
|
|
141
198
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
142
199
|
/**
|
|
@@ -245,11 +302,11 @@ export function hashSessionContent(data) {
|
|
|
245
302
|
* proposal validation failure) the session result records a warning and
|
|
246
303
|
* keeps going — one session's bad luck never aborts a multi-session run.
|
|
247
304
|
*/
|
|
248
|
-
async function processSession(harness, sessionRef, stashDir, config,
|
|
305
|
+
async function processSession(harness, sessionRef, stashDir, config, getLlmConfig, chat, ctx, sourceRun, dryRun, timeoutMs, maxTotalChars, minContentChars,
|
|
249
306
|
// #626 — pre-LLM heuristic triage gate. Default-off (enabled:false) takes the
|
|
250
307
|
// exact pre-change path (no scorer call, no new skipReason).
|
|
251
308
|
// #641 — proceduralAwareFloor is opt-in, DEFAULT OFF.
|
|
252
|
-
triage, sessionIndexing, schemaSimilarityCtx,
|
|
309
|
+
triage, sessionIndexing, schemaSimilarityCtx, hotProbationEnabled,
|
|
253
310
|
// #602 — already-extracted skip moved INSIDE processSession: the content hash
|
|
254
311
|
// can only be computed after readSession, so the skip decision lives here. The
|
|
255
312
|
// prior row + bypass flags are threaded in from the caller. Skipping here still
|
|
@@ -390,7 +447,7 @@ standardsContext) {
|
|
|
390
447
|
};
|
|
391
448
|
let llmRaw = "";
|
|
392
449
|
const llmResult = await tryLlmFeature("session_extraction", config, async () => {
|
|
393
|
-
llmRaw = await chat(
|
|
450
|
+
llmRaw = await chat(getLlmConfig(), [{ role: "user", content: prompt }], {
|
|
394
451
|
timeoutMs,
|
|
395
452
|
responseSchema: EXTRACT_JSON_SCHEMA,
|
|
396
453
|
});
|
|
@@ -451,12 +508,7 @@ standardsContext) {
|
|
|
451
508
|
// When enabled, system-generated extractions enter captureMode: hot-probation
|
|
452
509
|
// so they spend ONE consolidation cycle in probation before the deterministic
|
|
453
510
|
// dedup+quality pass promotes them. Default OFF.
|
|
454
|
-
//
|
|
455
|
-
// flag lives inside the 18-arg `processSession`, which has no handle to the
|
|
456
|
-
// active profile, and threading a 19th positional arg for a DEFAULT-OFF flag
|
|
457
|
-
// isn't worth the param bloat. The extract STAGE toggle (in `akmExtract`,
|
|
458
|
-
// below) already honors `--profile`.
|
|
459
|
-
const hotProbationEnabled = getImproveProcessConfig(config, "extract")?.hotProbation?.enabled === true;
|
|
511
|
+
// The invocation boundary resolves this from the frozen extract process.
|
|
460
512
|
for (const candidate of payload.candidates) {
|
|
461
513
|
if (dryRun) {
|
|
462
514
|
proposalIds.push(`dry-run:${candidate.type}:${candidate.name}`);
|
|
@@ -555,20 +607,19 @@ export async function akmExtract(options) {
|
|
|
555
607
|
const stashDir = options.stashDir ?? resolveStashDir();
|
|
556
608
|
const dryRun = options.dryRun ?? false;
|
|
557
609
|
const sourceRun = options.sourceRun ?? `extract-${timestampForFilename()}`;
|
|
558
|
-
// Read
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const activeProfile = options.improveProfile;
|
|
564
|
-
const extractProcess = activeProfile ? activeProfile.processes?.extract : getImproveProcessConfig(config, "extract");
|
|
610
|
+
// Read process behavior from the frozen standalone plan or the active improve
|
|
611
|
+
// strategy. This prevents config changes during watch mode from changing later
|
|
612
|
+
// triggers and prevents one improve strategy from overriding another.
|
|
613
|
+
const activeProfile = options.improveProfile ?? (options.resolvedPlan ? undefined : resolveImproveStrategy(undefined, config).config);
|
|
614
|
+
const extractProcess = options.resolvedPlan?.process ?? getImproveProcessConfig(config, "extract", activeProfile);
|
|
565
615
|
// The `extract.enabled` process toggle gates extract as a STAGE of `akm improve`
|
|
566
616
|
// (the activeProfile path) — consistent with #593/#594 where the active profile,
|
|
567
617
|
// not `default`, is the source of truth. An EXPLICIT `akm extract` invocation
|
|
568
618
|
// (no activeProfile) is a direct user/cron action and always runs; gating it on
|
|
569
619
|
// the default improve profile's stage toggle was a footgun — dropping extract
|
|
570
620
|
// from the daily improve profile would silently disable the standalone command.
|
|
571
|
-
const extractEnabled =
|
|
621
|
+
const extractEnabled = options.resolvedPlan?.enabled ??
|
|
622
|
+
(options.improveProfile ? resolveProcessEnabled("extract", options.improveProfile) : true);
|
|
572
623
|
// Feature-gate early so we get a clean "skipped because disabled" envelope.
|
|
573
624
|
if (!extractEnabled) {
|
|
574
625
|
return {
|
|
@@ -582,35 +633,29 @@ export async function akmExtract(options) {
|
|
|
582
633
|
candidatesCreated: 0,
|
|
583
634
|
proposals: [],
|
|
584
635
|
sessions: [],
|
|
585
|
-
warnings: [
|
|
586
|
-
"session_extraction feature disabled — set profiles.improve.default.processes.extract.enabled: true to use",
|
|
587
|
-
],
|
|
636
|
+
warnings: ["extract is disabled by the selected improve strategy"],
|
|
588
637
|
durationMs: Date.now() - startMs,
|
|
589
638
|
};
|
|
590
639
|
}
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if (!runnerIsLlm(runnerSpec)) {
|
|
600
|
-
throw new ConfigError(`Extract only supports mode: "llm" (in-tree LLM call). Got mode: "${runnerSpec.kind}" from profiles.improve.default.processes.extract — change it to "llm" or remove the override.`, "INVALID_CONFIG_FILE");
|
|
601
|
-
}
|
|
602
|
-
llmConfig = runnerSpec.connection;
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
605
|
-
llmConfig = getDefaultLlmConfig(config) ?? undefined;
|
|
606
|
-
}
|
|
607
|
-
if (!llmConfig) {
|
|
608
|
-
throw new ConfigError("No LLM connection configured for extract. Set profiles.llm + defaults.llm, or set profiles.improve.default.processes.extract.profile to a configured LLM profile.");
|
|
640
|
+
// Improve supplies its invocation-owned connection. Standalone extract
|
|
641
|
+
// resolves the selected process engine, then defaults.llmEngine.
|
|
642
|
+
const runnerSpec = options.resolvedPlan
|
|
643
|
+
? options.resolvedPlan.runner
|
|
644
|
+
: resolveImproveProcessRunner(activeProfile, "extract", config);
|
|
645
|
+
const fixedLlmConfig = options.resolvedPlan ? undefined : options.llmConfig;
|
|
646
|
+
if (!runnerSpec && !fixedLlmConfig) {
|
|
647
|
+
throw new ConfigError("No LLM engine configured for extract. Set defaults.llmEngine or improve.strategies.<name>.processes.extract.engine.", "LLM_NOT_CONFIGURED");
|
|
609
648
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
(
|
|
613
|
-
|
|
649
|
+
const timeoutMs = options.resolvedPlan
|
|
650
|
+
? options.resolvedPlan.timeoutMs
|
|
651
|
+
: Object.hasOwn(options, "timeoutMs")
|
|
652
|
+
? (options.timeoutMs ?? null)
|
|
653
|
+
: runnerSpec?.timeoutMs !== undefined
|
|
654
|
+
? runnerSpec.timeoutMs
|
|
655
|
+
: fixedLlmConfig && Object.hasOwn(fixedLlmConfig, "timeoutMs")
|
|
656
|
+
? (fixedLlmConfig.timeoutMs ?? null)
|
|
657
|
+
: 600_000;
|
|
658
|
+
const getLlmConfig = () => runnerSpec ? materializeLlmRunnerConnection(runnerSpec) : fixedLlmConfig;
|
|
614
659
|
// Pre-filter budget — process config can raise it for large-context models.
|
|
615
660
|
const maxTotalChars = typeof extractProcess?.maxTotalChars === "number" ? extractProcess.maxTotalChars : undefined;
|
|
616
661
|
// #595/#596 — minimum raw session size; sessions below it skip the LLM call
|
|
@@ -643,7 +688,7 @@ export async function akmExtract(options) {
|
|
|
643
688
|
const defaultSessionSummaryGenerator = async (data) => {
|
|
644
689
|
let raw = "";
|
|
645
690
|
await tryLlmFeature("session_extraction", config, async () => {
|
|
646
|
-
raw = await chatForSummary(
|
|
691
|
+
raw = await chatForSummary(getLlmConfig(), [{ role: "user", content: buildSessionSummaryPrompt(data) }], {
|
|
647
692
|
timeoutMs,
|
|
648
693
|
responseSchema: SESSION_SUMMARY_JSON_SCHEMA,
|
|
649
694
|
});
|
|
@@ -768,13 +813,14 @@ export async function akmExtract(options) {
|
|
|
768
813
|
// processes.extract.schemaSimilarity.enabled: false. The gate is inert in
|
|
769
814
|
// practice when no derived-layer embeddings exist (empty ctx → no penalty).
|
|
770
815
|
const schemaSimilarityCfg = extractProcess?.schemaSimilarity;
|
|
816
|
+
const hotProbationEnabled = extractProcess?.hotProbation?.enabled === true;
|
|
771
817
|
let schemaSimilarityCtx = null;
|
|
772
818
|
if (schemaSimilarityCfg?.enabled !== false) {
|
|
773
819
|
const derivedEmbeddings = options.schemaSimilarityEmbeddings ?? loadDerivedLayerEmbeddings();
|
|
774
820
|
schemaSimilarityCtx = {
|
|
775
821
|
config: { ...schemaSimilarityCfg, enabled: true },
|
|
776
822
|
derivedEmbeddings,
|
|
777
|
-
embeddingConfig: config.embedding,
|
|
823
|
+
embeddingConfig: options.resolvedPlan?.embeddingConfig ?? config.embedding,
|
|
778
824
|
embedFn: options.schemaSimilarityEmbedFn,
|
|
779
825
|
};
|
|
780
826
|
}
|
|
@@ -802,10 +848,11 @@ export async function akmExtract(options) {
|
|
|
802
848
|
// runs (those that open their own state.db): dry-run is read-only, an
|
|
803
849
|
// injected `stateDb` handle is an in-process/test scenario with no cross-
|
|
804
850
|
// process race, and skip-tracking-off opts out entirely.
|
|
805
|
-
let
|
|
851
|
+
let sessionLockOwnership;
|
|
806
852
|
if (trackingEnabled && !dryRun && !options.stateDb) {
|
|
807
|
-
sessionLockPath = getExtractSessionLockPath(harness.name, summary.sessionId, options.stateDbPath ?? getStateDbPath());
|
|
808
|
-
|
|
853
|
+
const sessionLockPath = getExtractSessionLockPath(harness.name, summary.sessionId, options.stateDbPath ?? getStateDbPath());
|
|
854
|
+
const sessionLock = acquireExtractSessionLock(sessionLockPath);
|
|
855
|
+
if (!sessionLock.proceed) {
|
|
809
856
|
sessions.push({
|
|
810
857
|
sessionId: summary.sessionId,
|
|
811
858
|
harness: harness.name,
|
|
@@ -819,9 +866,10 @@ export async function akmExtract(options) {
|
|
|
819
866
|
skippedCount += 1;
|
|
820
867
|
continue;
|
|
821
868
|
}
|
|
869
|
+
sessionLockOwnership = sessionLock.ownership;
|
|
822
870
|
}
|
|
823
871
|
try {
|
|
824
|
-
const result = await processSession(harness, summary, stashDir, config,
|
|
872
|
+
const result = await processSession(harness, summary, stashDir, config, getLlmConfig, chat, options.ctx, sourceRun, dryRun, timeoutMs, maxTotalChars, minContentChars, triage, sessionIndexing, schemaSimilarityCtx, hotProbationEnabled, prior, options.force === true, extractStandardsContext);
|
|
825
873
|
sessions.push(result);
|
|
826
874
|
// #626 — triage aggregation. A session reached the triage gate only when it
|
|
827
875
|
// was NOT already preempted by an earlier skip (read_failed / too_short /
|
|
@@ -916,8 +964,8 @@ export async function akmExtract(options) {
|
|
|
916
964
|
skippedCount += 1;
|
|
917
965
|
}
|
|
918
966
|
finally {
|
|
919
|
-
if (
|
|
920
|
-
releaseLock(
|
|
967
|
+
if (sessionLockOwnership)
|
|
968
|
+
releaseLock(sessionLockOwnership);
|
|
921
969
|
}
|
|
922
970
|
}
|
|
923
971
|
// Close the state.db connection we opened. Callers that injected stateDb
|
|
@@ -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
|
});
|