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
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* # Architectural seams
|
|
15
15
|
*
|
|
16
|
-
* - **Single bounded in-tree LLM call.**
|
|
16
|
+
* - **Single bounded in-tree LLM call.** Routed through `callStructured`
|
|
17
17
|
* under the `distill` gate (v1 spec §14; 0.8.0 unified the orchestration
|
|
18
18
|
* and LLM-call gates under `processes.distill.enabled`). The wrapper
|
|
19
19
|
* enforces a hard timeout (default 600s / 10 min — overridable via
|
|
@@ -34,14 +34,10 @@
|
|
|
34
34
|
*
|
|
35
35
|
* # Lesson-name derivation rule
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* derived name so two sources with the same asset-type/name collapse onto
|
|
42
|
-
* the same lesson queue entry rather than each generating its own — the
|
|
43
|
-
* proposal queue tolerates duplicate refs (id is a UUID), so the human
|
|
44
|
-
* reviewer can decide which one to accept.
|
|
37
|
+
* A nested input preserves its first legitimate scope segment
|
|
38
|
+
* (`memories/project-a/deploy` → `lessons/project-a/memory-deploy-lesson`). An
|
|
39
|
+
* unscoped input stays flat; asset types are not project scopes. Origin prefixes
|
|
40
|
+
* remain durable provenance but are not embedded in the output path.
|
|
45
41
|
*
|
|
46
42
|
* # Why we do not call `runAgent`
|
|
47
43
|
*
|
|
@@ -53,42 +49,47 @@
|
|
|
53
49
|
import fs from "node:fs";
|
|
54
50
|
import distillKnowledgeSystemPrompt from "../../assets/prompts/distill-knowledge-system.md" with { type: "text" };
|
|
55
51
|
import distillLessonSystemPrompt from "../../assets/prompts/distill-lesson-system.md" with { type: "text" };
|
|
56
|
-
import {
|
|
57
|
-
import { assembleAssetFromString } from "../../core/asset/asset-serialize.js";
|
|
52
|
+
import { assembleAsset, assembleAssetFromString, serializeFrontmatterQuoted } from "../../core/asset/asset-serialize.js";
|
|
58
53
|
import { parseFrontmatter, writeSalienceToFrontmatter } from "../../core/asset/frontmatter.js";
|
|
59
54
|
import { stripMarkdownFences } from "../../core/asset/markdown.js";
|
|
55
|
+
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
60
56
|
import { authoringRulesForType } from "../../core/authoring-rules.js";
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
57
|
+
import { getImproveProcessConfig, loadConfig } from "../../core/config/config.js";
|
|
58
|
+
import { UsageError } from "../../core/errors.js";
|
|
64
59
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
65
60
|
import { lintLessonContent } from "../../core/lesson-lint.js";
|
|
61
|
+
import { parseEmbeddedJsonResponse } from "../../core/parse.js";
|
|
66
62
|
import { getDbPath } from "../../core/paths.js";
|
|
67
|
-
import {
|
|
63
|
+
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
68
64
|
import { withStateDb } from "../../core/state-db.js";
|
|
69
65
|
import { warnVerbose } from "../../core/warn.js";
|
|
70
|
-
import { closeDatabase, getAllEntries, openIndexDatabase } from "../../indexer/db/db.js";
|
|
71
66
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
75
|
-
import {
|
|
67
|
+
import { getDefaultLlmConfig } from "../../integrations/agent/engine-resolution.js";
|
|
68
|
+
import { materializeLlmRunnerConnection, resolveImproveProcessRunner } from "../../integrations/agent/runner.js";
|
|
69
|
+
import { chatCompletion } from "../../llm/client.js";
|
|
70
|
+
import { callStructured } from "../../llm/structured-call.js";
|
|
71
|
+
import { closeDatabase, openIndexDatabase } from "../../storage/repositories/index-connection.js";
|
|
72
|
+
import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
|
|
73
|
+
import { isProposalSkipped, listProposals, proposalContent, } from "../proposal/repository.js";
|
|
74
|
+
import { stripFrontmatterBody as stripBodyForFidelity } from "./content-hash.js";
|
|
76
75
|
import { autoRepairLessonFrontmatter, autoSwapDescriptionWhenToUse, collectLessonQualityFindings, repairLessonDescriptionTruncation, } from "./distill/content-repair.js";
|
|
77
76
|
import { promoteMemoryToKnowledge } from "./distill/promote-memory.js";
|
|
78
77
|
import { fetchTopSimilarLessons, persistOutputEncodingSalience, runLessonQualityJudge, writeQualityRejection, } from "./distill/quality-gate.js";
|
|
79
78
|
import { buildClsContext, checkDistillFidelity } from "./distill-guards.js";
|
|
80
79
|
import { deriveKnowledgeRef } from "./distill-promotion-policy.js";
|
|
81
80
|
import { buildRefVocabulary, scoreEncodingSalience } from "./encoding-salience.js";
|
|
81
|
+
import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
|
|
82
|
+
import { emitProposal } from "./proposal-envelope.js";
|
|
83
|
+
import { createRunContext, resolveRunStashDir } from "./run-context.js";
|
|
82
84
|
import { computeSalience, upsertAssetSalience } from "./salience.js";
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
export { runLessonQualityJudge };
|
|
85
|
+
import { MAX_REJECTED_PROPOSALS } from "./shared.js";
|
|
86
|
+
import { durableImproveRef } from "./source-identity.js";
|
|
86
87
|
/**
|
|
87
88
|
* Asset-ref types that `akm distill` structurally refuses as inputs.
|
|
88
89
|
*
|
|
89
90
|
* Distill *produces* lessons from non-lesson sources (memory, skill, knowledge,
|
|
90
|
-
* etc.). Calling distill on an existing `
|
|
91
|
-
* `lesson
|
|
91
|
+
* etc.). Calling distill on an existing `lessons/*` ref would derive
|
|
92
|
+
* `lessons/lesson-<name>-lesson-lesson` (double `-lesson` suffix) — the
|
|
92
93
|
* recursive-ref defect observed across 323 archived rejected proposals.
|
|
93
94
|
*
|
|
94
95
|
* 08-F2: `env` and `secret` are refused as a STRUCTURAL floor — distill reads
|
|
@@ -118,38 +119,42 @@ export function isDistillRefusedInputType(type) {
|
|
|
118
119
|
// ── Lesson-ref derivation ───────────────────────────────────────────────────
|
|
119
120
|
/** Derive the proposed lesson ref from the input ref. See module docblock. */
|
|
120
121
|
export function deriveLessonRef(inputRef) {
|
|
121
|
-
const parsed =
|
|
122
|
-
// Strip
|
|
123
|
-
// distils into the same lesson namespace as `
|
|
122
|
+
const parsed = parseRefInput(inputRef);
|
|
123
|
+
// Strip the bundle: a feedback signal recorded against `team//skills/deploy`
|
|
124
|
+
// distils into the same lesson namespace as `skills/deploy`. The proposal
|
|
124
125
|
// id (a UUID) keeps the queue entries distinct, so collisions are not a
|
|
125
126
|
// problem — and reviewers want to see them next to each other anyway.
|
|
126
|
-
const
|
|
127
|
+
const parts = parsed.name.split("/");
|
|
128
|
+
const scope = parts.length > 1 ? parts.shift() : undefined;
|
|
129
|
+
const slug = `${parsed.type}-${parts.join("-")}`.toLowerCase();
|
|
127
130
|
// Replace anything outside the canonical asset-name charset with `-`. Keep
|
|
128
131
|
// it deterministic so re-runs produce the same ref.
|
|
129
132
|
const safe = slug
|
|
130
133
|
.replace(/[^a-z0-9-]+/g, "-")
|
|
131
134
|
.replace(/-+/g, "-")
|
|
132
135
|
.replace(/^-|-$/g, "");
|
|
133
|
-
|
|
136
|
+
const safeScope = scope
|
|
137
|
+
?.toLowerCase()
|
|
138
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
139
|
+
.replace(/-+/g, "-")
|
|
140
|
+
.replace(/^-|-$/g, "");
|
|
141
|
+
return `lessons/${safeScope ? `${safeScope}/` : ""}${safe}-lesson`;
|
|
134
142
|
}
|
|
135
143
|
// ── Content quality validators ──────────────────────────────────────────────
|
|
136
144
|
//
|
|
137
145
|
// The actual implementations now live in `core/proposal-quality-validators.ts`
|
|
138
146
|
// so the same checks run inside `runProposalValidators` on `proposal accept`.
|
|
139
|
-
|
|
140
|
-
// (`from "../../src/commands/distill"`) continue to resolve.
|
|
141
|
-
import { detectDoubleFrontmatter, isValidDescription, isValidWhenToUse, } from "../proposal/validators/proposal-quality-validators.js";
|
|
142
|
-
export { detectDoubleFrontmatter, isValidDescription, isValidWhenToUse };
|
|
147
|
+
import { detectDoubleFrontmatter, isValidDescription } from "../proposal/validators/proposal-quality-validators.js";
|
|
143
148
|
// ── Prompt assembly ─────────────────────────────────────────────────────────
|
|
144
149
|
const LESSON_SYSTEM_PROMPT = distillLessonSystemPrompt;
|
|
145
150
|
const KNOWLEDGE_SYSTEM_PROMPT = distillKnowledgeSystemPrompt;
|
|
146
151
|
// ── Structured-output schemas (responseSchema lift) ─────────────────────────
|
|
147
152
|
//
|
|
148
|
-
// PR 1 of the asset-writers decision (see knowledge
|
|
153
|
+
// PR 1 of the asset-writers decision (see knowledge/projects/akm/
|
|
149
154
|
// asset-writers-investigation/00-synthesis): on providers that honour
|
|
150
155
|
// `response_format: json_schema`, ask the LLM for a typed JSON object and
|
|
151
|
-
// re-assemble the markdown locally. The
|
|
152
|
-
//
|
|
156
|
+
// re-assemble the markdown locally. The "emit raw markdown with embedded
|
|
157
|
+
// frontmatter" path supports providers that ignore
|
|
153
158
|
// the schema (and for the `chat` test seam, which is wired to return strings
|
|
154
159
|
// today). Shape-level rejection codes — MALFORMED_FRONTMATTER_BLOCK,
|
|
155
160
|
// FRONTMATTER_NOT_OBJECT, INVALID_YAML, UNBALANCED_CODE_FENCE — become
|
|
@@ -252,16 +257,9 @@ export function assembleStructuredDistillMarkdown(payload, kind) {
|
|
|
252
257
|
if (kind === "knowledge" && Array.isArray(payload.sources)) {
|
|
253
258
|
const sources = payload.sources.filter((s) => typeof s === "string" && s.trim().length > 0);
|
|
254
259
|
if (sources.length > 0)
|
|
255
|
-
fm.
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.map(([k, v]) => {
|
|
259
|
-
if (Array.isArray(v))
|
|
260
|
-
return `${k}: [${v.map((s) => JSON.stringify(s)).join(", ")}]`;
|
|
261
|
-
return `${k}: ${JSON.stringify(v)}`;
|
|
262
|
-
})
|
|
263
|
-
.join("\n");
|
|
264
|
-
return assembleAssetFromString(fmLines, body);
|
|
260
|
+
fm.xrefs = sources;
|
|
261
|
+
}
|
|
262
|
+
return assembleAssetFromString(serializeFrontmatterQuoted(fm), body);
|
|
265
263
|
}
|
|
266
264
|
function validateKnowledgeContent(content, inputRef) {
|
|
267
265
|
const findings = [];
|
|
@@ -331,7 +329,11 @@ export function buildDistillPrompt(input) {
|
|
|
331
329
|
}
|
|
332
330
|
lines.push("Asset content:");
|
|
333
331
|
if (input.assetContent) {
|
|
334
|
-
|
|
332
|
+
// The output contract also uses YAML fences. Feeding source frontmatter
|
|
333
|
+
// verbatim caused local models to copy it into the lesson body, producing
|
|
334
|
+
// deterministic double-frontmatter rejection. Distillation needs the
|
|
335
|
+
// source body; its metadata is not evidence to reproduce.
|
|
336
|
+
const body = parseFrontmatter(input.assetContent).content.trim().slice(0, 3000);
|
|
335
337
|
lines.push("```");
|
|
336
338
|
lines.push(body);
|
|
337
339
|
lines.push("```");
|
|
@@ -420,78 +422,26 @@ export function buildDistillPrompt(input) {
|
|
|
420
422
|
* `distill_invoked` event (with `outcome` in the metadata) regardless of the
|
|
421
423
|
* branch taken — so observers can count invocations cheaply.
|
|
422
424
|
*/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const eligMeta = options.eligibilitySource
|
|
435
|
-
? { eligibilitySource: options.eligibilitySource }
|
|
436
|
-
: {};
|
|
437
|
-
// Recursive-distillation guard. Distill produces *lessons* from non-lesson
|
|
438
|
-
// sources (memory, skill, knowledge, etc.). Calling distill on an existing
|
|
439
|
-
// lesson would derive `lesson:lesson-<name>-lesson-lesson` (double `-lesson`
|
|
440
|
-
// suffix) and route a "lesson of a lesson" through the proposal queue —
|
|
441
|
-
// observed in 323 reviewed archived proposals as the recursive-ref defect.
|
|
442
|
-
// Refuse the input here so the improve loop (or other callers) get a clean
|
|
443
|
-
// skipped outcome instead of producing nonsense refs.
|
|
444
|
-
//
|
|
445
|
-
// The refused-type set is exported as {@link DISTILL_REFUSED_INPUT_TYPES} so
|
|
446
|
-
// the improve planner can skip these refs before queuing distill attempts;
|
|
447
|
-
// this runtime check stays as a defensive backstop for direct callers.
|
|
448
|
-
if (isDistillRefusedInputType(parsedInputRef.type)) {
|
|
449
|
-
// 08-F2: env/secret are a secret-material refusal (never read the bytes);
|
|
450
|
-
// lesson is the recursive-form refusal. Both skip BEFORE any readFileSync.
|
|
451
|
-
const isSecretInput = parsedInputRef.type === "env" || parsedInputRef.type === "secret";
|
|
452
|
-
const skippedRef = isSecretInput ? inputRef : `lesson:${parsedInputRef.name}`;
|
|
453
|
-
const message = isSecretInput
|
|
454
|
-
? `Distill refuses ${parsedInputRef.type} inputs — secret material must never be sent to the LLM.`
|
|
455
|
-
: "Distill refuses lesson inputs — lessons are the distilled form, not a source.";
|
|
456
|
-
appendEvent({
|
|
457
|
-
eventType: "distill_invoked",
|
|
458
|
-
ref: inputRef,
|
|
459
|
-
metadata: {
|
|
460
|
-
outcome: "skipped",
|
|
461
|
-
lessonRef: skippedRef,
|
|
462
|
-
message,
|
|
463
|
-
skipReason: isSecretInput ? "refused_secret_input" : "recursive_lesson_input",
|
|
464
|
-
...eligMeta,
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
return {
|
|
468
|
-
schemaVersion: 1,
|
|
469
|
-
ok: true,
|
|
470
|
-
outcome: "skipped",
|
|
471
|
-
inputRef,
|
|
472
|
-
lessonRef: skippedRef,
|
|
473
|
-
message,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
const config = options.config ?? loadConfig();
|
|
477
|
-
const stash = options.stashDir ?? resolveStashDir();
|
|
478
|
-
const chat = options.chat ?? chatCompletion;
|
|
479
|
-
const lookup = options.lookupFn ?? defaultLookup;
|
|
480
|
-
const readEventsImpl = options.readEventsFn ?? readEvents;
|
|
481
|
-
// R1 opt-out must flow into every computeSalience call this command makes so
|
|
482
|
-
// distill-written rank_score rows use the same weights as preparation's.
|
|
483
|
-
const outcomeWeightEnabled = config.improve?.salience?.outcomeWeightEnabled !== false;
|
|
484
|
-
// D-4 / #390: similar-lessons retrieval seam (test-injectable).
|
|
485
|
-
const fetchSimilarLessonsFn = options.fetchSimilarLessonsFn ?? ((query, n) => fetchTopSimilarLessons(query, n, options.stashDir));
|
|
425
|
+
/**
|
|
426
|
+
* Best-effort load of the distill INPUT asset plus the #608 encoding-time
|
|
427
|
+
* salience scoring: read the source, build the once-per-invocation bigram ref
|
|
428
|
+
* vocabulary, then score the asset (novelty×0.40 + magnitude×0.35 +
|
|
429
|
+
* predictionError×0.25) and mirror the result to both the asset frontmatter and
|
|
430
|
+
* `state.db :: asset_salience`. Every write is best-effort. Extracted verbatim
|
|
431
|
+
* from `akmDistill`; returns the (possibly salience-stamped) content plus the
|
|
432
|
+
* ref vocabulary the caller reuses when scoring the distilled OUTPUT (G4).
|
|
433
|
+
*/
|
|
434
|
+
async function loadAndScoreInputSalience(args) {
|
|
435
|
+
const { inputRef, durableInputRef, salienceWriteKey, stash, config, outcomeWeightEnabled, lookup, ctx } = args;
|
|
486
436
|
// Best-effort load: when the asset is not yet indexed we still proceed —
|
|
487
437
|
// the LLM is asked to distil from "available signal" (feedback alone).
|
|
488
438
|
let assetContent = null;
|
|
489
439
|
let assetFilePath = null;
|
|
490
440
|
try {
|
|
491
|
-
const filePath = await lookup(
|
|
441
|
+
const filePath = await lookup(durableInputRef);
|
|
492
442
|
if (filePath && fs.existsSync(filePath)) {
|
|
493
443
|
assetFilePath = filePath;
|
|
494
|
-
assetContent =
|
|
444
|
+
assetContent = ctx.readAsset(filePath);
|
|
495
445
|
}
|
|
496
446
|
}
|
|
497
447
|
catch {
|
|
@@ -523,7 +473,7 @@ export async function akmDistill(options) {
|
|
|
523
473
|
}
|
|
524
474
|
if (assetContent && assetFilePath) {
|
|
525
475
|
try {
|
|
526
|
-
const parsedRef =
|
|
476
|
+
const parsedRef = parseRefInput(inputRef);
|
|
527
477
|
// G4: predictionError decays with revision count — the prior hardcoded
|
|
528
478
|
// `revisionCount: 0` made it a dead constant 1.0. Use the number of
|
|
529
479
|
// proposals ever raised against this ref as the revision proxy.
|
|
@@ -543,7 +493,7 @@ export async function akmDistill(options) {
|
|
|
543
493
|
// 1. Write salience to the source asset frontmatter (idempotent).
|
|
544
494
|
const updatedContent = writeSalienceToFrontmatter(assetContent, salienceResult.score, salienceResult);
|
|
545
495
|
if (updatedContent !== assetContent) {
|
|
546
|
-
|
|
496
|
+
ctx.writeAsset(assetFilePath, updatedContent);
|
|
547
497
|
assetContent = updatedContent;
|
|
548
498
|
}
|
|
549
499
|
// 2. Persist encoding_salience to state.db.
|
|
@@ -556,7 +506,7 @@ export async function akmDistill(options) {
|
|
|
556
506
|
encodingSalience: salienceResult.score,
|
|
557
507
|
outcomeWeightEnabled,
|
|
558
508
|
});
|
|
559
|
-
upsertAssetSalience(stateDb,
|
|
509
|
+
upsertAssetSalience(stateDb, salienceWriteKey, vector);
|
|
560
510
|
});
|
|
561
511
|
}
|
|
562
512
|
catch {
|
|
@@ -567,24 +517,131 @@ export async function akmDistill(options) {
|
|
|
567
517
|
// Scoring errors never block distillation.
|
|
568
518
|
}
|
|
569
519
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
520
|
+
return { assetContent, existingRefVocabulary };
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Recursive-distillation + secret-material input guard. Distill produces
|
|
524
|
+
* *lessons* from non-lesson sources; a lesson input would derive a recursive
|
|
525
|
+
* `lessons/lesson-<name>` ref (the 323-archived-proposals defect) and
|
|
526
|
+
* env/secret inputs must never be read or sent to the LLM. Emits the
|
|
527
|
+
* `distill_invoked(skipped)` event and returns the terminal skipped result,
|
|
528
|
+
* or `null` when the input type is allowed. Extracted verbatim from
|
|
529
|
+
* `akmDistill` (R25/R31 — the events-ctx threading pushed it over the bar).
|
|
530
|
+
*/
|
|
531
|
+
function refuseDisallowedDistillInput(args) {
|
|
532
|
+
const { options, parsedInputRef, inputRef, durableInputRef, eligMeta } = args;
|
|
533
|
+
if (!isDistillRefusedInputType(parsedInputRef.type))
|
|
534
|
+
return null;
|
|
535
|
+
// 08-F2: env/secret are a secret-material refusal (never read the bytes);
|
|
536
|
+
// lesson is the recursive-form refusal. Both skip BEFORE any readFileSync.
|
|
537
|
+
const isSecretInput = parsedInputRef.type === "env" || parsedInputRef.type === "secret";
|
|
538
|
+
const skippedRef = isSecretInput ? inputRef : conceptIdFromTypeName("lesson", parsedInputRef.name);
|
|
539
|
+
const message = isSecretInput
|
|
540
|
+
? `Distill refuses ${parsedInputRef.type} inputs — secret material must never be sent to the LLM.`
|
|
541
|
+
: "Distill refuses lesson inputs — lessons are the distilled form, not a source.";
|
|
542
|
+
appendEvent({
|
|
543
|
+
eventType: "distill_invoked",
|
|
544
|
+
// Key on item_ref when the planner resolved one, otherwise the conceptId.
|
|
545
|
+
ref: options.itemRef ?? durableInputRef,
|
|
546
|
+
metadata: {
|
|
547
|
+
outcome: "skipped",
|
|
548
|
+
proposalRef: skippedRef,
|
|
549
|
+
message,
|
|
550
|
+
skipReason: isSecretInput ? "refused_secret_input" : "recursive_lesson_input",
|
|
551
|
+
...eligMeta,
|
|
552
|
+
},
|
|
553
|
+
}, options.eventsCtx);
|
|
554
|
+
return {
|
|
555
|
+
schemaVersion: 1,
|
|
556
|
+
ok: true,
|
|
557
|
+
outcome: "skipped",
|
|
558
|
+
inputRef,
|
|
559
|
+
proposalRef: skippedRef,
|
|
560
|
+
skipReason: isSecretInput ? "refused_secret_input" : "recursive_lesson_input",
|
|
561
|
+
message,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
export async function akmDistill(options) {
|
|
565
|
+
const inputRef = options.ref.trim();
|
|
566
|
+
if (!inputRef) {
|
|
567
|
+
throw new UsageError("Asset ref is required. Usage: akm distill <ref>", "MISSING_REQUIRED_ARGUMENT");
|
|
568
|
+
}
|
|
569
|
+
// Validate the ref shape up front so a typo never reaches the LLM.
|
|
570
|
+
const parsedInputRef = parseRefInput(inputRef);
|
|
571
|
+
const durableInputRef = durableImproveRef(inputRef);
|
|
572
|
+
// The input asset's durable salience write key is item_ref when resolved,
|
|
573
|
+
// otherwise the input conceptId.
|
|
574
|
+
const salienceWriteKey = options.itemRef ?? durableInputRef;
|
|
575
|
+
const targetKind = options.proposalKind ?? "lesson";
|
|
576
|
+
const config = options.config ?? loadConfig();
|
|
577
|
+
const improveProfile = options.improveProfile ?? resolveImproveStrategy(undefined, config).config;
|
|
578
|
+
options = { ...options, improveProfile };
|
|
579
|
+
if (!resolveProcessEnabled("distill", improveProfile)) {
|
|
580
|
+
const proposalKind = targetKind === "knowledge" ? "knowledge" : "lesson";
|
|
581
|
+
return {
|
|
582
|
+
schemaVersion: 1,
|
|
583
|
+
ok: true,
|
|
584
|
+
outcome: "config_disabled",
|
|
585
|
+
inputRef,
|
|
586
|
+
proposalRef: proposalKind === "knowledge" ? deriveKnowledgeRef(inputRef) : deriveLessonRef(inputRef),
|
|
587
|
+
proposalKind,
|
|
588
|
+
message: "distill is disabled in config; enable processes.distill.enabled to activate.",
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
// Attribution tagging: spread into every distill_invoked event's metadata so
|
|
592
|
+
// the lane that selected this asset is recorded uniformly across all outcome
|
|
593
|
+
// branches. Empty object when no lane was supplied (direct `akm distill`).
|
|
594
|
+
const eligMeta = options.eligibilitySource
|
|
595
|
+
? { eligibilitySource: options.eligibilitySource }
|
|
596
|
+
: {};
|
|
597
|
+
// Recursive-distillation guard (see refuseDisallowedDistillInput). The
|
|
598
|
+
// refused-type set is exported as {@link DISTILL_REFUSED_INPUT_TYPES} so
|
|
599
|
+
// the improve planner can skip these refs before queuing distill attempts;
|
|
600
|
+
// this runtime check stays as a defensive backstop for direct callers.
|
|
601
|
+
const refused = refuseDisallowedDistillInput({ options, parsedInputRef, inputRef, durableInputRef, eligMeta });
|
|
602
|
+
if (refused)
|
|
603
|
+
return refused;
|
|
604
|
+
const stash = resolveRunStashDir(options.stashDir);
|
|
605
|
+
const chat = options.chat ?? chatCompletion;
|
|
606
|
+
const distillLlm = Object.hasOwn(options, "llmConfig")
|
|
607
|
+
? (options.llmConfig ?? undefined)
|
|
608
|
+
: (() => {
|
|
609
|
+
const runner = resolveImproveProcessRunner(options.improveProfile, "distill", config);
|
|
610
|
+
return runner ? materializeLlmRunnerConnection(runner) : getDefaultLlmConfig(config);
|
|
611
|
+
})();
|
|
612
|
+
const lookup = options.lookupFn ?? ((ref) => defaultLookup(ref, stash));
|
|
613
|
+
const readEventsImpl = options.readEventsFn ?? readEvents;
|
|
614
|
+
// R1 opt-out must flow into every computeSalience call this command makes so
|
|
615
|
+
// distill-written rank_score rows use the same weights as preparation's.
|
|
616
|
+
const outcomeWeightEnabled = config.improve?.salience?.outcomeWeightEnabled !== false;
|
|
617
|
+
// D-4 / #390: similar-lessons retrieval seam (test-injectable).
|
|
618
|
+
const fetchSimilarLessonsFn = options.fetchSimilarLessonsFn ?? ((query, n) => fetchTopSimilarLessons(query, n, options.stashDir));
|
|
619
|
+
const assetCtx = createRunContext({
|
|
620
|
+
stashDir: stash,
|
|
621
|
+
config,
|
|
622
|
+
eventsCtx: options.eventsCtx ?? {},
|
|
623
|
+
proposalsCtx: options.ctx ?? {},
|
|
624
|
+
chat,
|
|
625
|
+
getLlmConfig: () => distillLlm ?? null,
|
|
626
|
+
sourceRun: options.sourceRun ?? `distill-${Date.now()}`,
|
|
627
|
+
dryRun: false,
|
|
628
|
+
signal: options.signal,
|
|
629
|
+
}).withFreshAssetMemo();
|
|
630
|
+
const { assetContent, existingRefVocabulary } = await loadAndScoreInputSalience({
|
|
631
|
+
inputRef,
|
|
632
|
+
durableInputRef,
|
|
633
|
+
salienceWriteKey,
|
|
634
|
+
stash,
|
|
635
|
+
config,
|
|
636
|
+
outcomeWeightEnabled,
|
|
637
|
+
lookup,
|
|
638
|
+
ctx: assetCtx,
|
|
639
|
+
});
|
|
640
|
+
const { filteredEvents, exclusionSet, filteredFeedbackCount, feedbackFullyFiltered } = readDistillFeedback({
|
|
641
|
+
readEventsImpl,
|
|
642
|
+
options,
|
|
643
|
+
durableInputRef,
|
|
575
644
|
});
|
|
576
|
-
// #267 — feedback exclusion. Filter events whose `ref` matches the
|
|
577
|
-
// exclusion list BEFORE the prompt is built. The original event stream
|
|
578
|
-
// is never mutated; only the `feedback` slice that reaches the LLM is
|
|
579
|
-
// affected. The exclusion set is normalised through `parseAssetRef` →
|
|
580
|
-
// re-serialised so callers can pass canonical or origin-prefixed refs
|
|
581
|
-
// and the comparison still works against the event payload's `ref`.
|
|
582
|
-
const exclusionList = options.excludeFeedbackFromRefs ?? [];
|
|
583
|
-
const exclusionSet = new Set(exclusionList.map((ref) => ref.trim()).filter((ref) => ref.length > 0));
|
|
584
|
-
const originalEventCount = events.length;
|
|
585
|
-
const filteredEvents = exclusionSet.size > 0 ? events.filter((e) => !(e.ref !== undefined && exclusionSet.has(e.ref))) : events;
|
|
586
|
-
const filteredFeedbackCount = originalEventCount - filteredEvents.length;
|
|
587
|
-
const feedbackFullyFiltered = exclusionSet.size > 0 && originalEventCount > 0 && filteredEvents.length === 0;
|
|
588
645
|
const feedback = filteredEvents.slice(-20).map((e) => ({
|
|
589
646
|
ts: e.ts,
|
|
590
647
|
eventType: e.eventType,
|
|
@@ -599,9 +656,14 @@ export async function akmDistill(options) {
|
|
|
599
656
|
const promotionResult = await promoteMemoryToKnowledge({
|
|
600
657
|
targetKind,
|
|
601
658
|
inputRef,
|
|
659
|
+
durableInputRef,
|
|
660
|
+
...(options.itemRef ? { itemRef: options.itemRef } : {}),
|
|
602
661
|
assetContent,
|
|
603
662
|
filteredEvents,
|
|
604
663
|
config,
|
|
664
|
+
strategy: options.improveProfile,
|
|
665
|
+
llmConfig: distillLlm,
|
|
666
|
+
signal: options.signal,
|
|
605
667
|
chat,
|
|
606
668
|
stash,
|
|
607
669
|
lookup,
|
|
@@ -612,6 +674,7 @@ export async function akmDistill(options) {
|
|
|
612
674
|
eligibilitySource: options.eligibilitySource,
|
|
613
675
|
sourceRun: options.sourceRun,
|
|
614
676
|
proposalsCtx: options.ctx,
|
|
677
|
+
eventsCtx: options.eventsCtx,
|
|
615
678
|
exclusionSetSize: exclusionSet.size,
|
|
616
679
|
filteredFeedbackCount,
|
|
617
680
|
feedbackFullyFiltered,
|
|
@@ -620,143 +683,226 @@ export async function akmDistill(options) {
|
|
|
620
683
|
return promotionResult;
|
|
621
684
|
const effectiveProposalKind = targetKind === "knowledge" ? "knowledge" : "lesson";
|
|
622
685
|
const effectiveLessonRef = effectiveProposalKind === "knowledge" ? deriveKnowledgeRef(inputRef) : deriveLessonRef(inputRef);
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const rejectedForRef = listProposals(stash, { ref: inputRef, status: "rejected", includeArchive: true })
|
|
627
|
-
.sort((a, b) => new Date(b.updatedAt ?? 0).getTime() - new Date(a.updatedAt ?? 0).getTime())
|
|
628
|
-
.slice(0, MAX_REJECTED_PROPOSALS)
|
|
629
|
-
.map((p) => ({
|
|
630
|
-
reason: p.review?.reason ?? "no reason given",
|
|
631
|
-
contentPreview: p.payload.content.slice(0, 500),
|
|
632
|
-
}));
|
|
633
|
-
// WS-3b CLS interleaving (step 9).
|
|
634
|
-
// When cls.enabled, inject embedding-retrieved adjacent lessons/knowledge
|
|
635
|
-
// into the distill prompt so the LLM avoids overwriting prior generalizations
|
|
636
|
-
// (catastrophic interference). DEFAULT OFF.
|
|
637
|
-
const clsConfig = getImproveProcessConfig(config, "distill", options.improveProfile)?.cls ?? {};
|
|
638
|
-
let clsContext = "";
|
|
639
|
-
if (clsConfig.enabled) {
|
|
640
|
-
try {
|
|
641
|
-
const adjacentCount = clsConfig.adjacentCount ?? 3;
|
|
642
|
-
// Use the asset content or input ref as the query for adjacent retrieval.
|
|
643
|
-
const clsQuery = assetContent ? assetContent.slice(0, 500) : inputRef;
|
|
644
|
-
const adjacentItems = await fetchSimilarLessonsFn(clsQuery, adjacentCount);
|
|
645
|
-
clsContext = buildClsContext(adjacentItems, clsConfig);
|
|
646
|
-
}
|
|
647
|
-
catch {
|
|
648
|
-
// Fail open — CLS is supplemental, never required.
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
// Distill output is a lesson/knowledge (non-wiki) → stash authoring
|
|
652
|
-
// standards. Resolved once for this single call.
|
|
653
|
-
const standardsContext = resolveStashStandards(stash);
|
|
654
|
-
const baseUserPrompt = buildDistillPrompt({
|
|
686
|
+
const messages = await buildDistillMessages({
|
|
687
|
+
options,
|
|
688
|
+
stash,
|
|
655
689
|
inputRef,
|
|
656
690
|
assetContent,
|
|
657
691
|
feedback,
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
692
|
+
effectiveProposalKind,
|
|
693
|
+
effectiveLessonRef,
|
|
694
|
+
fetchSimilarLessonsFn,
|
|
661
695
|
});
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
696
|
+
const { raw, fallbackReason } = await runDistillLlmCall({
|
|
697
|
+
config,
|
|
698
|
+
options,
|
|
699
|
+
distillLlm,
|
|
700
|
+
messages,
|
|
701
|
+
effectiveProposalKind,
|
|
702
|
+
});
|
|
703
|
+
if (raw === null || raw.trim() === "") {
|
|
704
|
+
return distillEmptyResponseResult({
|
|
705
|
+
fallbackReason,
|
|
706
|
+
inputRef,
|
|
707
|
+
durableInputRef,
|
|
708
|
+
...(options.itemRef ? { itemRef: options.itemRef } : {}),
|
|
709
|
+
effectiveLessonRef,
|
|
710
|
+
effectiveProposalKind,
|
|
711
|
+
exclusionSet,
|
|
712
|
+
filteredFeedbackCount,
|
|
713
|
+
feedbackFullyFiltered,
|
|
714
|
+
eligMeta,
|
|
715
|
+
eventsCtx: options.eventsCtx,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
const { content, descriptionSwapped } = assembleAndValidateDistillContent({
|
|
719
|
+
raw,
|
|
720
|
+
effectiveProposalKind,
|
|
721
|
+
inputRef,
|
|
722
|
+
durableInputRef,
|
|
723
|
+
...(options.itemRef ? { itemRef: options.itemRef } : {}),
|
|
724
|
+
effectiveLessonRef,
|
|
725
|
+
exclusionSet,
|
|
726
|
+
filteredFeedbackCount,
|
|
727
|
+
eligMeta,
|
|
728
|
+
eventsCtx: options.eventsCtx,
|
|
729
|
+
});
|
|
730
|
+
const gate = await applyDistillQualityGate({
|
|
731
|
+
config,
|
|
732
|
+
options,
|
|
733
|
+
content,
|
|
734
|
+
assetContent,
|
|
735
|
+
chat,
|
|
736
|
+
distillLlm,
|
|
737
|
+
fetchSimilarLessonsFn,
|
|
738
|
+
stash,
|
|
739
|
+
inputRef,
|
|
740
|
+
effectiveLessonRef,
|
|
741
|
+
exclusionSet,
|
|
742
|
+
filteredFeedbackCount,
|
|
743
|
+
feedbackFullyFiltered,
|
|
744
|
+
});
|
|
745
|
+
if ("rejection" in gate)
|
|
746
|
+
return gate.rejection;
|
|
747
|
+
const lessonJudgeConfidence = gate.confidence;
|
|
748
|
+
return emitDistillLessonProposal({
|
|
749
|
+
content,
|
|
750
|
+
options,
|
|
751
|
+
distillLlm,
|
|
752
|
+
assetContent,
|
|
753
|
+
inputRef,
|
|
754
|
+
durableInputRef,
|
|
755
|
+
effectiveLessonRef,
|
|
756
|
+
effectiveProposalKind,
|
|
757
|
+
stash,
|
|
758
|
+
exclusionSet,
|
|
759
|
+
filteredFeedbackCount,
|
|
760
|
+
feedbackFullyFiltered,
|
|
761
|
+
lessonJudgeConfidence,
|
|
762
|
+
existingRefVocabulary,
|
|
763
|
+
outcomeWeightEnabled,
|
|
764
|
+
descriptionSwapped,
|
|
765
|
+
eligMeta,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
769
|
+
/**
|
|
770
|
+
* The distill-propose pass: the optional WS-3b distill→source fidelity check
|
|
771
|
+
* (routes contradictions to human review), provenance xref round-trip, proposal
|
|
772
|
+
* creation, and the queued/skipped `distill_invoked` emit + output salience
|
|
773
|
+
* scoring (G4). Extracted verbatim from `akmDistill`; every outcome shape and
|
|
774
|
+
* event is byte-identical.
|
|
775
|
+
*/
|
|
776
|
+
async function emitDistillLessonProposal(args) {
|
|
777
|
+
const { options, distillLlm, assetContent, inputRef, durableInputRef, effectiveLessonRef, effectiveProposalKind, stash, exclusionSet, filteredFeedbackCount, feedbackFullyFiltered, lessonJudgeConfidence, existingRefVocabulary, outcomeWeightEnabled, descriptionSwapped, eligMeta, } = args;
|
|
778
|
+
let content = args.content;
|
|
779
|
+
// WS-3b: Distill→source fidelity check (step 10).
|
|
780
|
+
// When fidelityCheck.enabled, check the distill proposal against its cited
|
|
781
|
+
// source memories. A contradiction flag routes to human review (not auto-accept).
|
|
782
|
+
// DEFAULT OFF. Fail-open: any error is treated as no-contradiction.
|
|
783
|
+
const fidelityConfig = getImproveProcessConfig("distill", options.improveProfile)?.fidelityCheck ??
|
|
784
|
+
{};
|
|
785
|
+
if (fidelityConfig.enabled && assetContent) {
|
|
786
|
+
try {
|
|
787
|
+
const proposalBody = stripBodyForFidelity(content);
|
|
788
|
+
const sourceBodies = [stripBodyForFidelity(assetContent)];
|
|
789
|
+
const fidelityResult = checkDistillFidelity(proposalBody, sourceBodies, fidelityConfig);
|
|
790
|
+
if (fidelityResult.contradictionDetected) {
|
|
791
|
+
// Route to human review by writing a quality rejection with reviewNeeded=true.
|
|
792
|
+
return writeQualityRejection(stash, inputRef, effectiveLessonRef, content, 2.0, // below auto-accept threshold, signals review needed
|
|
793
|
+
fidelityResult.reason ?? "Proposal may contradict cited source memories.", {
|
|
794
|
+
reviewNeeded: true,
|
|
795
|
+
fidelityContradiction: true,
|
|
796
|
+
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
797
|
+
}, options.eligibilitySource, options.eventsCtx);
|
|
798
|
+
}
|
|
693
799
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// ignore the option fall through to the prompt-contract markdown path.
|
|
697
|
-
if (options.chat === undefined) {
|
|
698
|
-
return chatCompletion(distillLlm, messages, { responseSchema: distillSchema });
|
|
800
|
+
catch {
|
|
801
|
+
// Fail open — fidelity check is supplemental.
|
|
699
802
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
803
|
+
}
|
|
804
|
+
// Round-trip the parsed frontmatter so the proposal carries it as a
|
|
805
|
+
// structured payload alongside the raw content (matches the shape used by
|
|
806
|
+
// other proposal sources).
|
|
807
|
+
//
|
|
808
|
+
// Serialize canonical provenance into the content that promotion writes.
|
|
809
|
+
const parsed = parseFrontmatter(content);
|
|
810
|
+
const existingXrefs = Array.isArray(parsed.data.xrefs) ? parsed.data.xrefs.map(String) : [];
|
|
811
|
+
const frontmatterWithXrefs = {
|
|
812
|
+
...parsed.data,
|
|
813
|
+
xrefs: [...new Set([...existingXrefs, durableInputRef])],
|
|
814
|
+
};
|
|
815
|
+
delete frontmatterWithXrefs.sources;
|
|
816
|
+
content = assembleAsset(frontmatterWithXrefs, parsed.content);
|
|
817
|
+
const proposalResult2 = emitProposal({ stashDir: stash, proposalsCtx: options.ctx }, {
|
|
818
|
+
ref: effectiveLessonRef,
|
|
819
|
+
// §23.6 fingerprint model-id term (WI-6.4). Uses the RESOLVED connection
|
|
820
|
+
// (profile/config fallback included), not the raw option — a standalone
|
|
821
|
+
// `akm distill` run must fingerprint under the model that actually
|
|
822
|
+
// generated the content, matching the promote-memory branch.
|
|
823
|
+
...(distillLlm?.model ? { modelId: distillLlm.model } : {}),
|
|
824
|
+
source: "distill",
|
|
825
|
+
...(options.sourceRun !== undefined ? { sourceRun: options.sourceRun } : {}),
|
|
826
|
+
payload: {
|
|
827
|
+
content,
|
|
828
|
+
frontmatter: frontmatterWithXrefs,
|
|
709
829
|
},
|
|
830
|
+
...(lessonJudgeConfidence !== undefined ? { confidence: lessonJudgeConfidence } : {}),
|
|
831
|
+
// Attribution tagging: persist the eligibility lane on the proposal.
|
|
832
|
+
...(options.eligibilitySource ? { eligibilitySource: options.eligibilitySource } : {}),
|
|
710
833
|
});
|
|
711
|
-
if (
|
|
712
|
-
// Distinguish "config gate disabled" from "LLM call failed". For the
|
|
713
|
-
// config-disabled branch, we ALSO suppress the `distill_invoked` event
|
|
714
|
-
// because no LLM work was actually invoked — emitting the event causes
|
|
715
|
-
// the planner to accumulate phantom invocations that drown out real
|
|
716
|
-
// signal.
|
|
717
|
-
if (fallbackReason === "disabled") {
|
|
718
|
-
return {
|
|
719
|
-
schemaVersion: 1,
|
|
720
|
-
ok: true,
|
|
721
|
-
outcome: "config_disabled",
|
|
722
|
-
inputRef,
|
|
723
|
-
lessonRef: effectiveLessonRef,
|
|
724
|
-
proposalRef: effectiveLessonRef,
|
|
725
|
-
proposalKind: effectiveProposalKind,
|
|
726
|
-
message: "distill is disabled in config; enable processes.distill.enabled to activate.",
|
|
727
|
-
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
// LLM was actually invoked but produced nothing usable (transport error,
|
|
731
|
-
// timeout, or empty/whitespace response). Emit the event so the failure
|
|
732
|
-
// is observable.
|
|
834
|
+
if (isProposalSkipped(proposalResult2)) {
|
|
733
835
|
appendEvent({
|
|
734
836
|
eventType: "distill_invoked",
|
|
735
|
-
|
|
837
|
+
// Use item_ref when resolved, otherwise the input conceptId.
|
|
838
|
+
ref: options.itemRef ?? durableInputRef,
|
|
736
839
|
metadata: {
|
|
737
|
-
outcome: "
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
840
|
+
outcome: "skipped",
|
|
841
|
+
proposalRef: effectiveLessonRef,
|
|
842
|
+
message: proposalResult2.message,
|
|
843
|
+
skipReason: proposalResult2.reason,
|
|
741
844
|
...eligMeta,
|
|
742
845
|
},
|
|
743
|
-
});
|
|
846
|
+
}, options.eventsCtx);
|
|
744
847
|
return {
|
|
745
848
|
schemaVersion: 1,
|
|
746
849
|
ok: true,
|
|
747
|
-
outcome: "
|
|
850
|
+
outcome: "skipped",
|
|
748
851
|
inputRef,
|
|
749
|
-
lessonRef: effectiveLessonRef,
|
|
750
852
|
proposalRef: effectiveLessonRef,
|
|
751
|
-
|
|
752
|
-
message:
|
|
753
|
-
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
853
|
+
skipReason: proposalResult2.reason,
|
|
854
|
+
message: proposalResult2.message,
|
|
754
855
|
};
|
|
755
856
|
}
|
|
857
|
+
const proposal2 = proposalResult2;
|
|
858
|
+
// G4: content-score the distilled OUTPUT so it carries a real encoding
|
|
859
|
+
// salience (encoding_source='content') from creation — lessons never get
|
|
860
|
+
// another chance (they are refused as distill inputs).
|
|
861
|
+
persistOutputEncodingSalience(durableImproveRef(effectiveLessonRef), content, existingRefVocabulary, outcomeWeightEnabled);
|
|
862
|
+
appendEvent({
|
|
863
|
+
eventType: "distill_invoked",
|
|
864
|
+
// Use item_ref when resolved, otherwise the input conceptId.
|
|
865
|
+
ref: options.itemRef ?? durableInputRef,
|
|
866
|
+
metadata: {
|
|
867
|
+
outcome: "queued",
|
|
868
|
+
proposalRef: effectiveLessonRef,
|
|
869
|
+
proposalKind: effectiveProposalKind,
|
|
870
|
+
proposalId: proposal2.id,
|
|
871
|
+
// R3: judge verdicts are longitudinally queryable, not just a one-shot
|
|
872
|
+
// proposal.confidence write (normalized 1–5 score / 5).
|
|
873
|
+
...(lessonJudgeConfidence !== undefined ? { judgeConfidence: lessonJudgeConfidence } : {}),
|
|
874
|
+
...(options.sourceRun !== undefined ? { sourceRun: options.sourceRun } : {}),
|
|
875
|
+
...(exclusionSet.size > 0 ? { filteredFeedbackCount } : {}),
|
|
876
|
+
...(descriptionSwapped > 0 ? { descriptionSwapped } : {}),
|
|
877
|
+
...eligMeta,
|
|
878
|
+
},
|
|
879
|
+
}, options.eventsCtx);
|
|
880
|
+
return {
|
|
881
|
+
schemaVersion: 1,
|
|
882
|
+
ok: true,
|
|
883
|
+
outcome: "queued",
|
|
884
|
+
inputRef,
|
|
885
|
+
proposalRef: effectiveLessonRef,
|
|
886
|
+
proposalKind: effectiveProposalKind,
|
|
887
|
+
proposalId: proposal2.id,
|
|
888
|
+
proposal: proposal2,
|
|
889
|
+
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
890
|
+
...(descriptionSwapped > 0 ? { descriptionSwapped } : {}),
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Turn the raw LLM response into validated proposal content: prefer the
|
|
895
|
+
* structured-JSON assembly, else strip markdown fences; run the lesson-only
|
|
896
|
+
* auto-repair chain (frontmatter repair, description↔when_to_use swap, truncation
|
|
897
|
+
* repair); then lint/validate, emitting `distill_invoked(validation_failed)` and
|
|
898
|
+
* throwing a `UsageError` on any finding. Extracted verbatim from `akmDistill`.
|
|
899
|
+
*/
|
|
900
|
+
function assembleAndValidateDistillContent(args) {
|
|
901
|
+
const { raw, effectiveProposalKind, inputRef, durableInputRef, itemRef, effectiveLessonRef, exclusionSet, filteredFeedbackCount, eligMeta, eventsCtx, } = args;
|
|
756
902
|
// Structured-output path: when the provider honoured the JSON schema, `raw`
|
|
757
903
|
// is a JSON object string (not a markdown blob). Try to parse it and assemble
|
|
758
|
-
// the canonical `---\nfm\n---\n\nbody` form before
|
|
759
|
-
//
|
|
904
|
+
// the canonical `---\nfm\n---\n\nbody` form before using the markdown
|
|
905
|
+
// response path. Failure here (non-JSON response, missing
|
|
760
906
|
// required field, unexpected types) is non-fatal — we drop down to the
|
|
761
907
|
// markdown path which has its own auto-repair + lint pass.
|
|
762
908
|
let content;
|
|
@@ -801,156 +947,254 @@ export async function akmDistill(options) {
|
|
|
801
947
|
if (findings.length > 0) {
|
|
802
948
|
appendEvent({
|
|
803
949
|
eventType: "distill_invoked",
|
|
804
|
-
|
|
950
|
+
// Use item_ref when resolved, otherwise the input conceptId.
|
|
951
|
+
ref: itemRef ?? durableInputRef,
|
|
805
952
|
metadata: {
|
|
806
953
|
outcome: "validation_failed",
|
|
807
|
-
|
|
954
|
+
proposalRef: effectiveLessonRef,
|
|
808
955
|
proposalKind: effectiveProposalKind,
|
|
809
956
|
findingKinds: findings.map((f) => f.kind),
|
|
810
957
|
...(exclusionSet.size > 0 ? { filteredFeedbackCount } : {}),
|
|
811
958
|
...eligMeta,
|
|
812
959
|
},
|
|
813
|
-
});
|
|
960
|
+
}, eventsCtx);
|
|
814
961
|
const message = findings.map((f) => f.message).join("\n");
|
|
815
962
|
throw new UsageError(`Distilled ${effectiveProposalKind} failed validation:\n${message}`, "MISSING_REQUIRED_ARGUMENT", effectiveProposalKind === "knowledge"
|
|
816
963
|
? "Knowledge proposals require a non-empty markdown body."
|
|
817
964
|
: "Lessons require non-empty `description` and `when_to_use` frontmatter fields. See v1 spec §13.");
|
|
818
965
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
// When fidelityCheck.enabled, check the distill proposal against its cited
|
|
848
|
-
// source memories. A contradiction flag routes to human review (not auto-accept).
|
|
849
|
-
// DEFAULT OFF. Fail-open: any error is treated as no-contradiction.
|
|
850
|
-
const fidelityConfig = getImproveProcessConfig(config, "distill", options.improveProfile)?.fidelityCheck ?? {};
|
|
851
|
-
if (fidelityConfig.enabled && assetContent) {
|
|
852
|
-
try {
|
|
853
|
-
const proposalBody = stripBodyForFidelity(content);
|
|
854
|
-
const sourceBodies = [stripBodyForFidelity(assetContent)];
|
|
855
|
-
const fidelityResult = checkDistillFidelity(proposalBody, sourceBodies, fidelityConfig);
|
|
856
|
-
if (fidelityResult.contradictionDetected) {
|
|
857
|
-
// Route to human review by writing a quality rejection with reviewNeeded=true.
|
|
858
|
-
return writeQualityRejection(stash, inputRef, effectiveLessonRef, content, 2.0, // below auto-accept threshold, signals review needed
|
|
859
|
-
fidelityResult.reason ?? "Proposal may contradict cited source memories.", {
|
|
860
|
-
reviewNeeded: true,
|
|
861
|
-
fidelityContradiction: true,
|
|
862
|
-
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
863
|
-
}, options.eligibilitySource);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
catch {
|
|
867
|
-
// Fail open — fidelity check is supplemental.
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
// Round-trip the parsed frontmatter so the proposal carries it as a
|
|
871
|
-
// structured payload alongside the raw content (matches the shape used by
|
|
872
|
-
// other proposal sources).
|
|
873
|
-
//
|
|
874
|
-
// D-7 / #398: Inject `sources: [inputRef]` into the LLM-path proposal
|
|
875
|
-
// frontmatter when the field is absent, providing reviewers with provenance
|
|
876
|
-
// without requiring them to open event history. A-MEM arXiv:2502.12110 —
|
|
877
|
-
// all notes carry explicit provenance links.
|
|
878
|
-
const parsed = parseFrontmatter(content);
|
|
879
|
-
const frontmatterWithSources = { ...parsed.data };
|
|
880
|
-
if (!Array.isArray(frontmatterWithSources.sources) || frontmatterWithSources.sources.length === 0) {
|
|
881
|
-
frontmatterWithSources.sources = [inputRef];
|
|
966
|
+
return { content, descriptionSwapped };
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* The single bounded distill LLM call: gate-checked via `callStructured`
|
|
970
|
+
* (R26 migration off the raw `chatCompletion` scaffold), passing the
|
|
971
|
+
* lesson/knowledge JSON schema on the production path and keeping the
|
|
972
|
+
* injected test fake schema-blind. Returns the raw response (or `null`) and
|
|
973
|
+
* the fallback reason.
|
|
974
|
+
*/
|
|
975
|
+
async function runDistillLlmCall(args) {
|
|
976
|
+
const { config, options, distillLlm, messages, effectiveProposalKind } = args;
|
|
977
|
+
const distillSchema = effectiveProposalKind === "knowledge" ? DISTILL_KNOWLEDGE_JSON_SCHEMA : DISTILL_LESSON_JSON_SCHEMA;
|
|
978
|
+
let fallbackReason;
|
|
979
|
+
const enabled = resolveProcessEnabled("distill", options.improveProfile ?? resolveImproveStrategy(undefined, config).config);
|
|
980
|
+
const recordFallback = (feature, reason) => {
|
|
981
|
+
fallbackReason = reason;
|
|
982
|
+
// Log the fallback reason; the caller (raw === null path) handles
|
|
983
|
+
// emitting the distill_invoked event so we don't double-emit here.
|
|
984
|
+
warnVerbose(`[akm] LLM fallback for ${feature}: ${reason}`);
|
|
985
|
+
};
|
|
986
|
+
if (enabled && !distillLlm) {
|
|
987
|
+
// No LLM connection configured. At HEAD this threw a ConfigError inside
|
|
988
|
+
// the gated fn and tryLlmFeature routed it through the "error" fallback;
|
|
989
|
+
// reproduce that terminal state directly (the gate-disabled case above
|
|
990
|
+
// still dominates: when disabled, callStructured takes the "disabled"
|
|
991
|
+
// fallback before any LLM lookup, exactly as before).
|
|
992
|
+
recordFallback("distill", "error");
|
|
993
|
+
return { raw: null, fallbackReason };
|
|
882
994
|
}
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
995
|
+
const raw = await callStructured({
|
|
996
|
+
feature: "distill",
|
|
997
|
+
akmConfig: config,
|
|
998
|
+
enabled,
|
|
999
|
+
// Safe: when the gate is open, distillLlm is defined (guard above); when
|
|
1000
|
+
// it is closed, the transport never runs and config is never read.
|
|
1001
|
+
config: distillLlm,
|
|
1002
|
+
messages,
|
|
1003
|
+
request: options.chat === undefined
|
|
1004
|
+
? // Production path: pass the JSON schema so providers that honour
|
|
1005
|
+
// `response_format: json_schema` enforce shape upstream. Providers
|
|
1006
|
+
// that ignore the option fall through to the prompt-contract
|
|
1007
|
+
// markdown path.
|
|
1008
|
+
{ responseSchema: distillSchema }
|
|
1009
|
+
: // Test seam: keep the injected fake as the transport; fakes never
|
|
1010
|
+
// see the schema (they return markdown strings).
|
|
1011
|
+
{ chat: options.chat, ...(options.signal ? { signal: options.signal } : {}) },
|
|
1012
|
+
parse: (raw) => raw ?? null,
|
|
1013
|
+
onError: (_cls, err) => {
|
|
1014
|
+
// At HEAD a transport throw escaped to tryLlmFeature's catch, which
|
|
1015
|
+
// fired onFallback("error"); reproduce that observable state.
|
|
1016
|
+
void err;
|
|
1017
|
+
recordFallback("distill", "error");
|
|
1018
|
+
return null;
|
|
890
1019
|
},
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1020
|
+
fallback: null,
|
|
1021
|
+
onFallback: (evt) => recordFallback(evt.feature, evt.reason),
|
|
1022
|
+
});
|
|
1023
|
+
return { raw, fallbackReason };
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Build the terminal result for an empty/failed distill LLM response,
|
|
1027
|
+
* distinguishing the config-gate-off branch (event suppressed) from a real
|
|
1028
|
+
* transport/timeout/empty failure (emits `distill_invoked(llm_failed)`).
|
|
1029
|
+
* Extracted verbatim from `akmDistill`.
|
|
1030
|
+
*/
|
|
1031
|
+
function distillEmptyResponseResult(args) {
|
|
1032
|
+
const { fallbackReason, inputRef, durableInputRef, itemRef, effectiveLessonRef, effectiveProposalKind, exclusionSet, filteredFeedbackCount, feedbackFullyFiltered, eligMeta, eventsCtx, } = args;
|
|
1033
|
+
// Distinguish "config gate disabled" from "LLM call failed". For the
|
|
1034
|
+
// config-disabled branch, we ALSO suppress the `distill_invoked` event
|
|
1035
|
+
// because no LLM work was actually invoked — emitting the event causes
|
|
1036
|
+
// the planner to accumulate phantom invocations that drown out real
|
|
1037
|
+
// signal.
|
|
1038
|
+
if (fallbackReason === "disabled") {
|
|
907
1039
|
return {
|
|
908
1040
|
schemaVersion: 1,
|
|
909
1041
|
ok: true,
|
|
910
|
-
outcome: "
|
|
1042
|
+
outcome: "config_disabled",
|
|
911
1043
|
inputRef,
|
|
912
|
-
|
|
913
|
-
|
|
1044
|
+
proposalRef: effectiveLessonRef,
|
|
1045
|
+
proposalKind: effectiveProposalKind,
|
|
1046
|
+
message: "distill is disabled in config; enable processes.distill.enabled to activate.",
|
|
1047
|
+
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
914
1048
|
};
|
|
915
1049
|
}
|
|
916
|
-
|
|
917
|
-
//
|
|
918
|
-
//
|
|
919
|
-
// another chance (they are refused as distill inputs).
|
|
920
|
-
persistOutputEncodingSalience(effectiveLessonRef, content, existingRefVocabulary, outcomeWeightEnabled);
|
|
1050
|
+
// LLM was actually invoked but produced nothing usable (transport error,
|
|
1051
|
+
// timeout, or empty/whitespace response). Emit the event so the failure
|
|
1052
|
+
// is observable.
|
|
921
1053
|
appendEvent({
|
|
922
1054
|
eventType: "distill_invoked",
|
|
923
|
-
|
|
1055
|
+
// Use item_ref when resolved, otherwise the input conceptId.
|
|
1056
|
+
ref: itemRef ?? durableInputRef,
|
|
924
1057
|
metadata: {
|
|
925
|
-
outcome: "
|
|
926
|
-
lessonRef: effectiveLessonRef,
|
|
1058
|
+
outcome: "llm_failed",
|
|
927
1059
|
proposalRef: effectiveLessonRef,
|
|
928
1060
|
proposalKind: effectiveProposalKind,
|
|
929
|
-
proposalId: proposal2.id,
|
|
930
|
-
// R3: judge verdicts are longitudinally queryable, not just a one-shot
|
|
931
|
-
// proposal.confidence write (normalized 1–5 score / 5).
|
|
932
|
-
...(lessonJudgeConfidence !== undefined ? { judgeConfidence: lessonJudgeConfidence } : {}),
|
|
933
|
-
...(options.sourceRun !== undefined ? { sourceRun: options.sourceRun } : {}),
|
|
934
1061
|
...(exclusionSet.size > 0 ? { filteredFeedbackCount } : {}),
|
|
935
|
-
...(descriptionSwapped > 0 ? { descriptionSwapped } : {}),
|
|
936
1062
|
...eligMeta,
|
|
937
1063
|
},
|
|
938
|
-
});
|
|
1064
|
+
}, eventsCtx);
|
|
939
1065
|
return {
|
|
940
1066
|
schemaVersion: 1,
|
|
941
1067
|
ok: true,
|
|
942
|
-
outcome: "
|
|
1068
|
+
outcome: "llm_failed",
|
|
943
1069
|
inputRef,
|
|
944
|
-
lessonRef: effectiveLessonRef,
|
|
945
1070
|
proposalRef: effectiveLessonRef,
|
|
946
1071
|
proposalKind: effectiveProposalKind,
|
|
947
|
-
|
|
948
|
-
proposal: proposal2,
|
|
1072
|
+
message: "LLM call returned no usable output (timeout, empty, or error).",
|
|
949
1073
|
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
950
|
-
...(descriptionSwapped > 0 ? { descriptionSwapped } : {}),
|
|
951
1074
|
};
|
|
952
1075
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1076
|
+
/**
|
|
1077
|
+
* The P2-B LLM-as-judge quality gate (fail-CLOSED; D-5/#388 three-band). Returns
|
|
1078
|
+
* a terminal rejection result when the judge rejects (or routes to review), or
|
|
1079
|
+
* the normalized [0,1] confidence to carry onto the proposal. Extracted verbatim
|
|
1080
|
+
* from `akmDistill`.
|
|
1081
|
+
*/
|
|
1082
|
+
async function applyDistillQualityGate(args) {
|
|
1083
|
+
const { config, options, content, assetContent, chat, distillLlm, fetchSimilarLessonsFn, stash, inputRef, effectiveLessonRef, exclusionSet, filteredFeedbackCount, feedbackFullyFiltered, } = args;
|
|
1084
|
+
if (!(options.improveProfile?.processes?.distill?.qualityGate?.enabled ?? true)) {
|
|
1085
|
+
return { confidence: undefined };
|
|
1086
|
+
}
|
|
1087
|
+
// D-4 / #390: retrieve top-3 similar lessons for dedup check in judge.
|
|
1088
|
+
const similarLessons = await fetchSimilarLessonsFn(content.slice(0, 500), 3);
|
|
1089
|
+
const judgeResult = await runLessonQualityJudge(config, content, assetContent ?? "", chat, {
|
|
1090
|
+
...(similarLessons.length > 0 ? { similarLessons } : {}),
|
|
1091
|
+
...(distillLlm ? { llmConfig: distillLlm } : {}),
|
|
1092
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1093
|
+
});
|
|
1094
|
+
if (!judgeResult.pass) {
|
|
1095
|
+
if (judgeResult.reviewNeeded) {
|
|
1096
|
+
return {
|
|
1097
|
+
rejection: writeQualityRejection(stash, inputRef, effectiveLessonRef, content, judgeResult.score, judgeResult.reason, {
|
|
1098
|
+
reviewNeeded: true,
|
|
1099
|
+
...(exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}),
|
|
1100
|
+
}, options.eligibilitySource, options.eventsCtx),
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
return {
|
|
1104
|
+
rejection: writeQualityRejection(stash, inputRef, effectiveLessonRef, content, judgeResult.score, judgeResult.reason, exclusionSet.size > 0 ? { filteredFeedbackCount, feedbackFullyFiltered } : {}, options.eligibilitySource, options.eventsCtx),
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
// Normalize 1-5 judge score to [0, 1]. Only a real passing verdict
|
|
1108
|
+
// reaches here (07 P0-2: the judge now fails CLOSED on no-LLM / timeout /
|
|
1109
|
+
// parse failure, so those return pass:false and never fall through to
|
|
1110
|
+
// this line). A defensive score>0 guard keeps confidence undefined for any
|
|
1111
|
+
// non-positive score the auto-accept gate should treat as unscored.
|
|
1112
|
+
return { confidence: judgeResult.score > 0 ? judgeResult.score / 5 : undefined };
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Read the target ref's `feedback` events and apply the #267 exclusion filter.
|
|
1116
|
+
* Returns the filtered events plus the exclusion tallies the outcome branches
|
|
1117
|
+
* carry. Extracted verbatim from `akmDistill`.
|
|
1118
|
+
*/
|
|
1119
|
+
function readDistillFeedback(args) {
|
|
1120
|
+
const { readEventsImpl, options, durableInputRef } = args;
|
|
1121
|
+
const { events: unfilteredEvents } = readEventsImpl({
|
|
1122
|
+
ref: options.itemRef ?? durableInputRef,
|
|
1123
|
+
type: "feedback",
|
|
1124
|
+
excludeTags: options.excludeTags,
|
|
1125
|
+
includeTags: options.includeTags,
|
|
1126
|
+
});
|
|
1127
|
+
const events = unfilteredEvents;
|
|
1128
|
+
// #267 — feedback exclusion. Filter events whose `ref` matches the
|
|
1129
|
+
// exclusion list BEFORE the prompt is built. The original event stream
|
|
1130
|
+
// is never mutated; only the `feedback` slice that reaches the LLM is
|
|
1131
|
+
// affected. Exclusion refs are compared with event refs exactly.
|
|
1132
|
+
const exclusionList = options.excludeFeedbackFromRefs ?? [];
|
|
1133
|
+
const exclusionSet = new Set(exclusionList.map((ref) => ref.trim()).filter((ref) => ref.length > 0));
|
|
1134
|
+
const originalEventCount = events.length;
|
|
1135
|
+
const filteredEvents = exclusionSet.size > 0 ? events.filter((e) => !(e.ref !== undefined && exclusionSet.has(e.ref))) : events;
|
|
1136
|
+
const filteredFeedbackCount = originalEventCount - filteredEvents.length;
|
|
1137
|
+
const feedbackFullyFiltered = exclusionSet.size > 0 && originalEventCount > 0 && filteredEvents.length === 0;
|
|
1138
|
+
return { filteredEvents, exclusionSet, filteredFeedbackCount, feedbackFullyFiltered };
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Build the distill chat messages: inject the last 1–3 rejected proposals
|
|
1142
|
+
* (Reflexion verbal-RL), the optional WS-3b CLS adjacent-context, and the stash
|
|
1143
|
+
* authoring standards, then assemble the system+user prompt. Extracted verbatim
|
|
1144
|
+
* from `akmDistill`.
|
|
1145
|
+
*/
|
|
1146
|
+
async function buildDistillMessages(args) {
|
|
1147
|
+
const { options, stash, inputRef, assetContent, feedback, effectiveProposalKind, effectiveLessonRef, fetchSimilarLessonsFn, } = args;
|
|
1148
|
+
// Inject last 1–3 rejected proposals for this ref as Reflexion-style
|
|
1149
|
+
// verbal-RL context so the LLM avoids regenerating refused proposals.
|
|
1150
|
+
const rejectedForRef = listProposals(stash, { ref: inputRef, status: "rejected", includeArchive: true })
|
|
1151
|
+
.sort((a, b) => new Date(b.updatedAt ?? 0).getTime() - new Date(a.updatedAt ?? 0).getTime())
|
|
1152
|
+
.slice(0, MAX_REJECTED_PROPOSALS)
|
|
1153
|
+
.map((p) => ({
|
|
1154
|
+
reason: p.review?.reason ?? "no reason given",
|
|
1155
|
+
contentPreview: proposalContent(p).slice(0, 500),
|
|
1156
|
+
}));
|
|
1157
|
+
// WS-3b CLS interleaving (step 9).
|
|
1158
|
+
// When cls.enabled, inject embedding-retrieved adjacent lessons/knowledge
|
|
1159
|
+
// into the distill prompt so the LLM avoids overwriting prior generalizations
|
|
1160
|
+
// (catastrophic interference). DEFAULT OFF.
|
|
1161
|
+
const clsConfig = getImproveProcessConfig("distill", options.improveProfile)?.cls ?? {};
|
|
1162
|
+
let clsContext = "";
|
|
1163
|
+
if (clsConfig.enabled) {
|
|
1164
|
+
try {
|
|
1165
|
+
const adjacentCount = clsConfig.adjacentCount ?? 3;
|
|
1166
|
+
// Use the asset content or input ref as the query for adjacent retrieval.
|
|
1167
|
+
const clsQuery = assetContent ? assetContent.slice(0, 500) : inputRef;
|
|
1168
|
+
const adjacentItems = await fetchSimilarLessonsFn(clsQuery, adjacentCount);
|
|
1169
|
+
clsContext = buildClsContext(adjacentItems, clsConfig);
|
|
1170
|
+
}
|
|
1171
|
+
catch {
|
|
1172
|
+
// Fail open — CLS is supplemental, never required.
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
// Distill output is a lesson/knowledge (non-wiki) → stash authoring
|
|
1176
|
+
// standards. Resolved once for this single call.
|
|
1177
|
+
const standardsContext = resolveStandardsContext(effectiveLessonRef, stash);
|
|
1178
|
+
const baseUserPrompt = buildDistillPrompt({
|
|
1179
|
+
inputRef,
|
|
1180
|
+
assetContent,
|
|
1181
|
+
feedback,
|
|
1182
|
+
proposalKind: effectiveProposalKind,
|
|
1183
|
+
...(rejectedForRef.length > 0 ? { rejectedProposals: rejectedForRef } : {}),
|
|
1184
|
+
...(standardsContext.trim() ? { standardsContext } : {}),
|
|
1185
|
+
});
|
|
1186
|
+
const userPrompt = clsContext ? `${baseUserPrompt}${clsContext}` : baseUserPrompt;
|
|
1187
|
+
return [
|
|
1188
|
+
{ role: "system", content: effectiveProposalKind === "knowledge" ? KNOWLEDGE_SYSTEM_PROMPT : LESSON_SYSTEM_PROMPT },
|
|
1189
|
+
{ role: "user", content: userPrompt },
|
|
1190
|
+
];
|
|
1191
|
+
}
|
|
1192
|
+
async function defaultLookup(ref, stashDir) {
|
|
1193
|
+
return resolveAssetPath(ref, {
|
|
1194
|
+
stashDir,
|
|
1195
|
+
mode: "disk-only",
|
|
1196
|
+
directoryIndexNames: ["SKILL.md"],
|
|
1197
|
+
preserveDirectNameFallback: true,
|
|
1198
|
+
honorOrigin: false,
|
|
1199
|
+
});
|
|
956
1200
|
}
|