akm-cli 0.9.30 → 0.10.261001-beta
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 +438 -0
- package/README.md +10 -10
- package/ROADMAP.md +112 -0
- package/STABILITY.md +99 -57
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +15 -29
- package/dist/assets/hints/cli-hints-short.md +2 -2
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/assets/stash-skeleton/README.md +1 -1
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/feedback-cli.js +75 -49
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/chunking.js +3 -10
- package/dist/commands/improve/consolidate/continuity-check.js +4 -4
- package/dist/commands/improve/consolidate/pair-pass.js +29 -10
- package/dist/commands/improve/consolidate.js +10 -65
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +148 -21
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/extract-prompt.js +17 -7
- package/dist/commands/improve/extract.js +5 -7
- package/dist/commands/improve/improve-cli.js +125 -118
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +134 -52
- package/dist/commands/improve/loop-stages.js +18 -5
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +29 -34
- package/dist/commands/improve/retrieval-queries.js +37 -0
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/session-asset.js +4 -1
- package/dist/commands/improve/stage.js +23 -12
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +43 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +20 -14
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +6 -14
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +10 -63
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/file-lock.js +12 -7
- package/dist/core/improve-result.js +13 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/paths.js +4 -0
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/run-lock.js +1 -1
- package/dist/core/subprocess.js +49 -0
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/indexer.js +15 -6
- package/dist/indexer/passes/memory-inference.js +15 -6
- package/dist/indexer/search/db-search.js +49 -36
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/indexer/search/ranking.js +29 -1
- package/dist/indexer/usage/usage-events.js +21 -16
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +74 -14
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +15769 -16731
- package/dist/scripts/akm-migrate.js +16339 -17297
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/sources/providers/git-stash.js +94 -13
- package/dist/storage/repositories/index-utility-repository.js +2 -32
- package/dist/storage/repositories/index-vec-repository.js +4 -0
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/run-command-task.js +9 -1
- package/dist/tasks/run/run-workflow-task.js +4 -3
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/run/task-log.js +38 -1
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +43 -42
- package/docs/agents/README.md +15 -0
- package/docs/agents/agent-install.md +14 -0
- package/docs/architecture/0.10-process-rules.md +827 -0
- package/docs/architecture/README.md +67 -0
- package/docs/architecture/adapters.md +258 -0
- package/docs/architecture/akm-architecture-decision-history.md +2055 -0
- package/docs/architecture/akm-core-principles.md +126 -0
- package/docs/architecture/architecture.md +578 -0
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
- package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
- package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
- package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
- package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
- package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
- package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
- package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
- package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
- package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
- package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
- package/docs/architecture/decisions/README.md +62 -0
- package/docs/architecture/improvement.md +479 -0
- package/docs/architecture/internals/classification.md +154 -0
- package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
- package/docs/architecture/internals/functional-contract-patterns.md +340 -0
- package/docs/architecture/internals/health-advisories.md +110 -0
- package/docs/architecture/internals/improve-workflow.md +719 -0
- package/docs/architecture/internals/indexing.md +498 -0
- package/docs/architecture/internals/registry-network-boundary.md +57 -0
- package/docs/architecture/internals/search.md +248 -0
- package/docs/architecture/internals/storage-locations.md +943 -0
- package/docs/architecture/persisted-data-compat.md +68 -0
- package/docs/architecture/reviews/env-secret-access.md +319 -0
- package/docs/architecture/runtime-boundary-design.md +153 -0
- package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
- package/docs/architecture/specs/0.9.0-decisions.md +532 -0
- package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
- package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
- package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
- package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
- package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
- package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
- package/docs/architecture/specs/di-seams-plan.md +539 -0
- package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
- package/docs/architecture/specs/fact-asset-type.md +142 -0
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
- package/docs/architecture/specs/okf-support.md +148 -0
- package/docs/architecture/specs/ref.md +329 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
- package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
- package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
- package/docs/architecture/specs/stash-organization-conventions.md +377 -0
- package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
- package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
- package/docs/architecture/specs/workflow-format-unification.md +432 -0
- package/docs/architecture/testing/manual-testing-checklist.md +3411 -0
- package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
- package/docs/architecture/testing/task-workflow-manual-tests.md +1720 -0
- package/docs/architecture/testing/testing-workflow.md +446 -0
- package/docs/architecture/workflow-engine.md +223 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/author-bundles.md +310 -0
- package/docs/guides/author-workflows.md +490 -0
- package/docs/guides/bundles.md +323 -0
- package/docs/guides/capture-knowledge.md +136 -0
- package/docs/guides/claude-code-vs-akm-workflows.md +29 -0
- package/docs/guides/concepts.md +93 -0
- package/docs/guides/discover-and-load.md +158 -0
- package/docs/guides/getting-started.md +240 -0
- package/docs/guides/improve-the-library.md +247 -0
- package/docs/guides/knowledge-management.md +18 -0
- package/docs/guides/recipes/headless-install.md +97 -0
- package/docs/guides/recipes/website-source.md +28 -0
- package/docs/guides/run-workflows.md +279 -0
- package/docs/guides/scheduling.md +131 -0
- package/docs/guides/use-with-any-agent.md +174 -0
- package/docs/guides/wikis.md +81 -0
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/README.md +19 -0
- package/docs/maintainers/curate-workmap.md +335 -0
- package/docs/maintainers/eval.md +3 -0
- package/docs/maintainers/local-development.md +108 -0
- package/docs/maintainers/release-checklist.md +135 -0
- package/docs/migration/README.md +4 -2
- package/docs/migration/release-notes/0.0.13.md +1 -1
- package/docs/migration/release-notes/0.1.0.md +1 -1
- package/docs/migration/release-notes/0.10.0.md +201 -0
- package/docs/migration/release-notes/0.2.0.md +1 -1
- package/docs/migration/release-notes/0.3.0.md +1 -1
- package/docs/migration/release-notes/0.5.0.md +1 -1
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.3.md +2 -2
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +1 -1
- package/docs/migration/release-notes/0.8.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +1 -1
- package/docs/migration/release-notes/0.9.14.md +1 -1
- package/docs/migration/release-notes/0.9.15.md +1 -1
- package/docs/migration/release-notes/0.9.16.md +1 -1
- package/docs/migration/release-notes/0.9.17.md +1 -1
- package/docs/migration/release-notes/0.9.19.md +1 -1
- package/docs/migration/release-notes/0.9.2.md +1 -1
- package/docs/migration/release-notes/README.md +28 -31
- package/docs/migration/v0.5-to-v0.6.md +759 -0
- package/docs/migration/v0.8-to-v0.9.md +1 -1
- package/docs/migration/v0.9-to-v0.10.md +507 -0
- package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
- package/docs/posts/README.md +30 -0
- package/docs/posts/akm-command-tour.md +495 -0
- package/docs/posts/akm-logo-sized.webp +0 -0
- package/docs/posts/belief-aware-memory-12.md +139 -0
- package/docs/posts/config-v2-lessons-16.md +191 -0
- package/docs/posts/improve-loop-autonomous-10.md +225 -0
- package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
- package/docs/posts/intro-part-01.md +76 -0
- package/docs/posts/intro-part-02.md +221 -0
- package/docs/posts/intro-part-03.md +190 -0
- package/docs/posts/intro-part-04.md +221 -0
- package/docs/posts/llm-mode-reflect-13.md +166 -0
- package/docs/posts/openpalm-built-on-akm.md +86 -0
- package/docs/posts/progressive-disclosure-06.md +129 -0
- package/docs/posts/proposal-safety-net-15.md +112 -0
- package/docs/posts/release-0.5.0.md +247 -0
- package/docs/posts/release-0.6.0.md +117 -0
- package/docs/posts/release-0.7.0.md +159 -0
- package/docs/posts/release-0.8.0.md +332 -0
- package/docs/posts/scattered-skills-05.md +100 -0
- package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
- package/docs/posts/team-skills-07.md +198 -0
- package/docs/posts/wiki-agent-knowledge-08.md +223 -0
- package/docs/posts/workflows-vaults-09.md +321 -0
- package/docs/product-surface.md +54 -0
- package/docs/reference/README.md +2 -2
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +166 -0
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +163 -192
- package/docs/reference/configuration.md +101 -20
- package/docs/reference/data-and-telemetry.md +2 -2
- package/docs/reference/env-and-secrets.md +126 -0
- package/docs/reference/memory.md +124 -0
- package/docs/reference/refs.md +156 -0
- package/docs/reference/registry.md +489 -0
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +141 -0
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +23 -16
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/improve/retrieval-gate.js +0 -132
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/indexer/feedback/utility-policy.js +0 -75
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- package/dist/workflows/runtime/run-outputs.js +0 -80
|
@@ -21,15 +21,17 @@ import { getDbPath } from "../core/paths.js";
|
|
|
21
21
|
import { withStateDb } from "../core/state-db.js";
|
|
22
22
|
import { warn } from "../core/warn.js";
|
|
23
23
|
import { withWriteTargetMutation } from "../core/write-source.js";
|
|
24
|
+
import { recomputeUtilityScores } from "../indexer/indexer.js";
|
|
24
25
|
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
25
|
-
import {
|
|
26
|
+
import { insertUsageEvent, resolveUsageEventSource } from "../indexer/usage/usage-events.js";
|
|
26
27
|
import { resolveSourcesForOrigin } from "../registry/origin-resolve.js";
|
|
27
28
|
import { closeDatabase, openExistingDatabase } from "../storage/repositories/index-connection.js";
|
|
28
29
|
import { findEntryIdByRef, getEntryFilePathById, getItemRefById, } from "../storage/repositories/index-entries-repository.js";
|
|
29
|
-
import {
|
|
30
|
+
import { getUtilityScore } from "../storage/repositories/index-utility-repository.js";
|
|
30
31
|
import { contentHash } from "./improve/content-hash.js";
|
|
31
32
|
import { readEdgeList } from "./improve/memory/memory-belief.js";
|
|
32
|
-
import {
|
|
33
|
+
import { akmProposalAccept } from "./proposal/proposal.js";
|
|
34
|
+
import { createProposal, recordGateDecision, rejectProposalDurably } from "./proposal/repository.js";
|
|
33
35
|
// ── Tag validation ────────────────────────────────────────────────────────────
|
|
34
36
|
const TAG_KEY_RE = /^[a-z_][a-z0-9_]*$/;
|
|
35
37
|
function validateFeedbackTags(raw) {
|
|
@@ -144,26 +146,25 @@ function buildLessonStrengthUpdate(rawBytes, feedbackRef) {
|
|
|
144
146
|
const nextBytes = Buffer.from(next);
|
|
145
147
|
return rawBytes.equals(nextBytes) ? { strength: strengthList.length } : { strength: strengthList.length, nextBytes };
|
|
146
148
|
}
|
|
149
|
+
/** Utility at or above this is "high"; falling below it after feedback emits `improve_review_needed`. */
|
|
150
|
+
const UTILITY_REVIEW_THRESHOLD = 0.5;
|
|
147
151
|
/**
|
|
148
|
-
* Persist the feedback usage-event (state.db) and
|
|
149
|
-
* entry's
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* and
|
|
160
|
-
* caller rather than passing silently. Best-effort: a utility-update failure
|
|
161
|
-
* never fails the feedback record.
|
|
152
|
+
* Persist the feedback usage-event (state.db) and, for a user-attributed event,
|
|
153
|
+
* recompute the entry's utility score (index.db) with the same function the
|
|
154
|
+
* `akm index` finalize pass uses, scoped to this one entry, so there is one
|
|
155
|
+
* formula and the stored value is what the next index would compute. Chunk-8
|
|
156
|
+
* WI-8.3: usage_events lives in state.db; entries + utility_scores stay in `indexDb`.
|
|
157
|
+
* Search ranking does not read the utility score: it counts the recorded
|
|
158
|
+
* `source='user'` feedback events itself, so a user-attributed event affects
|
|
159
|
+
* ranking at once and an `improve`/`task`/`audit`/`unknown` one never does — an
|
|
160
|
+
* anti-self-reinforcement guard that stops an agent or automated pipeline from
|
|
161
|
+
* boosting its own picks. This is by design and must not be removed. The
|
|
162
|
+
* utility score is read by improve and metrics only; its update is best-effort
|
|
163
|
+
* and its failure never fails the feedback record or changes the ranking effect.
|
|
162
164
|
*/
|
|
163
165
|
function recordFeedbackUsage(indexDb, entryId, durableEntryRef, signal, metadataStr) {
|
|
164
166
|
let utilityResult;
|
|
165
|
-
let
|
|
166
|
-
let rankingUpdateSkippedReason;
|
|
167
|
+
let rankingSkippedReason;
|
|
167
168
|
const eventSource = resolveUsageEventSource();
|
|
168
169
|
withStateDb((stateDb) => {
|
|
169
170
|
insertUsageEvent(stateDb, {
|
|
@@ -175,22 +176,22 @@ function recordFeedbackUsage(indexDb, entryId, durableEntryRef, signal, metadata
|
|
|
175
176
|
source: eventSource,
|
|
176
177
|
});
|
|
177
178
|
if (eventSource !== "user") {
|
|
178
|
-
|
|
179
|
-
`feedback source is "${eventSource}", not "user" —
|
|
180
|
-
"feedback (anti-self-reinforcement guard; set AKM_EVENT_SOURCE=user to record as user demand).";
|
|
179
|
+
rankingSkippedReason =
|
|
180
|
+
`feedback source is "${eventSource}", not "user" — it does not count toward search ranking, which counts ` +
|
|
181
|
+
"user-attributed feedback only (anti-self-reinforcement guard; set AKM_EVENT_SOURCE=user to record as user demand).";
|
|
181
182
|
return;
|
|
182
183
|
}
|
|
183
184
|
try {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
const previousUtility = getUtilityScore(indexDb, entryId)?.utility ?? 0;
|
|
186
|
+
recomputeUtilityScores(indexDb, stateDb, entryId);
|
|
187
|
+
utilityResult = { previousUtility, nextUtility: getUtilityScore(indexDb, entryId)?.utility ?? previousUtility };
|
|
187
188
|
}
|
|
188
189
|
catch (err) {
|
|
189
|
-
// best-effort —
|
|
190
|
-
|
|
190
|
+
// best-effort — the event is recorded (and ranked) even if the utility update fails
|
|
191
|
+
warn(`[feedback] utility update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
192
|
}
|
|
192
193
|
});
|
|
193
|
-
return { utilityResult,
|
|
194
|
+
return { utilityResult, countsTowardRanking: eventSource === "user", rankingSkippedReason };
|
|
194
195
|
}
|
|
195
196
|
// ── Judged text ──────────────────────────────────────────────────────────────
|
|
196
197
|
/**
|
|
@@ -416,15 +417,20 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
416
417
|
'the exact fix: --replace "<exact current text>" --with "<corrected text>" --source "<URL,\n' +
|
|
417
418
|
'command or file that shows it>" (repeat --replace/--with for several edits; use --with=...\n' +
|
|
418
419
|
"for a value that starts with -). akm checks that each --replace text appears exactly once\n" +
|
|
419
|
-
"and
|
|
420
|
+
"and applies the edit at once, with no review (`akm proposal revert` undoes it). To mark\n" +
|
|
421
|
+
"the asset's history instead, add\n" +
|
|
420
422
|
"--superseded-by <ref> (the asset that replaces it) or --outdated (it describes a past\n" +
|
|
421
|
-
"state and nothing replaces it), with --reason and --source: the same
|
|
423
|
+
"state and nothing replaces it), with --reason and --source: the same edit sets its\n" +
|
|
422
424
|
"beliefState and, for --superseded-by, adds the ref to supersededBy. `--positive` records\n" +
|
|
423
425
|
"that an asset helped (it raises its ranking) and does not trigger a rewrite.\n\n" +
|
|
424
|
-
"
|
|
425
|
-
"
|
|
426
|
-
"
|
|
427
|
-
"
|
|
426
|
+
"Feedback changes `akm search` and `akm curate` ranking starting with the very\n" +
|
|
427
|
+
"next search; no reindex is needed. Each recorded positive raises the asset's\n" +
|
|
428
|
+
"score and each negative lowers it, the same way for every asset: the more\n" +
|
|
429
|
+
"positives, the higher it ranks (5 outrank 2), and each further vote counts\n" +
|
|
430
|
+
"for a little less. The effect is small, so it reorders close matches and\n" +
|
|
431
|
+
"does not override relevance. Only feedback recorded as the user counts, not\n" +
|
|
432
|
+
"feedback from improve, task or audit runs. Feedback also moves the utility\n" +
|
|
433
|
+
"score that improve and `akm metrics` read.",
|
|
428
434
|
},
|
|
429
435
|
args: {
|
|
430
436
|
// Optional in citty so run() is invoked even when omitted; we re-validate
|
|
@@ -565,8 +571,8 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
565
571
|
// background reindex it spawned — now that ensureIndex is removed, holding
|
|
566
572
|
// the lock only causes feedback to block for the full improve run duration.
|
|
567
573
|
let utilityResult;
|
|
568
|
-
let
|
|
569
|
-
let
|
|
574
|
+
let countsTowardRanking = false;
|
|
575
|
+
let rankingSkippedReason;
|
|
570
576
|
let durableRef = ref;
|
|
571
577
|
let fix;
|
|
572
578
|
const db = openExistingDatabase();
|
|
@@ -586,10 +592,10 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
586
592
|
throw new NotFoundError(`Ref "${ref}" is not in the index. ` +
|
|
587
593
|
"Run 'akm search' to verify the asset exists, then 'akm index' if it was recently added.", "ASSET_NOT_FOUND");
|
|
588
594
|
}
|
|
589
|
-
// Persist the feedback signal into usage_events.
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
//
|
|
595
|
+
// Persist the feedback signal into usage_events. A user-attributed signal
|
|
596
|
+
// also recomputes the entry's utility at once (see recordFeedbackUsage).
|
|
597
|
+
// Search ranking reads the recorded events directly, so no `akm index`
|
|
598
|
+
// run is needed.
|
|
593
599
|
// WI-8.5b: the `feedback` / `improve_review_needed` events key on the
|
|
594
600
|
// resolved entry's fully-qualified item_ref — the SAME durable key the
|
|
595
601
|
// usage_events row carries and the SAME spelling the signal-delta
|
|
@@ -645,8 +651,8 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
645
651
|
const metadataStr = Object.keys(metadataObj).length > 1 ? JSON.stringify(metadataObj) : undefined;
|
|
646
652
|
const recordResult = recordFeedbackUsage(db, entryId, itemRef, signal, metadataStr);
|
|
647
653
|
utilityResult = recordResult.utilityResult;
|
|
648
|
-
|
|
649
|
-
|
|
654
|
+
countsTowardRanking = recordResult.countsTowardRanking;
|
|
655
|
+
rankingSkippedReason = recordResult.rankingSkippedReason;
|
|
650
656
|
}
|
|
651
657
|
finally {
|
|
652
658
|
closeDatabase(db);
|
|
@@ -656,16 +662,34 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
656
662
|
ref: durableRef,
|
|
657
663
|
metadata: metadataObj,
|
|
658
664
|
});
|
|
659
|
-
|
|
660
|
-
|
|
665
|
+
// A person typed the fix, so it is applied now: the proposal is minted for the audit trail, `akm proposal
|
|
666
|
+
// revert` and reflect's "already acted on" rule, then accepted through the ordinary accept path (the same
|
|
667
|
+
// checks and git commit). If that refuses, the proposal is rejected and the command fails.
|
|
668
|
+
let fixProposal;
|
|
669
|
+
if (fix && reason?.trim() && fixSource) {
|
|
670
|
+
const stashDir = resolveStashDir();
|
|
671
|
+
const minted = createProposal(stashDir, {
|
|
661
672
|
ref: durableRef,
|
|
662
673
|
itemRef: durableRef,
|
|
663
674
|
target: fix.target,
|
|
664
675
|
source: "feedback",
|
|
665
676
|
payload: { content: fix.content },
|
|
666
677
|
feedback: { reason: reason.trim(), source: fixSource },
|
|
667
|
-
})
|
|
668
|
-
|
|
678
|
+
});
|
|
679
|
+
try {
|
|
680
|
+
recordGateDecision(stashDir, minted.id, { outcome: "auto-accepted", reason: "manual-fix", gate: "feedback" });
|
|
681
|
+
fixProposal = (await akmProposalAccept({ stashDir, id: minted.id })).proposal;
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
try {
|
|
685
|
+
rejectProposalDurably(stashDir, minted.id, `manual fix not applied: ${err instanceof Error ? err.message : String(err)}`);
|
|
686
|
+
}
|
|
687
|
+
catch (rejectErr) {
|
|
688
|
+
warn(`[feedback] could not reject proposal ${minted.id}: ${rejectErr instanceof Error ? rejectErr.message : String(rejectErr)}`);
|
|
689
|
+
}
|
|
690
|
+
throw err;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
669
693
|
// F-5 / #386: When a high-utility asset crosses below the review threshold,
|
|
670
694
|
// auto-create a review-needed escalation proposal so a human can confirm
|
|
671
695
|
// whether the negative feedback is valid before the asset falls out of
|
|
@@ -674,7 +698,9 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
674
698
|
// Emit a structured event rather than a proposal so the review-needed
|
|
675
699
|
// signal doesn't risk an accidental asset overwrite if the proposal is
|
|
676
700
|
// accepted.
|
|
677
|
-
if (utilityResult
|
|
701
|
+
if (utilityResult &&
|
|
702
|
+
utilityResult.previousUtility >= UTILITY_REVIEW_THRESHOLD &&
|
|
703
|
+
utilityResult.nextUtility < UTILITY_REVIEW_THRESHOLD) {
|
|
678
704
|
try {
|
|
679
705
|
appendEvent({
|
|
680
706
|
eventType: "improve_review_needed",
|
|
@@ -729,9 +755,9 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
729
755
|
signal,
|
|
730
756
|
reason: reason?.trim() ?? null,
|
|
731
757
|
tags: validatedTags,
|
|
732
|
-
rankingUpdate:
|
|
758
|
+
rankingUpdate: countsTowardRanking
|
|
733
759
|
? { applied: true }
|
|
734
|
-
: { applied: false, reason:
|
|
760
|
+
: { applied: false, reason: rankingSkippedReason ?? "unknown" },
|
|
735
761
|
...(fixProposal
|
|
736
762
|
? {
|
|
737
763
|
fix: {
|
|
@@ -11,6 +11,8 @@ import { withEngineFallback } from "../../integrations/agent/engine-fallback.js"
|
|
|
11
11
|
import { isLlmCredentialAvailable, resolveEngine, } from "../../integrations/agent/engine-resolution.js";
|
|
12
12
|
import { executionEngineDefinitionsFromConfig } from "../../integrations/agent/execution.js";
|
|
13
13
|
import { loadModelMap, mergeModelMapLayers, parseModelMapLayer, readInstalledModelMapText, resolveModelMapAlias, userModelMapPath, } from "../../integrations/agent/model-map.js";
|
|
14
|
+
import { detectOpencodeMajor, resolveOpencodeBin, } from "../../integrations/harnesses/opencode/version.js";
|
|
15
|
+
import { OPENCODE_CLIENT_PACKAGE } from "../../integrations/harnesses/opencode-sdk/wire.js";
|
|
14
16
|
import { probeEndpointOnce } from "../../llm/client.js";
|
|
15
17
|
import { STATE_DB_FREELIST_WARN_RATIO, } from "../../storage/state-db-integrity.js";
|
|
16
18
|
import { listKeys } from "../env/env.js";
|
|
@@ -116,20 +118,15 @@ async function runConfiguredEngineProbe(checkName, engineName, config, deps, rea
|
|
|
116
118
|
const env = deps.env ?? process.env;
|
|
117
119
|
const configuredEngine = config.engines?.[engineName];
|
|
118
120
|
if (configuredEngine?.kind === "agent" && configuredEngine.platform === "opencode-sdk") {
|
|
119
|
-
|
|
121
|
+
const binary = resolveOpencodeBin(configuredEngine.bin);
|
|
122
|
+
let detected;
|
|
120
123
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
const binary = configuredEngine.bin ?? "opencode";
|
|
129
|
-
let binaryAvailable;
|
|
130
|
-
try {
|
|
131
|
-
const version = (deps.spawnSync ?? spawnSync)(binary, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
132
|
-
binaryAvailable = (version.status ?? 1) === 0;
|
|
124
|
+
detected = detectOpencodeMajor(binary, {
|
|
125
|
+
probe: (bin) => {
|
|
126
|
+
const run = (deps.spawnSync ?? spawnSync)(bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
127
|
+
return (run.status ?? 1) === 0 ? String(run.stdout ?? "").trim() : undefined;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
133
130
|
}
|
|
134
131
|
catch {
|
|
135
132
|
return {
|
|
@@ -141,6 +138,18 @@ async function runConfiguredEngineProbe(checkName, engineName, config, deps, rea
|
|
|
141
138
|
evidence: { engine: engineName, runtimeKind: "sdk", binaryAvailable: false },
|
|
142
139
|
};
|
|
143
140
|
}
|
|
141
|
+
const binaryAvailable = detected.runnable;
|
|
142
|
+
// The client package of the OpenCode major the binary reports; never the other major's.
|
|
143
|
+
const clientPackage = OPENCODE_CLIENT_PACKAGE[detected.major];
|
|
144
|
+
let packageAvailable = false;
|
|
145
|
+
try {
|
|
146
|
+
const resolvePackage = deps.resolvePackage ?? ((name) => import.meta.resolve(name));
|
|
147
|
+
resolvePackage(clientPackage);
|
|
148
|
+
packageAvailable = true;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
packageAvailable = false;
|
|
152
|
+
}
|
|
144
153
|
const fallbackEngine = configuredEngine.llmEngine;
|
|
145
154
|
let fallback;
|
|
146
155
|
let fallbackCredential;
|
|
@@ -183,7 +192,7 @@ async function runConfiguredEngineProbe(checkName, engineName, config, deps, rea
|
|
|
183
192
|
? findSuppliedByEnvAsset(fallbackCredential, deps)
|
|
184
193
|
: null;
|
|
185
194
|
const missing = [
|
|
186
|
-
!packageAvailable ?
|
|
195
|
+
!packageAvailable ? `${clientPackage} package` : undefined,
|
|
187
196
|
!binaryAvailable ? `${binary} binary` : undefined,
|
|
188
197
|
fallbackEngine && !fallback ? "configured fallback LLM connection" : undefined,
|
|
189
198
|
!fallbackCredentialAvailable
|
|
@@ -198,7 +207,9 @@ async function runConfiguredEngineProbe(checkName, engineName, config, deps, rea
|
|
|
198
207
|
runtimeKind: "sdk",
|
|
199
208
|
binary,
|
|
200
209
|
binaryAvailable,
|
|
201
|
-
package:
|
|
210
|
+
package: clientPackage,
|
|
211
|
+
detectedVersion: detected.version || null,
|
|
212
|
+
detectedMajor: detected.major,
|
|
202
213
|
packageAvailable,
|
|
203
214
|
model: effectiveModel ?? null,
|
|
204
215
|
configuredModel: configuredModel ?? null,
|
|
@@ -533,7 +544,21 @@ export function probeActiveImproveStrategy(deps = {}) {
|
|
|
533
544
|
const requiredModelProcessNames = modelProcessNames.filter((name) => plan.processes[name].enabled || plan.engineUnavailable.some((item) => item.process === name));
|
|
534
545
|
const availableModelProcessNames = modelProcessNames.filter((name) => plan.processes[name].enabled);
|
|
535
546
|
const allRequiredUnavailable = requiredModelProcessNames.length > 0 && availableModelProcessNames.length === 0;
|
|
536
|
-
|
|
547
|
+
// No engine configured at all (no unavailable item carries a resolved engine)
|
|
548
|
+
// is "not set up yet", not a broken credential: it keeps the unknown/warn
|
|
549
|
+
// result it had when the plan threw for it. Engine-free deterministic triage
|
|
550
|
+
// (#1143) now lets such a plan resolve, so this has to be explicit.
|
|
551
|
+
const noEngineConfigured = plan.engineUnavailable.length > 0 && plan.engineUnavailable.every((i) => !i.engine);
|
|
552
|
+
const explicitlyConfigured = config.defaults?.improveStrategy !== undefined || Object.keys(config.improve?.strategies ?? {}).length > 0;
|
|
553
|
+
const status = unavailableProcesses.length === 0
|
|
554
|
+
? "pass"
|
|
555
|
+
: allRequiredUnavailable
|
|
556
|
+
? noEngineConfigured
|
|
557
|
+
? explicitlyConfigured
|
|
558
|
+
? "warn"
|
|
559
|
+
: "unknown"
|
|
560
|
+
: "fail"
|
|
561
|
+
: "warn";
|
|
537
562
|
return {
|
|
538
563
|
check: {
|
|
539
564
|
name: "active-improve-strategy",
|
|
@@ -52,6 +52,7 @@ import { spawnSync } from "node:child_process";
|
|
|
52
52
|
import fs from "node:fs";
|
|
53
53
|
import os from "node:os";
|
|
54
54
|
import path from "node:path";
|
|
55
|
+
import { OPENCODE_V1, OPENCODE_V2, readPackageVersion, } from "../../integrations/harnesses/opencode/plugin-layout.js";
|
|
55
56
|
import { isExactSemver, isSemverRange, maxSatisfying, satisfiesRange } from "../../registry/semver.js";
|
|
56
57
|
/**
|
|
57
58
|
* Root directory holding Claude Code's plugin cache + marketplace clones.
|
|
@@ -66,41 +67,69 @@ function claudePluginsDir() {
|
|
|
66
67
|
/**
|
|
67
68
|
* Root of OpenCode's package cache, same override pattern as
|
|
68
69
|
* {@link claudePluginsDir} (`AKM_OPENCODE_CACHE_DIR` for tests).
|
|
69
|
-
* The OpenCode
|
|
70
|
-
* `
|
|
70
|
+
* The OpenCode plugins (`akm-opencode`, and `akm-opencode-v2` for OpenCode 2) bundle their own `akm-cli`
|
|
71
|
+
* (layout: `integrations/harnesses/opencode/plugin-layout.ts`) and run it in-process,
|
|
71
72
|
* sharing the host's databases with every other akm install — its version
|
|
72
73
|
* lag matters the same way a stale Claude plugin's does.
|
|
73
74
|
*/
|
|
74
75
|
function opencodeCacheDir() {
|
|
75
76
|
return process.env.AKM_OPENCODE_CACHE_DIR ?? path.join(os.homedir(), ".cache", "opencode");
|
|
76
77
|
}
|
|
78
|
+
const OPENCODE_PLUGIN_LOCATIONS = [
|
|
79
|
+
{
|
|
80
|
+
// OpenCode 1: <cache>/packages/akm-opencode/node_modules/akm-cli (the folder read since this check began).
|
|
81
|
+
flavor: OPENCODE_V1,
|
|
82
|
+
advisory: "opencode-plugin-version",
|
|
83
|
+
label: "OpenCode's",
|
|
84
|
+
remedy: "update the OpenCode plugin",
|
|
85
|
+
folders: (cacheRoot) => [{ folder: path.join(cacheRoot, OPENCODE_V1.cacheFolder(OPENCODE_V1.pkg)) }],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
// OpenCode 2: <cache>/npm/akm-opencode-v2@<spec>/<epoch-ms>/node_modules/akm-cli, newest build per spec.
|
|
89
|
+
flavor: OPENCODE_V2,
|
|
90
|
+
advisory: "opencode-v2-plugin-version",
|
|
91
|
+
label: "OpenCode 2's akm-opencode-v2",
|
|
92
|
+
remedy: "update the akm-opencode-v2 plugin",
|
|
93
|
+
folders: (cacheRoot) => {
|
|
94
|
+
const npmDir = path.join(cacheRoot, OPENCODE_V2.cacheFolder(""));
|
|
95
|
+
let specs;
|
|
96
|
+
try {
|
|
97
|
+
specs = fs.readdirSync(npmDir).filter((name) => name.startsWith(`${OPENCODE_V2.pkg}@`));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
return specs.sort().map((spec) => ({ spec, folder: path.join(npmDir, spec) }));
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
77
106
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* {@link detectInstalledPlugins}'s manifest read. `undefined` when the
|
|
81
|
-
* OpenCode plugin, or its bundled akm-cli, is not installed.
|
|
107
|
+
* The bundled `akm-cli` of each cached copy of `plugin`: its `package.json` is read, never executed,
|
|
108
|
+
* mirroring {@link detectInstalledPlugins}'s manifest read. Copies with no bundled akm-cli are skipped.
|
|
82
109
|
*/
|
|
83
|
-
function
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return undefined;
|
|
110
|
+
function findBundledAkm(plugin, cacheRoot) {
|
|
111
|
+
const found = [];
|
|
112
|
+
for (const { spec, folder } of plugin.folders(cacheRoot)) {
|
|
113
|
+
const nodeModules = plugin.flavor.nodeModulesDir(folder);
|
|
114
|
+
const version = nodeModules && readPackageVersion(path.join(nodeModules, "akm-cli", "package.json"));
|
|
115
|
+
if (version !== undefined)
|
|
116
|
+
found.push(spec ? { spec, version } : { version });
|
|
91
117
|
}
|
|
118
|
+
return found;
|
|
92
119
|
}
|
|
93
|
-
function buildOpencodeBundledAdvisory(
|
|
120
|
+
function buildOpencodeBundledAdvisory(plugin, bundled, cliVersion) {
|
|
121
|
+
const bundledVersion = bundled.version;
|
|
94
122
|
const stale = bundledVersion !== cliVersion;
|
|
123
|
+
const where = bundled.spec ? ` (${bundled.spec})` : "";
|
|
95
124
|
return {
|
|
96
|
-
name:
|
|
125
|
+
name: plugin.advisory,
|
|
97
126
|
kind: "deterministic",
|
|
98
127
|
status: stale ? "warn" : "pass",
|
|
99
128
|
confidence: "high",
|
|
100
129
|
message: stale
|
|
101
|
-
?
|
|
102
|
-
:
|
|
103
|
-
evidence: { bundledVersion, cliVersion },
|
|
130
|
+
? `${plugin.label} bundled akm-cli${where} is v${bundledVersion}, but the running CLI is v${cliVersion} — it shares the same databases in-process; ${plugin.remedy}.`
|
|
131
|
+
: `${plugin.label} bundled akm-cli${where} matches the running v${cliVersion}.`,
|
|
132
|
+
evidence: bundled.spec ? { bundledVersion, cliVersion, spec: bundled.spec } : { bundledVersion, cliVersion },
|
|
104
133
|
};
|
|
105
134
|
}
|
|
106
135
|
/**
|
|
@@ -218,9 +247,10 @@ export function collectPluginStalenessAdvisories(options) {
|
|
|
218
247
|
const plugins = detectInstalledPlugins(pluginsRoot);
|
|
219
248
|
const results = plugins.map((plugin) => buildAdvisory(plugin, options.cliVersion, pluginsRoot, listRemoteTags));
|
|
220
249
|
const opencodeCacheRoot = options.opencodeCacheRoot ?? opencodeCacheDir();
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
250
|
+
for (const plugin of OPENCODE_PLUGIN_LOCATIONS) {
|
|
251
|
+
for (const bundled of findBundledAkm(plugin, opencodeCacheRoot)) {
|
|
252
|
+
results.push(buildOpencodeBundledAdvisory(plugin, bundled, options.cliVersion));
|
|
253
|
+
}
|
|
224
254
|
}
|
|
225
255
|
return results;
|
|
226
256
|
}
|
|
@@ -6,7 +6,7 @@ import { IMPROVE_AUTONOMY_CONFIG_KEY, isImproveAutonomyEnabled, } from "../../co
|
|
|
6
6
|
export const AUTONOMY_LANES = ["memoryInference", "triagePromote", "memoryCleanup"];
|
|
7
7
|
const LANE_REASONS = {
|
|
8
8
|
memoryInference: "writes derived memory children and rewrites parent frontmatter",
|
|
9
|
-
triagePromote: "auto-accepts queued proposals
|
|
9
|
+
triagePromote: "auto-accepts queued proposals the triage judgment tier approves (downgraded to queue)",
|
|
10
10
|
memoryCleanup: "rewrites belief-state frontmatter and moves files into the cleanup archive",
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
@@ -59,8 +59,10 @@ export function applyAutonomyGate(strategy, config) {
|
|
|
59
59
|
}
|
|
60
60
|
// Triage stays ENABLED — queued proposals are still triaged, they just are not
|
|
61
61
|
// auto-accepted. Disabling it would remove review work the user asked for,
|
|
62
|
-
// which is the opposite of what a review-first default should do.
|
|
63
|
-
|
|
62
|
+
// which is the opposite of what a review-first default should do. Only a
|
|
63
|
+
// promote that runs the judgment tier is gated (#1143); a deterministic-only
|
|
64
|
+
// promote is the default and needs no opt-in.
|
|
65
|
+
if (processes.triage?.applyMode === "promote" && processes.triage.judgment?.enabled === true) {
|
|
64
66
|
processes.triage = { ...processes.triage, applyMode: "queue" };
|
|
65
67
|
gated.push(gatedLane("triagePromote"));
|
|
66
68
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/** Consolidate's chunk sizing and per-chunk prompt (pure token math and text). */
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
-
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
7
6
|
import { contentHash, stripFrontmatterBody } from "../content-hash.js";
|
|
8
7
|
/** A conservative chars-per-token estimate, shared by every context-length budget. */
|
|
9
8
|
export const CHARS_PER_TOKEN = 3;
|
|
@@ -24,9 +23,8 @@ export function computeSafeChunkSize(contextLength, bodyTruncation, maxChunkSize
|
|
|
24
23
|
/**
|
|
25
24
|
* One chunk's user prompt. Each memory shows its body only (frontmatter can
|
|
26
25
|
* outrun the truncation and leave the model judging metadata), marked
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
* prompt forbids promoting those.
|
|
26
|
+
* `already queued` when a pending consolidate proposal carries its body hash —
|
|
27
|
+
* the system prompt forbids promoting those.
|
|
30
28
|
*/
|
|
31
29
|
export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()) {
|
|
32
30
|
const lines = [
|
|
@@ -42,12 +40,7 @@ export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks,
|
|
|
42
40
|
catch {
|
|
43
41
|
body = "(unreadable)";
|
|
44
42
|
}
|
|
45
|
-
const
|
|
46
|
-
if (parseFrontmatter(body).data.captureMode === "hot")
|
|
47
|
-
annotations.push("captureMode: hot");
|
|
48
|
-
if (pendingProposalBodyHashes.has(contentHash(body, "body")))
|
|
49
|
-
annotations.push("already queued");
|
|
50
|
-
const annotationSuffix = annotations.length > 0 ? ` (${annotations.join("; ")})` : "";
|
|
43
|
+
const annotationSuffix = pendingProposalBodyHashes.has(contentHash(body, "body")) ? " (already queued)" : "";
|
|
51
44
|
lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`, `Description: ${m.description || "(none)"}`, `Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`, "---", stripFrontmatterBody(body).slice(0, bodyTruncation), "");
|
|
52
45
|
});
|
|
53
46
|
return lines.join("\n");
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { searchLocal } from "../../../indexer/search/db-search.js";
|
|
5
5
|
import { stripFrontmatterBody } from "../content-hash.js";
|
|
6
6
|
import { stripBundle } from "../ledger.js";
|
|
7
|
-
import { loadRetrievalQueries } from "../retrieval-
|
|
7
|
+
import { loadRetrievalQueries } from "../retrieval-queries.js";
|
|
8
8
|
/** At most this many of the retired asset's most recent queries are replayed (plan §5.4, §7). */
|
|
9
9
|
export const CONTINUITY_MAX_QUERIES = 5;
|
|
10
10
|
/** "Top 10" per the plan's rule — both the pass/fail cutoff and the search `limit`. */
|
|
@@ -63,8 +63,8 @@ function normalizedBody(raw) {
|
|
|
63
63
|
*
|
|
64
64
|
* S3: two fixes against false flags measured on a real night-1 admission
|
|
65
65
|
* (300 pairs, 6 flags, half spurious):
|
|
66
|
-
* - queries are the
|
|
67
|
-
*
|
|
66
|
+
* - queries are the cleaned set `loadRetrievalQueries` returns
|
|
67
|
+
* (`../retrieval-queries.ts`) — stash-README
|
|
68
68
|
* boilerplate, harness/tool envelopes, pastes over 2,000 characters, and
|
|
69
69
|
* duplicates are dropped before replay, not just capped at 5 raw entries;
|
|
70
70
|
* - when the retired and successor bodies normalize identical, the check
|
|
@@ -92,7 +92,7 @@ export async function checkRetirementContinuity(args) {
|
|
|
92
92
|
// no real risk here to check for.
|
|
93
93
|
if (normalizedBody(args.retiredRaw) === normalizedBody(args.successorRaw))
|
|
94
94
|
return undefined;
|
|
95
|
-
// S3a: the
|
|
95
|
+
// S3a: the cleaned queries from `loadRetrievalQueries` —
|
|
96
96
|
// boilerplate, envelopes, pastes and duplicates dropped before replay.
|
|
97
97
|
const queries = loadRetrievalQueries(args.ledgerAccess, args.retiredRef).slice(0, CONTINUITY_MAX_QUERIES);
|
|
98
98
|
if (queries.length === 0)
|
|
@@ -42,10 +42,9 @@ import { assertRunnerCredentials } from "../../../integrations/agent/runner-disp
|
|
|
42
42
|
import { runGit } from "../../../sources/providers/git-install.js";
|
|
43
43
|
import { closeDatabase, openExistingDatabase, openReadonlyExistingDatabase, } from "../../../storage/repositories/index-connection.js";
|
|
44
44
|
import { getAllEntries, getEntryById } from "../../../storage/repositories/index-entries-repository.js";
|
|
45
|
-
import { getNeighborsByEntryId } from "../../../storage/repositories/index-vec-repository.js";
|
|
46
|
-
import { isRetireProposal, PAIR_PASS_GATE, } from "../../proposal/proposal-types.js";
|
|
45
|
+
import { getNeighborsByEntryId, hasStoredEmbedding } from "../../../storage/repositories/index-vec-repository.js";
|
|
46
|
+
import { EXPIRED_GATE_REASON, isRetireProposal, PAIR_PASS_GATE, STALE_TARGET_GATE_REASON, } from "../../proposal/proposal-types.js";
|
|
47
47
|
import { createRetireProposal, listProposalsReadOnly, proposalContentHash, recordGateDecision, } from "../../proposal/repository.js";
|
|
48
|
-
import { isHotCapturedMemory } from "../consolidate.js";
|
|
49
48
|
import { contentHash, stripFrontmatterBody } from "../content-hash.js";
|
|
50
49
|
import { loadLedgerSnapshot, PAIR_PASS_LEDGER_SOURCE, recordLedgerAttempt, stripBundle } from "../ledger.js";
|
|
51
50
|
import { isInRetrievalScope, loadRetrievalScope } from "../retrieval-scope.js";
|
|
@@ -313,6 +312,10 @@ export function loadRejectedPairKeys(stashDir, proposalsCtx) {
|
|
|
313
312
|
for (const p of listProposalsReadOnly(stashDir, { status, includeArchive: true }, proposalsCtx)) {
|
|
314
313
|
if (!isRetireProposal(p) || !p.retirement)
|
|
315
314
|
continue;
|
|
315
|
+
// Expiry and a stale target are procedural, not a person saying no: the pair may be judged again.
|
|
316
|
+
const autoReason = p.gateDecision?.outcome === "auto-rejected" ? p.gateDecision.reason : undefined;
|
|
317
|
+
if (autoReason === EXPIRED_GATE_REASON || autoReason === STALE_TARGET_GATE_REASON)
|
|
318
|
+
continue;
|
|
316
319
|
keys.add(rejectedPairKey(p.retirement.retiredRef, p.retirement.successorRef, p.retirement.retiredContentHash, p.retirement.successorContentHash));
|
|
317
320
|
}
|
|
318
321
|
}
|
|
@@ -364,12 +367,18 @@ export function selectInitiators(pool, opts, stashDir, gitFirstAdded) {
|
|
|
364
367
|
* apart from a fully-judged one). S2: fetches `PAIR_NEIGHBOR_FETCH_K` (20)
|
|
365
368
|
* raw neighbours and keeps the first `PAIR_NEIGHBOR_K` (5) that clear every
|
|
366
369
|
* filter, so a few self/twin/bundle/tier misses in the raw top-5 no longer
|
|
367
|
-
* starve an initiator down to zero real candidates.
|
|
370
|
+
* starve an initiator down to zero real candidates. An initiator with no
|
|
371
|
+
* stored vector (embeddings off, or not embedded yet) cannot be queried at
|
|
372
|
+
* all; its ref is added to `unvectored` so the caller does not settle it.
|
|
368
373
|
*/
|
|
369
|
-
export function selectCandidates(db, initiators, bundleId) {
|
|
374
|
+
export function selectCandidates(db, initiators, bundleId, unvectored) {
|
|
370
375
|
const candidates = [];
|
|
371
376
|
const seenPairs = new Set();
|
|
372
377
|
for (const initiator of initiators) {
|
|
378
|
+
if (!hasStoredEmbedding(db, initiator.entryId)) {
|
|
379
|
+
unvectored?.add(initiator.ref);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
373
382
|
// S1: a changed-content or new-material initiator judges at T_PAIR; the
|
|
374
383
|
// rest of the backlog (no row, not recently git-added) needs the higher
|
|
375
384
|
// BACKFILL_FLOOR.
|
|
@@ -465,7 +474,13 @@ function orderByAge(x, y) {
|
|
|
465
474
|
}
|
|
466
475
|
/** The user message: dates decide "A (older)" / "B (newer)" (plan Appendix A), matching the calibration sample's own ordering. */
|
|
467
476
|
export function buildPairUserPrompt(older, newer) {
|
|
468
|
-
|
|
477
|
+
// Equal dates (a batch added in one commit, notes from one file time) make neither side newer: say so, or the judge
|
|
478
|
+
// reads "B (newer)" as proof that B replaces A (#1134).
|
|
479
|
+
const tied = Date.parse(older.createdIso) === Date.parse(newer.createdIso);
|
|
480
|
+
return [
|
|
481
|
+
...sideSection(tied ? "A (same age as B)" : "A (older)", older),
|
|
482
|
+
...sideSection(tied ? "B (same age as A)" : "B (newer)", newer),
|
|
483
|
+
].join("\n");
|
|
469
484
|
}
|
|
470
485
|
/** The tombstone-vocabulary reason a judge label maps to (`supersedes` -> `superseded`; the rest unchanged). */
|
|
471
486
|
export function tombstoneReason(label) {
|
|
@@ -591,9 +606,6 @@ async function judgeOne(ctx, candidate) {
|
|
|
591
606
|
[retired, successor] = [successor, retired];
|
|
592
607
|
}
|
|
593
608
|
// Guards (plan §5.2 step 4 / brief §A "Guards").
|
|
594
|
-
if (retired.asset.type === "memory" && isHotCapturedMemory(retired.asset.filePath)) {
|
|
595
|
-
return { failed: false }; // never propose retiring a captureMode: hot memory — leave the pair alone
|
|
596
|
-
}
|
|
597
609
|
if (retired.asset.type === "memory" && retired.asset.name.endsWith(DERIVED_SUFFIX)) {
|
|
598
610
|
// S3: derive the parent path from the FULL file path, not from name +
|
|
599
611
|
// dirname — for a subfolder memory (e.g. memories/sub/foo.derived) the
|
|
@@ -730,6 +742,7 @@ seams = {}) {
|
|
|
730
742
|
let candidates;
|
|
731
743
|
let gitFirstAdded;
|
|
732
744
|
let db;
|
|
745
|
+
const unvectored = new Set();
|
|
733
746
|
try {
|
|
734
747
|
db = opts.dryRun ? openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true }) : openExistingDatabase();
|
|
735
748
|
if (!db)
|
|
@@ -737,7 +750,7 @@ seams = {}) {
|
|
|
737
750
|
gitFirstAdded = loadGitFirstAddedMap(stashDir);
|
|
738
751
|
const pool = loadPairPassPool(db, bundleId);
|
|
739
752
|
initiators = selectInitiators(pool, opts, stashDir, gitFirstAdded).initiators;
|
|
740
|
-
candidates = selectCandidates(db, initiators, bundleId);
|
|
753
|
+
candidates = selectCandidates(db, initiators, bundleId, unvectored);
|
|
741
754
|
}
|
|
742
755
|
catch (error) {
|
|
743
756
|
warnings.push(`Pair pass: index unavailable — skipped (${error instanceof Error ? error.message : String(error)}).`);
|
|
@@ -748,6 +761,9 @@ seams = {}) {
|
|
|
748
761
|
closeDatabase(db);
|
|
749
762
|
}
|
|
750
763
|
const initiatorsBacklog = initiators.filter((i) => i.backlog).length;
|
|
764
|
+
if (unvectored.size > 0) {
|
|
765
|
+
warnings.push(`Pair pass: ${unvectored.size} asset(s) have no stored embedding, so their neighbours cannot be found and they were not judged. The pair pass needs stored embeddings (set semanticSearchMode to "auto" in the config, then run akm index).`);
|
|
766
|
+
}
|
|
751
767
|
// Never judge a pair when either side already has a pending retire
|
|
752
768
|
// proposal, as the retired ref OR its successor (B2 widens this from the
|
|
753
769
|
// retired ref alone): an asset spoken for by one pending decision cannot
|
|
@@ -861,6 +877,9 @@ seams = {}) {
|
|
|
861
877
|
const admittedRefs = new Set(judgeable.map((c) => c.initiator.ref));
|
|
862
878
|
const ledgerInputs = initiators
|
|
863
879
|
.filter((i) => {
|
|
880
|
+
// No stored vector: the neighbour query never ran, so nothing was judged. Leave it unsettled.
|
|
881
|
+
if (unvectored.has(i.ref))
|
|
882
|
+
return false;
|
|
864
883
|
const hasCandidates = byInitiator.has(i.ref);
|
|
865
884
|
if (!hasCandidates)
|
|
866
885
|
return true;
|