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
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 1. An LLM profile must be configured (no provider = no extraction). When
|
|
17
17
|
* absent, `resolveIndexPassLLM("graph", config)` returns `undefined`
|
|
18
18
|
* and the pass short-circuits.
|
|
19
|
-
* 2. `
|
|
19
|
+
* 2. The selected strategy's `processes.graphExtraction.enabled !== false`
|
|
20
20
|
* — the feature-gate layer (historically v1 spec §14, since superseded by
|
|
21
21
|
* the 0.8.0 profile shape). Set to `false` to block the pass at the
|
|
22
22
|
* feature-gate layer (no network call may ever issue).
|
|
@@ -260,7 +260,7 @@ function reuseGraphNode(previousNodes, candidate, bodyHash) {
|
|
|
260
260
|
* 1. **Provider configured** — an LLM profile must be selectable. Without a
|
|
261
261
|
* configured provider, `resolveIndexPassLLM("graph", config)` returns
|
|
262
262
|
* `undefined` (the pass cannot run because there is no model to call).
|
|
263
|
-
* 2. **Feature gate** — `
|
|
263
|
+
* 2. **Feature gate** — the selected strategy's `processes.graphExtraction.enabled`
|
|
264
264
|
* (defaults to `true`). When `false`, no network call may issue regardless
|
|
265
265
|
* of per-pass settings.
|
|
266
266
|
* 3. **Per-pass gate** — `index.graph.llm` (defaults to `true`). When
|
|
@@ -276,21 +276,25 @@ function reuseGraphNode(previousNodes, candidate, bodyHash) {
|
|
|
276
276
|
*/
|
|
277
277
|
export async function runGraphExtractionPass(ctx) {
|
|
278
278
|
const { config, sources, signal, db, reEnrich, onProgress, options = {} } = ctx;
|
|
279
|
+
const invocationOwnsConnection = Object.hasOwn(ctx, "llmConfig");
|
|
279
280
|
// Gate 1 — feature gate via isProcessEnabled, which reads the 0.8.0 path
|
|
280
|
-
// (
|
|
281
|
+
// (selected strategy's processes.graphExtraction.enabled). Defaults to
|
|
281
282
|
// enabled when the key is absent.
|
|
282
|
-
if (!isProcessEnabled("index", "graph_extraction", config))
|
|
283
|
+
if (!invocationOwnsConnection && !isProcessEnabled("index", "graph_extraction", config))
|
|
283
284
|
return { ...EMPTY_RESULT };
|
|
284
285
|
// Gate 2 — per-pass opt-out (#208). Returns the resolved llm config or
|
|
285
286
|
// `undefined` when the pass should not run.
|
|
286
|
-
const llmConfig = resolveIndexPassLLM("graph", config);
|
|
287
|
+
const llmConfig = Object.hasOwn(ctx, "llmConfig") ? ctx.llmConfig : resolveIndexPassLLM("graph", config);
|
|
287
288
|
if (!llmConfig) {
|
|
288
|
-
const reason = getIndexPassConfig(config.index, "graph")?.
|
|
289
|
-
? "index.graph.
|
|
290
|
-
: "no
|
|
289
|
+
const reason = getIndexPassConfig(config.index, "graph")?.enabled === false
|
|
290
|
+
? "index.graph.enabled is false"
|
|
291
|
+
: "no LLM engine is configured";
|
|
291
292
|
warnVerbose(`graph extraction: skipped because ${reason}.`);
|
|
292
293
|
return { ...EMPTY_RESULT };
|
|
293
294
|
}
|
|
295
|
+
const featureConfig = invocationOwnsConnection
|
|
296
|
+
? { ...config, index: { ...config.index, graph: { ...config.index?.graph, enabled: true } } }
|
|
297
|
+
: config;
|
|
294
298
|
// The pass only writes to the primary (working) stash. Read-only caches
|
|
295
299
|
// (git, npm, website) are deliberately untouched — the graph artifact for
|
|
296
300
|
// those sources would be clobbered by the next sync().
|
|
@@ -309,10 +313,14 @@ export async function runGraphExtractionPass(ctx) {
|
|
|
309
313
|
for (const queued of drained) {
|
|
310
314
|
if (signal?.aborted)
|
|
311
315
|
break;
|
|
312
|
-
await extractGraphForSingleFile(db, primary.path, queued.filePath, queued.bodyHash, {
|
|
316
|
+
await extractGraphForSingleFile(db, primary.path, queued.filePath, queued.bodyHash, {
|
|
317
|
+
config: featureConfig,
|
|
318
|
+
signal,
|
|
319
|
+
llmConfig,
|
|
320
|
+
});
|
|
313
321
|
}
|
|
314
322
|
}
|
|
315
|
-
const includeTypes = getGraphExtractionIncludeTypes(config);
|
|
323
|
+
const includeTypes = options.includeTypes ?? getGraphExtractionIncludeTypes(config);
|
|
316
324
|
let eligible = collectEligibleFiles(primary.path, includeTypes).filter((candidate) => !options.candidatePaths || options.candidatePaths.has(candidate.absPath));
|
|
317
325
|
// P2 (#624): when topN is set and a DB is available, rank the (already
|
|
318
326
|
// candidate-filtered) eligible set by utility_scores DESC and keep only the
|
|
@@ -357,7 +365,7 @@ export async function runGraphExtractionPass(ctx) {
|
|
|
357
365
|
// Resolve the effective batch size. Falls back to
|
|
358
366
|
// DEFAULT_GRAPH_EXTRACTION_BATCH_SIZE (4) when unset, and clamps against
|
|
359
367
|
// `llm.contextLength` if the model's context window is configured.
|
|
360
|
-
const batchSize = resolveBatchSize(getIndexPassConfig(config.index, "graph")?.graphExtractionBatchSize, llmConfig.contextLength);
|
|
368
|
+
const batchSize = resolveBatchSize(options.batchSize ?? getIndexPassConfig(config.index, "graph")?.graphExtractionBatchSize, llmConfig.contextLength);
|
|
361
369
|
const extractionRunId = crypto.randomUUID();
|
|
362
370
|
const extractorId = getGraphExtractorId({ model: llmConfig.model, batchSize, includeTypes });
|
|
363
371
|
const cacheVariant = extractorId;
|
|
@@ -438,7 +446,7 @@ export async function runGraphExtractionPass(ctx) {
|
|
|
438
446
|
}
|
|
439
447
|
if (!cached) {
|
|
440
448
|
telemetry.cacheMisses += 1;
|
|
441
|
-
const extraction = await graphExtract.extractGraphFromBody(llmConfig, candidate.body, signal,
|
|
449
|
+
const extraction = await graphExtract.extractGraphFromBody(llmConfig, candidate.body, signal, featureConfig, onFallback, { batchState, telemetry: runtimeTelemetry });
|
|
442
450
|
cached = {
|
|
443
451
|
entities: extraction.entities,
|
|
444
452
|
relations: extraction.relations,
|
|
@@ -562,7 +570,7 @@ export async function runGraphExtractionPass(ctx) {
|
|
|
562
570
|
telemetry.cacheMisses += uncachedChunk.length;
|
|
563
571
|
// extractGraphFromBodies always returns an array of the same length
|
|
564
572
|
// as bodies (it falls back per-asset for any missing indices).
|
|
565
|
-
const batchExtractions = await graphExtract.extractGraphFromBodies(llmConfig, bodies, signal,
|
|
573
|
+
const batchExtractions = await graphExtract.extractGraphFromBodies(llmConfig, bodies, signal, featureConfig, onFallback, { batchState, telemetry: runtimeTelemetry });
|
|
566
574
|
// Map LLM results back to original positions and write cache entries.
|
|
567
575
|
let llmIdx = 0;
|
|
568
576
|
for (let j = 0; j < chunk.length; j++) {
|
|
@@ -718,12 +726,16 @@ export async function extractGraphForSingleFile(db, stashRoot, filePath, bodyHas
|
|
|
718
726
|
}
|
|
719
727
|
else {
|
|
720
728
|
const config = opts?.config ?? loadConfig();
|
|
721
|
-
|
|
729
|
+
const invocationOwnsConnection = Object.hasOwn(opts ?? {}, "llmConfig");
|
|
730
|
+
if (!invocationOwnsConnection && !isProcessEnabled("index", "graph_extraction", config))
|
|
722
731
|
return false;
|
|
723
|
-
const llmConfig = resolveIndexPassLLM("graph", config);
|
|
732
|
+
const llmConfig = Object.hasOwn(opts ?? {}, "llmConfig") ? opts?.llmConfig : resolveIndexPassLLM("graph", config);
|
|
724
733
|
if (!llmConfig)
|
|
725
734
|
return false; // model-available guard
|
|
726
|
-
const
|
|
735
|
+
const featureConfig = invocationOwnsConnection
|
|
736
|
+
? { ...config, index: { ...config.index, graph: { ...config.index?.graph, enabled: true } } }
|
|
737
|
+
: config;
|
|
738
|
+
const result = await graphExtract.extractGraphFromBody(llmConfig, body, opts?.signal, featureConfig);
|
|
727
739
|
extraction = {
|
|
728
740
|
entities: result.entities,
|
|
729
741
|
relations: result.relations,
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { probeLock,
|
|
6
|
+
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync, } from "../core/file-lock.js";
|
|
7
|
+
import { tryAcquireMaintenanceBarrier } from "../core/maintenance-barrier.js";
|
|
7
8
|
import { getDbPath, getIndexWriterLockPath } from "../core/paths.js";
|
|
8
9
|
const INDEX_WRITER_LOCK_STALE_AFTER_MS = 12 * 60 * 60 * 1000;
|
|
9
10
|
const INDEX_WRITER_WAIT_MS = 100;
|
|
10
11
|
const DEFAULT_INDEX_WRITER_MAX_WAIT_MS = 10 * 60 * 1000;
|
|
11
12
|
const heldLocks = new Map();
|
|
12
|
-
function buildPayload(purpose
|
|
13
|
-
return
|
|
14
|
-
pid,
|
|
13
|
+
function buildPayload(purpose) {
|
|
14
|
+
return createLockPayload({
|
|
15
15
|
purpose,
|
|
16
16
|
dbPath: getDbPath(),
|
|
17
17
|
startedAt: new Date().toISOString(),
|
|
@@ -34,17 +34,19 @@ function releaseHeldLock(lockPath) {
|
|
|
34
34
|
return;
|
|
35
35
|
heldLocks.delete(lockPath);
|
|
36
36
|
process.off("exit", held.exitHandler);
|
|
37
|
-
|
|
37
|
+
releaseLock(held.ownership);
|
|
38
38
|
}
|
|
39
|
-
function retainHeldLock(lockPath) {
|
|
39
|
+
function retainHeldLock(lockPath, ownership) {
|
|
40
40
|
const existing = heldLocks.get(lockPath);
|
|
41
41
|
if (existing) {
|
|
42
42
|
existing.depth += 1;
|
|
43
43
|
return { lockPath, release: () => releaseHeldLock(lockPath) };
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
if (!ownership)
|
|
46
|
+
throw new Error(`Missing ownership for index writer lock at ${lockPath}.`);
|
|
47
|
+
const exitHandler = () => releaseLock(ownership);
|
|
46
48
|
process.on("exit", exitHandler);
|
|
47
|
-
heldLocks.set(lockPath, { depth: 1, exitHandler });
|
|
49
|
+
heldLocks.set(lockPath, { depth: 1, exitHandler, ownership });
|
|
48
50
|
return { lockPath, release: () => releaseHeldLock(lockPath) };
|
|
49
51
|
}
|
|
50
52
|
export async function acquireIndexWriterLease(options) {
|
|
@@ -53,25 +55,30 @@ export async function acquireIndexWriterLease(options) {
|
|
|
53
55
|
const startedAt = Date.now();
|
|
54
56
|
const maxWaitMs = options.maxWaitMs ?? DEFAULT_INDEX_WRITER_MAX_WAIT_MS;
|
|
55
57
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
56
|
-
if (heldLocks.has(lockPath)) {
|
|
57
|
-
options.onAcquired?.({ waitedMs: 0 });
|
|
58
|
-
return retainHeldLock(lockPath);
|
|
59
|
-
}
|
|
60
58
|
let lastWaitNoticeMs = 0;
|
|
61
59
|
while (true) {
|
|
62
60
|
throwIfAborted(options.signal);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
const releaseBarrier = tryAcquireMaintenanceBarrier();
|
|
62
|
+
if (releaseBarrier) {
|
|
63
|
+
try {
|
|
64
|
+
if (heldLocks.has(lockPath)) {
|
|
65
|
+
options.onAcquired?.({ waitedMs: Date.now() - startedAt });
|
|
66
|
+
return retainHeldLock(lockPath);
|
|
67
|
+
}
|
|
68
|
+
const ownership = tryAcquireLockSync(lockPath, buildPayload(options.purpose));
|
|
69
|
+
if (ownership) {
|
|
70
|
+
options.onAcquired?.({ waitedMs: Date.now() - startedAt });
|
|
71
|
+
return retainHeldLock(lockPath, ownership);
|
|
72
|
+
}
|
|
73
|
+
const probe = probeLock(lockPath, { staleAfterMs: INDEX_WRITER_LOCK_STALE_AFTER_MS });
|
|
74
|
+
if (probe.state === "stale") {
|
|
75
|
+
if (reclaimStaleLock(lockPath, probe))
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
releaseBarrier();
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
if (mode === "try")
|
|
77
84
|
return undefined;
|
|
@@ -23,7 +23,8 @@ import fs from "node:fs";
|
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
import { getDbPath } from "../core/paths.js";
|
|
25
25
|
import { warnVerbose } from "../core/warn.js";
|
|
26
|
-
import {
|
|
26
|
+
import { takeWorkflowDocument } from "../workflows/runtime/document-cache.js";
|
|
27
|
+
import { closeDatabase, getEntryCount, openExistingDatabase, rebuildFts, upsertEntry, upsertWorkflowDocument, } from "./db/db.js";
|
|
27
28
|
import { generateMetadataFlat } from "./passes/metadata.js";
|
|
28
29
|
import { buildSearchText } from "./search/search-fields.js";
|
|
29
30
|
/**
|
|
@@ -69,10 +70,10 @@ export async function indexWrittenAssets(stashDir, filePaths) {
|
|
|
69
70
|
for (const file of files) {
|
|
70
71
|
const generated = await generateMetadataFlat(stashDir, [file]);
|
|
71
72
|
const entry = generated.entries[0];
|
|
72
|
-
// Workflows carry a side-table
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
if (entry
|
|
73
|
+
// Workflows also carry a workflow_documents side-table upsert — handled
|
|
74
|
+
// below, mirroring the full walk — since `akm mv` rewrites citer files
|
|
75
|
+
// that can be workflows.
|
|
76
|
+
if (entry)
|
|
76
77
|
pairs.push({ file, entry });
|
|
77
78
|
}
|
|
78
79
|
if (pairs.length === 0)
|
|
@@ -91,7 +92,15 @@ export async function indexWrittenAssets(stashDir, filePaths) {
|
|
|
91
92
|
catch {
|
|
92
93
|
// stat raced a delete — index without the size, like the full walk does.
|
|
93
94
|
}
|
|
94
|
-
upsertEntry(db, entryKey, path.dirname(file), file, stashDir, entryWithSize, buildSearchText(entry));
|
|
95
|
+
const entryId = upsertEntry(db, entryKey, path.dirname(file), file, stashDir, entryWithSize, buildSearchText(entry));
|
|
96
|
+
if (entry.type === "workflow") {
|
|
97
|
+
// Same contract as the full walk (indexer.ts): the renderer cached
|
|
98
|
+
// the parsed document during metadata generation; persist it so the
|
|
99
|
+
// workflow runtime never sees an entry without its document.
|
|
100
|
+
const doc = takeWorkflowDocument(entry);
|
|
101
|
+
if (doc)
|
|
102
|
+
upsertWorkflowDocument(db, entryId, doc, fs.readFileSync(file));
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
rebuildFts(db, { incremental: true });
|
|
97
106
|
}
|
package/dist/indexer/indexer.js
CHANGED
|
@@ -182,6 +182,11 @@ async function runFinalizePhase(ctx) {
|
|
|
182
182
|
setMeta(db, "stashDir", stashDir);
|
|
183
183
|
setMeta(db, "stashDirs", JSON.stringify(sourceDirs));
|
|
184
184
|
setMeta(db, "hasEmbeddings", embeddingResult.success ? "1" : "0");
|
|
185
|
+
// Stash-organization conventions (SPEC-8): track which `index.indexBodyOpening`
|
|
186
|
+
// state the index was built with, and warn while the flag diverges from it.
|
|
187
|
+
const bodyOpeningWarning = reconcileBodyOpeningIndexState(db, config.index?.indexBodyOpening === true, ctx.full || !isIncremental);
|
|
188
|
+
if (bodyOpeningWarning)
|
|
189
|
+
warn(bodyOpeningWarning);
|
|
185
190
|
warnIfVecMissing(db);
|
|
186
191
|
const totalEntries = getEntryCount(db);
|
|
187
192
|
const semanticEntryCount = getEmbeddableEntryCount(db);
|
|
@@ -209,6 +214,41 @@ async function runFinalizePhase(ctx) {
|
|
|
209
214
|
// suppress unused warning — sources was previously used inline
|
|
210
215
|
void sources;
|
|
211
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Stash-organization conventions (SPEC-8): reconcile the `index.indexBodyOpening`
|
|
219
|
+
* flag with the state the index was last FULLY built with (index_meta key
|
|
220
|
+
* `indexBodyOpening`), returning a warning message while they diverge.
|
|
221
|
+
*
|
|
222
|
+
* Incremental runs re-extract only changed files (and embeddings are only
|
|
223
|
+
* generated for rows lacking one), so a flag toggle leaves the index MIXED
|
|
224
|
+
* until a full rebuild — `akm index --full` re-extracts every entry and wipes
|
|
225
|
+
* embeddings so they regenerate from the new text. The warning therefore
|
|
226
|
+
* repeats on every incremental run until a full walk records the flag state
|
|
227
|
+
* as applied.
|
|
228
|
+
*
|
|
229
|
+
* A missing meta key on an incremental run means the index predates this
|
|
230
|
+
* feature, i.e. it was necessarily built with the flag OFF — so the absent
|
|
231
|
+
* key reads (and is seeded) as "0", never as the current flag value. This
|
|
232
|
+
* keeps the most likely real toggle scenario — upgrade, enable the flag, run
|
|
233
|
+
* a plain `akm index` — warning until `--full` runs (review finding).
|
|
234
|
+
*
|
|
235
|
+
* Exported for tests; production's only caller is the finalize phase above.
|
|
236
|
+
*/
|
|
237
|
+
export function reconcileBodyOpeningIndexState(db, flagEnabled, isFullWalk) {
|
|
238
|
+
const bodyOpeningFlag = flagEnabled ? "1" : "0";
|
|
239
|
+
const prevBodyOpeningFlag = getMeta(db, "indexBodyOpening") ?? "0";
|
|
240
|
+
// Only a full walk (which includes the first build ever) may record the
|
|
241
|
+
// current flag as the applied state; incremental runs preserve — or, for a
|
|
242
|
+
// pre-feature index, seed — the state of the last full build.
|
|
243
|
+
setMeta(db, "indexBodyOpening", isFullWalk ? bodyOpeningFlag : prevBodyOpeningFlag);
|
|
244
|
+
if (isFullWalk || prevBodyOpeningFlag === bodyOpeningFlag)
|
|
245
|
+
return undefined;
|
|
246
|
+
return (`index.indexBodyOpening is ${flagEnabled ? "enabled" : "disabled"} but the index was built with it ` +
|
|
247
|
+
`${flagEnabled ? "disabled" : "enabled"}. Incremental runs only re-extract changed files, so ` +
|
|
248
|
+
"indexed text and embeddings are stale for unchanged entries. Run `akm index --full` to apply the new " +
|
|
249
|
+
"setting everywhere (embeddings regenerate), and re-mint collapse-detector canary baselines via " +
|
|
250
|
+
"`akm improve canary --refresh` if you use them.");
|
|
251
|
+
}
|
|
212
252
|
// ── Clean pass ───────────────────────────────────────────────────────────────
|
|
213
253
|
/**
|
|
214
254
|
* Post-index clean pass: scan the `entries` table for rows whose source file
|
|
@@ -733,10 +773,11 @@ async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal
|
|
|
733
773
|
// P3 — wall-clock budget for the enrichment pass. Defaults to llm.timeoutMs
|
|
734
774
|
// (or 10 minutes if not set). Users can extend this via llm.timeoutMs in
|
|
735
775
|
// config — no separate knob needed.
|
|
736
|
-
const
|
|
737
|
-
const enrichDeadline = AbortSignal.timeout(budgetMs);
|
|
776
|
+
const enrichDeadline = createEnrichmentDeadline(llmConfig.timeoutMs, totalEntries);
|
|
738
777
|
let deadlineHit = false;
|
|
739
778
|
const enrichSignal = (() => {
|
|
779
|
+
if (!enrichDeadline)
|
|
780
|
+
return signal ?? new AbortController().signal;
|
|
740
781
|
if (!signal)
|
|
741
782
|
return enrichDeadline;
|
|
742
783
|
// Combine: abort when either fires.
|
|
@@ -865,6 +906,10 @@ async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal
|
|
|
865
906
|
warn(`LLM enhancement failed for ${failed}/${summary.attempted} entries — they were left un-enhanced.${sample}`);
|
|
866
907
|
}
|
|
867
908
|
}
|
|
909
|
+
export function createEnrichmentDeadline(timeoutMs, totalEntries) {
|
|
910
|
+
const perEntryTimeoutMs = timeoutMs === undefined ? 10 * 60 * 1000 : timeoutMs;
|
|
911
|
+
return perEntryTimeoutMs === null ? undefined : AbortSignal.timeout(perEntryTimeoutMs * Math.max(totalEntries, 1));
|
|
912
|
+
}
|
|
868
913
|
async function generateEmbeddingsForDb(db, config, onProgress, signal) {
|
|
869
914
|
throwIfAborted(signal);
|
|
870
915
|
if (config.semanticSearchMode === "off") {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* the parent without re-running the LLM.
|
|
20
20
|
*
|
|
21
21
|
* Disabling — two orthogonal gates:
|
|
22
|
-
* 1. `
|
|
22
|
+
* 1. The selected strategy sets `processes.memoryInference.enabled = false`
|
|
23
23
|
* blocks the pass at the feature-flag layer (no network call may ever
|
|
24
24
|
* issue). Historically the v1 spec §14 gate, superseded by the 0.8.0
|
|
25
25
|
* profile shape.
|
|
@@ -60,7 +60,7 @@ const FM_CAPTURE_MODE = "captureMode";
|
|
|
60
60
|
*
|
|
61
61
|
* Two orthogonal gates:
|
|
62
62
|
*
|
|
63
|
-
* 1. **Feature gate** — `
|
|
63
|
+
* 1. **Feature gate** — the selected strategy's `processes.memoryInference.enabled`
|
|
64
64
|
* (defaults to `true`). When `false`, no network call may issue regardless
|
|
65
65
|
* of per-pass settings.
|
|
66
66
|
* 2. **Per-pass gate** — `resolveIndexPassLLM("memory", config)` (which
|
|
@@ -72,6 +72,7 @@ const FM_CAPTURE_MODE = "captureMode";
|
|
|
72
72
|
*/
|
|
73
73
|
export async function runMemoryInferencePass(ctx) {
|
|
74
74
|
const { config, sources, signal, db, reEnrich, onProgress, options = {} } = ctx;
|
|
75
|
+
const invocationOwnsConnection = Object.hasOwn(ctx, "llmConfig");
|
|
75
76
|
const compressMemoryToDerivedMemory = options.compressMemoryToDerivedMemory ?? memoryInfer.compressMemoryToDerivedMemory;
|
|
76
77
|
const result = {
|
|
77
78
|
considered: 0,
|
|
@@ -90,15 +91,18 @@ export async function runMemoryInferencePass(ctx) {
|
|
|
90
91
|
// gate) bubbles up into the pass result.
|
|
91
92
|
const inferTelemetry = {};
|
|
92
93
|
// Gate 1 — feature gate via isProcessEnabled, which reads the 0.8.0 path
|
|
93
|
-
// (
|
|
94
|
+
// (selected strategy's processes.memoryInference.enabled). Defaults to
|
|
94
95
|
// enabled when the key is absent.
|
|
95
|
-
if (!isProcessEnabled("index", "memory_inference", config))
|
|
96
|
+
if (!invocationOwnsConnection && !isProcessEnabled("index", "memory_inference", config))
|
|
96
97
|
return result;
|
|
97
98
|
// Gate 2 — per-pass opt-out (#208). Returns the resolved llm config or
|
|
98
99
|
// `undefined` when the pass should not run.
|
|
99
|
-
const llmConfig = resolveIndexPassLLM("memory", config);
|
|
100
|
+
const llmConfig = Object.hasOwn(ctx, "llmConfig") ? ctx.llmConfig : resolveIndexPassLLM("memory", config);
|
|
100
101
|
if (!llmConfig)
|
|
101
102
|
return result;
|
|
103
|
+
const featureConfig = invocationOwnsConnection
|
|
104
|
+
? { ...config, index: { ...config.index, memory: { ...config.index?.memory, enabled: true } } }
|
|
105
|
+
: config;
|
|
102
106
|
// The pass only writes to the primary (working) stash. Read-only caches
|
|
103
107
|
// (git, npm, website) are deliberately untouched — writing inferred
|
|
104
108
|
// children there would be clobbered by the next sync().
|
|
@@ -173,7 +177,7 @@ export async function runMemoryInferencePass(ctx) {
|
|
|
173
177
|
retryAttempts += 1;
|
|
174
178
|
};
|
|
175
179
|
const derived = db
|
|
176
|
-
? await withLlmCache(db, record.filePath, record.body, reEnrich ?? false, () => compressMemoryToDerivedMemory(llmConfig, record.body, signal,
|
|
180
|
+
? await withLlmCache(db, record.filePath, record.body, reEnrich ?? false, () => compressMemoryToDerivedMemory(llmConfig, record.body, signal, featureConfig, (evt) => {
|
|
177
181
|
warn(`[akm] LLM fallback for ${evt.feature}: ${evt.reason}`);
|
|
178
182
|
}, inferTelemetry, onRetryAttempt), validate, undefined, "", {
|
|
179
183
|
onCacheHit: () => {
|