akm-cli 0.9.0-rc.0 → 0.9.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- 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 +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -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/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -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/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -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 +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { writeFileAtomic } from "../core/common.js";
|
|
7
|
-
import { rethrowIfTestIsolationError } from "../core/errors.js";
|
|
8
|
-
import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
|
|
7
|
+
import { ConfigError, rethrowIfTestIsolationError } from "../core/errors.js";
|
|
8
|
+
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
|
|
9
|
+
import { acquireMaintenanceBarrier } from "../core/maintenance-barrier.js";
|
|
9
10
|
import { getDataDir, getLockfileLockPath, getLockfilePath } from "../core/paths.js";
|
|
10
11
|
// ── Lock sentinel ────────────────────────────────────────────────────────────
|
|
11
12
|
const LOCK_MAX_RETRIES = 3;
|
|
@@ -15,23 +16,26 @@ async function acquireLockSentinel() {
|
|
|
15
16
|
// Ensure the directory exists before attempting to create the sentinel.
|
|
16
17
|
fs.mkdirSync(path.dirname(sentinelPath), { recursive: true });
|
|
17
18
|
for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const releaseBarrier = acquireMaintenanceBarrier();
|
|
20
|
+
try {
|
|
21
|
+
const ownership = tryAcquireLockSync(sentinelPath, createLockPayload());
|
|
22
|
+
if (ownership) {
|
|
23
|
+
return () => releaseLock(ownership);
|
|
24
|
+
}
|
|
25
|
+
const probe = probeLock(sentinelPath);
|
|
26
|
+
if (probe.state === "stale" && reclaimStaleLock(sentinelPath, probe)) {
|
|
27
|
+
continue; // Reclaimed — retry immediately.
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
continue; // Reclaimed — retry immediately.
|
|
30
|
+
finally {
|
|
31
|
+
releaseBarrier();
|
|
24
32
|
}
|
|
25
33
|
// Another process holds the lock — wait briefly before retrying.
|
|
26
34
|
if (attempt < LOCK_MAX_RETRIES - 1) {
|
|
27
35
|
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS));
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
|
-
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
function releaseLockSentinel() {
|
|
34
|
-
releaseLock(getLockfileLockPath());
|
|
38
|
+
throw new ConfigError(`Could not acquire lockfile sentinel at ${sentinelPath}; refusing to write without exclusive ownership.`, "INVALID_CONFIG_FILE");
|
|
35
39
|
}
|
|
36
40
|
// ── Read / Write ────────────────────────────────────────────────────────────
|
|
37
41
|
export function readLockfile() {
|
|
@@ -50,40 +54,223 @@ export function readLockfile() {
|
|
|
50
54
|
return [];
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Like {@link readLockfile}, but THROWS instead of silently degrading to `[]`
|
|
59
|
+
* when the on-disk lockfile exists yet is not parseable JSON or not a JSON
|
|
60
|
+
* array (R-012).
|
|
61
|
+
*
|
|
62
|
+
* `readLockfile`'s fail-open contract is intentional for READ paths — a
|
|
63
|
+
* corrupt lock degrades a managed bundle to "unmanaged" rather than erroring
|
|
64
|
+
* every read-only command (`list`, `installed-stashes`, …). But
|
|
65
|
+
* {@link upsertLockEntry} and {@link removeLockEntry} read the current
|
|
66
|
+
* entries and then WRITE `[...entries, change]` back out; if that read
|
|
67
|
+
* silently returned `[]` for a corrupt file, the write would silently
|
|
68
|
+
* replace the corrupt file with one containing only the single new/changed
|
|
69
|
+
* entry — permanently destroying every other surviving lock record. Write
|
|
70
|
+
* paths use this strict variant so a corrupt lockfile fails the operation
|
|
71
|
+
* loudly (matching the guard `mergeLockEntriesSync` already applies) instead
|
|
72
|
+
* of quietly deleting user state. A missing file is NOT corruption — there
|
|
73
|
+
* is nothing to preserve, so that case still returns `[]`. Entries that fail
|
|
74
|
+
* per-entry validation are still tolerated (filtered out), matching
|
|
75
|
+
* `readLockfile`'s existing shape-tolerant behavior.
|
|
76
|
+
*/
|
|
77
|
+
function readLockfileOrThrow() {
|
|
78
|
+
const lockfilePath = getLockfilePath();
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = fs.readFileSync(lockfilePath, "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
rethrowIfTestIsolationError(err);
|
|
85
|
+
return []; // File does not exist (or is otherwise unreadable) — nothing to preserve.
|
|
86
|
+
}
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = JSON.parse(raw);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not valid JSON (${error instanceof Error ? error.message : String(error)}). Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
|
|
93
|
+
}
|
|
94
|
+
if (!Array.isArray(parsed)) {
|
|
95
|
+
throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not a JSON array. Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
|
|
96
|
+
}
|
|
97
|
+
return parsed.filter(isValidLockfileEntry);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The materialized content root recorded in the lock for a managed (git/npm)
|
|
101
|
+
* bundle — spec §10.2 desired/resolved split, where the desired config carries
|
|
102
|
+
* only the source LOCATOR and the resolved `localRoot` lives here.
|
|
103
|
+
*
|
|
104
|
+
* This is the SINGLE lock-first resolution point shared by the indexer READ
|
|
105
|
+
* path (`resolveEntryContentDir` in indexer/search) and the command-layer WRITE
|
|
106
|
+
* path (`adaptConfiguredSource` in core/write-source): consulting it first makes
|
|
107
|
+
* a write land in exactly the directory a read walks. Returns `undefined` for a
|
|
108
|
+
* bundle with no lock `localRoot` (e.g. a config migrated from a `sources[]`
|
|
109
|
+
* url, whose provider re-derives the cache path) or a non-managed type, so both
|
|
110
|
+
* callers fall back to the identical provider-path derivation.
|
|
111
|
+
*/
|
|
112
|
+
export function lockContentRootFor(bundleId, type) {
|
|
113
|
+
if (!bundleId || (type !== "git" && type !== "npm"))
|
|
114
|
+
return undefined;
|
|
115
|
+
for (const lock of readLockfile()) {
|
|
116
|
+
if (lock.id === bundleId && typeof lock.localRoot === "string" && lock.localRoot.length > 0) {
|
|
117
|
+
return lock.localRoot;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
function writeLockfileUnlocked(entries) {
|
|
54
123
|
// Always write to $DATA — never to the legacy $CONFIG location.
|
|
55
124
|
const lockfilePath = getLockfilePath();
|
|
56
125
|
const dir = path.dirname(lockfilePath);
|
|
57
126
|
fs.mkdirSync(dir, { recursive: true });
|
|
58
127
|
writeFileAtomic(lockfilePath, `${JSON.stringify(entries, null, 2)}\n`);
|
|
59
128
|
}
|
|
129
|
+
export async function writeLockfile(entries) {
|
|
130
|
+
const release = await acquireLockSentinel();
|
|
131
|
+
try {
|
|
132
|
+
writeLockfileUnlocked(entries);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
release();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Replace one exact parsed lock generation while the lockfile sentinel is held. */
|
|
139
|
+
export async function compareAndSwapLockfile(expected, desired) {
|
|
140
|
+
const release = await acquireLockSentinel();
|
|
141
|
+
try {
|
|
142
|
+
const current = readLockfileOrThrow();
|
|
143
|
+
if (JSON.stringify(current) !== JSON.stringify(expected))
|
|
144
|
+
return false;
|
|
145
|
+
writeLockfileUnlocked(desired);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
release();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
60
152
|
export async function upsertLockEntry(entry) {
|
|
61
|
-
const
|
|
153
|
+
const release = await acquireLockSentinel();
|
|
62
154
|
try {
|
|
63
|
-
|
|
155
|
+
// R-012: readLockfileOrThrow (not readLockfile) — a corrupt/malformed
|
|
156
|
+
// lockfile must abort the upsert loudly rather than read as `[]` and get
|
|
157
|
+
// silently overwritten with just this one entry.
|
|
158
|
+
const entries = readLockfileOrThrow();
|
|
64
159
|
const withoutExisting = entries.filter((e) => e.id !== entry.id);
|
|
65
|
-
|
|
160
|
+
writeLockfileUnlocked([...withoutExisting, entry]);
|
|
66
161
|
}
|
|
67
162
|
finally {
|
|
68
|
-
|
|
69
|
-
releaseLockSentinel();
|
|
163
|
+
release();
|
|
70
164
|
}
|
|
71
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Synchronously upsert lock entries (merge by id) WITHOUT acquiring the async
|
|
168
|
+
* sentinel — for a caller already holding an exclusive lifecycle lock (e.g.
|
|
169
|
+
* migrate-apply's config lock + maintenance barrier) whose synchronous body
|
|
170
|
+
* cannot await the sentinel's retry loop. No-op for an empty list.
|
|
171
|
+
*/
|
|
172
|
+
export function mergeLockEntriesSync(entries) {
|
|
173
|
+
if (entries.length === 0)
|
|
174
|
+
return;
|
|
175
|
+
let existing = [];
|
|
176
|
+
const lockfilePath = getLockfilePath();
|
|
177
|
+
if (fs.existsSync(lockfilePath)) {
|
|
178
|
+
let raw;
|
|
179
|
+
try {
|
|
180
|
+
raw = JSON.parse(fs.readFileSync(lockfilePath, "utf8"));
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new ConfigError(`Cannot merge migration lock entries into unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
|
|
184
|
+
}
|
|
185
|
+
if (!Array.isArray(raw) || !raw.every(isValidLockfileEntry)) {
|
|
186
|
+
throw new ConfigError(`Cannot merge migration lock entries into malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
|
|
187
|
+
}
|
|
188
|
+
existing = raw;
|
|
189
|
+
}
|
|
190
|
+
// MERGE per id, never replace: the migrator's entries are sparse (id/source/
|
|
191
|
+
// ref/localRoot), while an existing row may carry `resolvedVersion`,
|
|
192
|
+
// `resolvedRevision`, `integrity`, `installedAt`, … from a real install.
|
|
193
|
+
// Replacing the whole row discarded the user's recorded resolution/pin and
|
|
194
|
+
// left later update reporting comparing against an unknown prior version.
|
|
195
|
+
// Incoming DEFINED fields win; existing fields absent from the incoming
|
|
196
|
+
// entry are preserved.
|
|
197
|
+
const byId = new Map(existing.map((e) => [e.id, e]));
|
|
198
|
+
const merged = entries.map((incoming) => {
|
|
199
|
+
const prior = byId.get(incoming.id);
|
|
200
|
+
return prior ? { ...prior, ...incoming } : incoming;
|
|
201
|
+
});
|
|
202
|
+
const incomingIds = new Set(entries.map((e) => e.id));
|
|
203
|
+
writeLockfileUnlocked([...existing.filter((e) => !incomingIds.has(e.id)), ...merged]);
|
|
204
|
+
}
|
|
205
|
+
/** Freeze exact old/desired lockfile bytes while the migration lifecycle lock is held. */
|
|
206
|
+
export function planLockMergeSync(entries) {
|
|
207
|
+
const lockfilePath = getLockfilePath();
|
|
208
|
+
let oldBytes = null;
|
|
209
|
+
let existing = [];
|
|
210
|
+
try {
|
|
211
|
+
oldBytes = fs.readFileSync(lockfilePath, "utf8");
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error.code !== "ENOENT")
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
if (oldBytes !== null) {
|
|
218
|
+
let parsed;
|
|
219
|
+
try {
|
|
220
|
+
parsed = JSON.parse(oldBytes);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
throw new ConfigError(`Cannot plan migration lock entries from unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
|
|
224
|
+
}
|
|
225
|
+
if (!Array.isArray(parsed) || !parsed.every(isValidLockfileEntry)) {
|
|
226
|
+
throw new ConfigError(`Cannot plan migration lock entries from malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
|
|
227
|
+
}
|
|
228
|
+
existing = parsed;
|
|
229
|
+
}
|
|
230
|
+
if (entries.length === 0)
|
|
231
|
+
return { oldBytes, desiredBytes: oldBytes };
|
|
232
|
+
const byId = new Map(existing.map((entry) => [entry.id, entry]));
|
|
233
|
+
for (const entry of entries)
|
|
234
|
+
byId.set(entry.id, { ...byId.get(entry.id), ...entry });
|
|
235
|
+
return { oldBytes, desiredBytes: `${JSON.stringify([...byId.values()], null, 2)}\n` };
|
|
236
|
+
}
|
|
237
|
+
/** Publish a frozen migration lock generation, accepting only exact old or exact desired bytes. */
|
|
238
|
+
export function publishPlannedLockMergeSync(plan) {
|
|
239
|
+
const lockfilePath = getLockfilePath();
|
|
240
|
+
let current = null;
|
|
241
|
+
try {
|
|
242
|
+
current = fs.readFileSync(lockfilePath, "utf8");
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (error.code !== "ENOENT")
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
if (current === plan.desiredBytes)
|
|
249
|
+
return;
|
|
250
|
+
if (current !== plan.oldBytes) {
|
|
251
|
+
throw new ConfigError(`Refusing migration lockfile publication because ${lockfilePath} is a third generation, neither the journaled old nor desired bytes.`, "INVALID_CONFIG_FILE");
|
|
252
|
+
}
|
|
253
|
+
if (plan.desiredBytes === null)
|
|
254
|
+
return;
|
|
255
|
+
fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
|
|
256
|
+
writeFileAtomic(lockfilePath, plan.desiredBytes, 0o600);
|
|
257
|
+
}
|
|
72
258
|
export async function removeLockEntry(id) {
|
|
73
259
|
if (!fs.existsSync(getDataDir()))
|
|
74
260
|
return;
|
|
75
|
-
const
|
|
261
|
+
const release = await acquireLockSentinel();
|
|
76
262
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
263
|
+
// R-012: see upsertLockEntry — same destructive-overwrite risk on a
|
|
264
|
+
// corrupt lockfile, so the same strict reader is used here.
|
|
265
|
+
const entries = readLockfileOrThrow();
|
|
266
|
+
writeLockfileUnlocked(entries.filter((e) => e.id !== id));
|
|
79
267
|
}
|
|
80
268
|
finally {
|
|
81
|
-
|
|
82
|
-
releaseLockSentinel();
|
|
269
|
+
release();
|
|
83
270
|
}
|
|
84
271
|
}
|
|
85
272
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
86
|
-
function isValidLockfileEntry(value) {
|
|
273
|
+
export function isValidLockfileEntry(value) {
|
|
87
274
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
88
275
|
return false;
|
|
89
276
|
const obj = value;
|
|
@@ -2,24 +2,46 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { getHarness, SESSION_LOG_HARNESSES } from "../harnesses/index.js";
|
|
5
|
-
import { ClaudeCodeProvider } from "../harnesses/claude/session-log.js";
|
|
6
|
-
import { OpenCodeProvider } from "../harnesses/opencode/session-log.js";
|
|
7
5
|
export { extractInlineRefMentions } from "./inline-refs.js";
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
6
|
+
// #562/P2 (plan §"Kill registry drift"): the provider array is DERIVED from
|
|
7
|
+
// the unified HARNESS_REGISTRY — every harness with `capabilities.sessionLogs`
|
|
8
|
+
// must supply a `sessionLogProvider` factory on its descriptor, and this is
|
|
9
|
+
// the only place providers are instantiated. Adding a session-log harness is
|
|
10
|
+
// therefore one registry entry, never an edit here.
|
|
11
|
+
//
|
|
12
|
+
// Ordered by canonical id so the pre-derivation provider order
|
|
13
|
+
// ([claude-code, opencode] — visible in e.g. `extract --auto` result order)
|
|
14
|
+
// is preserved deterministically, independent of HARNESS_REGISTRY declaration
|
|
15
|
+
// order (which is pinned for JSON-schema enum stability).
|
|
16
|
+
//
|
|
17
|
+
// WI-9.7 (H1): `SESSION_LOG_HARNESSES` is `HARNESS_REGISTRY` narrowed by the
|
|
18
|
+
// `isSessionLogHarness` type-predicate (see `../harnesses/types.ts`), so every
|
|
19
|
+
// element's `capabilities.sessionLogs` is the literal `true` and
|
|
20
|
+
// `sessionLogProvider` is a required (non-optional) field — a harness
|
|
21
|
+
// declaring `sessionLogs: true` without a provider is now a compile error at
|
|
22
|
+
// its `HARNESS_REGISTRY` entry, not a throw here. The load-time throw this
|
|
23
|
+
// used to guard (`h.sessionLogProvider?.()` returning undefined) is gone;
|
|
24
|
+
// `h.sessionLogProvider()` cannot be called with no provider to invoke.
|
|
25
|
+
const HARNESSES = [...SESSION_LOG_HARNESSES]
|
|
26
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
27
|
+
.map((h) => h.sessionLogProvider());
|
|
28
|
+
// Reverse invariant (kept from #562): every derived provider's runtime `name`
|
|
29
|
+
// — a plain string the provider implementation sets independently of the
|
|
30
|
+
// harness registry (e.g. `ClaudeCodeProvider`'s `"claude-code"`) — must
|
|
31
|
+
// resolve back, via the id-normalization bridge, to a registry harness whose
|
|
32
|
+
// `sessionLogs` capability is set. This is NOT expressible as a compile-time
|
|
33
|
+
// type constraint: `SessionLogHarness.name` is a runtime string with no
|
|
34
|
+
// static link to the `AkmHarness` that produced it, so a typo'd or
|
|
35
|
+
// stale `name` (e.g. copy-pasted from another provider, or renamed on one
|
|
36
|
+
// side only) would only ever surface as a wrong/silent lookup miss much later
|
|
37
|
+
// (health scans, `extract --auto` attribution) rather than here at load time.
|
|
38
|
+
// Kept as a runtime guard for that reason.
|
|
15
39
|
for (const provider of HARNESSES) {
|
|
16
40
|
const harness = getHarness(provider.name);
|
|
17
41
|
if (!harness?.capabilities.sessionLogs) {
|
|
18
42
|
throw new Error(`[akm] session-log provider "${provider.name}" is not registered as a sessionLogs harness in HARNESS_REGISTRY (src/integrations/harnesses). Add it there.`);
|
|
19
43
|
}
|
|
20
44
|
}
|
|
21
|
-
// Touch the derived list so the dependency is explicit and tree-shake-safe.
|
|
22
|
-
void SESSION_LOG_HARNESSES;
|
|
23
45
|
const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENOENT|timeout/i;
|
|
24
46
|
/**
|
|
25
47
|
* Returns all available session log harnesses for the current machine.
|
|
@@ -28,22 +50,6 @@ const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENO
|
|
|
28
50
|
export function getAvailableHarnesses() {
|
|
29
51
|
return HARNESSES.filter((harness) => harness.isAvailable());
|
|
30
52
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Map each available harness to its `{ harnessName, roots }` watch target,
|
|
33
|
-
* skipping harnesses that expose no roots (absent `watchRoots()` or an empty
|
|
34
|
-
* result). This is the one stable entry point the watcher uses so it never
|
|
35
|
-
* reaches into providers directly.
|
|
36
|
-
*/
|
|
37
|
-
export function getWatchTargets() {
|
|
38
|
-
const targets = [];
|
|
39
|
-
for (const harness of getAvailableHarnesses()) {
|
|
40
|
-
const roots = harness.watchRoots?.() ?? [];
|
|
41
|
-
if (roots.length === 0)
|
|
42
|
-
continue;
|
|
43
|
-
targets.push({ harnessName: harness.name, roots });
|
|
44
|
-
}
|
|
45
|
-
return targets;
|
|
46
|
-
}
|
|
47
53
|
export function normalizeSessionTopic(text) {
|
|
48
54
|
const normalized = text.replace(/\s+/g, " ").trim().toLowerCase();
|
|
49
55
|
if (normalized.length < 10)
|
|
@@ -85,16 +91,13 @@ export function aggregateSessionEvents(events) {
|
|
|
85
91
|
* Collect normalized session events from a set of harnesses for the health
|
|
86
92
|
* candidate scan (#568).
|
|
87
93
|
*
|
|
88
|
-
* Pipeline selection per harness
|
|
89
|
-
* -
|
|
90
|
-
* richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
|
|
94
|
+
* Pipeline selection per harness:
|
|
95
|
+
* - Drive the richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
|
|
91
96
|
* structured content — tool calls, assistant content blocks, thinking,
|
|
92
97
|
* tool_result — into event text (e.g. ClaudeCodeProvider's `parseClaudeEvent`
|
|
93
98
|
* surfaces `[tool:*]` / `[tool_result]` blocks that the legacy flat
|
|
94
99
|
* `readEvents` scan drops entirely). This is what lets health advisories see
|
|
95
100
|
* repeated tool failures / long runs that the flat scan hid.
|
|
96
|
-
* - legacy-only harness (`supportsReadSession === false`): fall back to the
|
|
97
|
-
* legacy flat `readEvents()` scan (behaviour-preserving).
|
|
98
101
|
*
|
|
99
102
|
* Extracted as a pure function (harnesses injected) so it is unit-testable
|
|
100
103
|
* without touching the real on-disk session-log locations.
|
|
@@ -107,19 +110,13 @@ export function aggregateSessionEvents(events) {
|
|
|
107
110
|
* health command (`akm health`, which calls this synchronously) blow past its
|
|
108
111
|
* latency budget. `listSessions()` returns summaries sorted newest-first, so
|
|
109
112
|
* capping to the most-recent N sessions per harness keeps the richer signal
|
|
110
|
-
* for what actually matters (recent activity) while bounding cost.
|
|
111
|
-
* flat-scan path is naturally cheaper and is left uncapped.
|
|
113
|
+
* for what actually matters (recent activity) while bounding cost.
|
|
112
114
|
*/
|
|
113
115
|
const DEFAULT_MAX_SESSIONS_PER_HARNESS = 50;
|
|
114
116
|
export function collectSessionEvents(harnesses, sinceMs, maxSessionsPerHarness = DEFAULT_MAX_SESSIONS_PER_HARNESS) {
|
|
115
117
|
const events = [];
|
|
116
118
|
for (const harness of harnesses) {
|
|
117
119
|
try {
|
|
118
|
-
if (harness.supportsReadSession === false) {
|
|
119
|
-
// Legacy-only harness: only the flat event scan is available.
|
|
120
|
-
events.push(...harness.readEvents({ sinceMs }));
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
120
|
// Rich path: enumerate sessions cheaply, then read each one's full
|
|
124
121
|
// structured event stream. Falls back to readEvents if listSessions
|
|
125
122
|
// surfaces nothing (e.g. a harness that wired readSession but whose
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Shared skeleton for {@link SessionLogHarness} providers (§4.6 dedup).
|
|
6
|
+
*
|
|
7
|
+
* Holds only what the concrete providers genuinely share: safe stat'ing,
|
|
8
|
+
* recursive directory walking, the mtime-filtered file→summary listing loop,
|
|
9
|
+
* the flat JSONL/log line→event scan, and conditional-spread assembly of
|
|
10
|
+
* {@link SessionSummary} refs stamped with the provider's runtime name.
|
|
11
|
+
* Everything platform-specific — file layouts, metadata peeking, SQLite
|
|
12
|
+
* stores, message flattening — stays in the subclasses.
|
|
13
|
+
*/
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
export class AbstractSessionLogProvider {
|
|
17
|
+
isAvailable() {
|
|
18
|
+
return fs.existsSync(this.availabilityRoot());
|
|
19
|
+
}
|
|
20
|
+
/** `fs.statSync` that returns `undefined` instead of throwing. */
|
|
21
|
+
statSafe(target) {
|
|
22
|
+
try {
|
|
23
|
+
return fs.statSync(target);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Recursively yield files under `dir` whose basename passes `matches`. */
|
|
30
|
+
*walkFiles(dir, matches) {
|
|
31
|
+
try {
|
|
32
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
33
|
+
const full = path.join(dir, entry.name);
|
|
34
|
+
if (entry.isDirectory())
|
|
35
|
+
yield* this.walkFiles(full, matches);
|
|
36
|
+
else if (matches(entry.name))
|
|
37
|
+
yield full;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// permission errors etc.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Assemble a {@link SessionSummary} stamped with this provider's name.
|
|
46
|
+
* Timestamps are included whenever defined (0 is valid); `projectHint` /
|
|
47
|
+
* `title` only when non-empty, so absent metadata stays absent.
|
|
48
|
+
*/
|
|
49
|
+
sessionRef(input) {
|
|
50
|
+
return {
|
|
51
|
+
harness: this.name,
|
|
52
|
+
sessionId: input.sessionId,
|
|
53
|
+
filePath: input.filePath,
|
|
54
|
+
...(input.startedAt !== undefined ? { startedAt: input.startedAt } : {}),
|
|
55
|
+
...(input.endedAt !== undefined ? { endedAt: input.endedAt } : {}),
|
|
56
|
+
...(input.projectHint ? { projectHint: input.projectHint } : {}),
|
|
57
|
+
...(input.title ? { title: input.title } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The shared listing loop: enumerate candidate session files, drop the
|
|
62
|
+
* unstat'able and the ones older than `sinceMs`, summarize the rest, and
|
|
63
|
+
* sort newest-ended first. An enumeration failure (root missing or
|
|
64
|
+
* unreadable) returns what was collected so far rather than throwing.
|
|
65
|
+
*/
|
|
66
|
+
listSessionsFromFiles(input) {
|
|
67
|
+
const summaries = [];
|
|
68
|
+
try {
|
|
69
|
+
for (const filePath of input.enumerate()) {
|
|
70
|
+
const stat = this.statSafe(filePath);
|
|
71
|
+
if (!stat)
|
|
72
|
+
continue;
|
|
73
|
+
if (stat.mtimeMs < input.sinceMs)
|
|
74
|
+
continue;
|
|
75
|
+
const summary = input.summarize(filePath, stat);
|
|
76
|
+
if (summary)
|
|
77
|
+
summaries.push(summary);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Root missing or unreadable — return what we have.
|
|
82
|
+
}
|
|
83
|
+
return summaries.sort((a, b) => (b.endedAt ?? 0) - (a.endedAt ?? 0));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The legacy flat line scan shared by both providers' `readEvents`: parse
|
|
87
|
+
* each line as JSON, pull text/session-id through the per-provider
|
|
88
|
+
* selectors, skip entries whose text is missing or under 10 chars, and
|
|
89
|
+
* fall back to the file mtime when the entry carries no numeric timestamp.
|
|
90
|
+
*/
|
|
91
|
+
*logLineEvents(input) {
|
|
92
|
+
for (const line of input.lines) {
|
|
93
|
+
try {
|
|
94
|
+
const entry = JSON.parse(line);
|
|
95
|
+
const text = input.selectText(entry);
|
|
96
|
+
if (typeof text !== "string" || text.length < 10)
|
|
97
|
+
continue;
|
|
98
|
+
const sessionId = input.selectSessionId(entry);
|
|
99
|
+
yield {
|
|
100
|
+
harness: this.name,
|
|
101
|
+
text,
|
|
102
|
+
ts: typeof entry?.timestamp === "number" ? entry.timestamp : input.fallbackTsMs,
|
|
103
|
+
sessionId: typeof sessionId === "string" ? sessionId : undefined,
|
|
104
|
+
role: typeof entry?.role === "string" ? entry.role : "unknown",
|
|
105
|
+
filePath: input.filePath,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// skip malformed lines
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|