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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/**
|
|
5
|
-
* `akm
|
|
5
|
+
* `akm task` — register, inspect, run, and remove scheduled task assets.
|
|
6
6
|
*
|
|
7
7
|
* Each handler exported here is a pure function that performs the real work;
|
|
8
8
|
* `src/cli.ts` wraps these in citty `defineCommand`s and shapes their return
|
|
@@ -11,22 +11,26 @@
|
|
|
11
11
|
import fs from "node:fs";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { stringify as yamlStringify } from "yaml";
|
|
14
|
-
import {
|
|
14
|
+
import { assetPathForName } from "../../core/asset/asset-placement.js";
|
|
15
|
+
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
15
16
|
import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
16
17
|
import { loadConfig } from "../../core/config/config.js";
|
|
18
|
+
import { IMPROVE_AUTONOMY_CONFIG_KEY, isImproveAutonomyEnabled } from "../../core/config/experimental.js";
|
|
17
19
|
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
18
20
|
import { getTaskHistoryDir, getTaskLogDir } from "../../core/paths.js";
|
|
19
|
-
import { commitWriteTargetBoundary, deleteAssetFromSource, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
|
|
20
|
-
import { listAgentProfileNames } from "../../integrations/agent/index.js";
|
|
21
|
-
import { resolveAssetPath } from "../../sources/resolve.js";
|
|
21
|
+
import { commitWriteTargetBoundary, deleteAssetFromSource, prepareWriteTargetForMutation, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
|
|
22
22
|
import { backendNameForPlatform, selectBackend } from "../../tasks/backends/index.js";
|
|
23
23
|
import { parseTaskDocument } from "../../tasks/parser.js";
|
|
24
24
|
import { resolveAkmInvocation } from "../../tasks/resolve-akm-bin.js";
|
|
25
|
-
import { exitCodeForStatus, readTaskHistory, runTask } from "../../tasks/runner.js";
|
|
26
|
-
import { parseSchedule, SCHEDULE_SUPPORTED_SUBSET_HINT
|
|
25
|
+
import { exitCodeForStatus, INVALID_TASK_ATTEMPT_ID, readTaskHistory, recordTaskAttemptFailure, runTask, } from "../../tasks/runner.js";
|
|
26
|
+
import { parseSchedule, SCHEDULE_SUPPORTED_SUBSET_HINT } from "../../tasks/schedule.js";
|
|
27
|
+
import { schedulerContextDescriptor, validateSchedulerContextDescriptor, writeSchedulerContextDescriptor, } from "../../tasks/scheduler-invocation.js";
|
|
28
|
+
import { normaliseTaskId } from "../../tasks/task-id.js";
|
|
27
29
|
import { validateTaskDocument } from "../../tasks/validator.js";
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
+
import { isWorkflowEngineEnabled, WORKFLOW_ENGINE_CONFIG_KEY } from "../../workflows/exec/workflow-engine-gate.js";
|
|
31
|
+
import { applyAutonomyGate, configuredDirectAutonomyLanes, describeGatedLanes } from "../improve/autonomy-gate.js";
|
|
32
|
+
import { resolveImproveStrategy } from "../improve/improve-strategies.js";
|
|
33
|
+
export async function akmTasksAdd(input, deps = {}) {
|
|
30
34
|
const id = normaliseTaskId(input.id);
|
|
31
35
|
const hasCommand = input.command !== undefined &&
|
|
32
36
|
input.command !== null &&
|
|
@@ -36,19 +40,28 @@ export async function akmTasksAdd(input) {
|
|
|
36
40
|
if (targetCount !== 1) {
|
|
37
41
|
throw new UsageError("Pass exactly one of --workflow <ref>, --prompt <asset-ref|./file.md|text>, or --command <shell-command>.", "INVALID_FLAG_VALUE");
|
|
38
42
|
}
|
|
43
|
+
if (input.workflow && (input.engine !== undefined || input.model !== undefined || input.timeoutMs !== undefined)) {
|
|
44
|
+
throw new UsageError("Workflow tasks accept only --params; engine, model, and timeout are prompt-task fields.", "INVALID_FLAG_VALUE");
|
|
45
|
+
}
|
|
46
|
+
if (hasCommand && (input.engine !== undefined || input.model !== undefined)) {
|
|
47
|
+
throw new UsageError("Command tasks accept --timeout-ms but not --engine or --model.", "INVALID_FLAG_VALUE");
|
|
48
|
+
}
|
|
39
49
|
// Validate the schedule for the active backend before writing anything.
|
|
40
|
-
|
|
50
|
+
// WI-9.10e: the injected backend (tests) carries its own name, so derive it
|
|
51
|
+
// from `deps.backend` when present — retiring the `_setBackendsForTests` seam.
|
|
52
|
+
const backend = deps.backend?.name ?? backendNameForPlatform();
|
|
41
53
|
parseSchedule(input.schedule, backend);
|
|
42
|
-
const
|
|
43
|
-
const
|
|
54
|
+
const bundle = resolveTaskBundle(input.target, { requireWritable: true });
|
|
55
|
+
const writeTarget = bundle.resolved;
|
|
56
|
+
const stashDir = bundle.stashDir;
|
|
57
|
+
const installOpts = bundle.installTarget !== undefined ? { target: bundle.installTarget } : undefined;
|
|
44
58
|
const typeRoot = path.join(stashDir, "tasks");
|
|
45
|
-
|
|
46
|
-
const assetPath = resolveAssetPathFromName("task", typeRoot, id);
|
|
59
|
+
const assetPath = assetPathForName("task", typeRoot, id);
|
|
47
60
|
if (!isWithin(assetPath, typeRoot)) {
|
|
48
61
|
throw new UsageError(`Resolved task path escapes the stash: "${id}".`, "PATH_ESCAPE_VIOLATION");
|
|
49
62
|
}
|
|
50
63
|
if (fs.existsSync(assetPath) && !input.force) {
|
|
51
|
-
throw new UsageError(`Task "${id}" already exists. Pass --force to overwrite, or
|
|
64
|
+
throw new UsageError(`Task "${id}" already exists. Pass --force to overwrite, or delete its file and run \`akm task sync\` first.`, "RESOURCE_ALREADY_EXISTS");
|
|
52
65
|
}
|
|
53
66
|
const yaml = renderTaskYaml({
|
|
54
67
|
id,
|
|
@@ -56,7 +69,9 @@ export async function akmTasksAdd(input) {
|
|
|
56
69
|
workflow: input.workflow,
|
|
57
70
|
prompt: input.prompt,
|
|
58
71
|
command: input.command,
|
|
59
|
-
|
|
72
|
+
engine: input.engine,
|
|
73
|
+
model: input.model,
|
|
74
|
+
timeoutMs: input.timeoutMs,
|
|
60
75
|
params: input.params,
|
|
61
76
|
name: input.name,
|
|
62
77
|
description: input.description,
|
|
@@ -67,231 +82,187 @@ export async function akmTasksAdd(input) {
|
|
|
67
82
|
const task = parseTaskDocument({ yaml, filePath: assetPath, id });
|
|
68
83
|
await validateTaskDocument(task, { backend, stashDir });
|
|
69
84
|
const ref = taskAssetRef(id);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
85
|
+
const previousYaml = fs.existsSync(assetPath) ? fs.readFileSync(assetPath, "utf8") : undefined;
|
|
86
|
+
let previousTask;
|
|
87
|
+
let previousTaskError;
|
|
88
|
+
if (previousYaml !== undefined) {
|
|
89
|
+
try {
|
|
90
|
+
previousTask = parseTaskDocument({ yaml: previousYaml, filePath: assetPath, id });
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
previousTaskError = err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const sched = deps.backend ?? selectBackend();
|
|
97
|
+
const writeAsset = deps.writeAsset ?? writeAssetToSource;
|
|
98
|
+
const deleteAsset = deps.deleteAsset ?? deleteAssetFromSource;
|
|
99
|
+
const commitBoundary = deps.commitBoundary ?? commitWriteTargetBoundary;
|
|
100
|
+
const installedEntries = await sched.list();
|
|
101
|
+
assertNoForeignSchedule(installedEntries, id, bundle.installTarget);
|
|
102
|
+
const wasInstalled = previousYaml !== undefined && installedEntries.some((entry) => entry.id === id);
|
|
103
|
+
const installedEntry = installedEntries.find((entry) => entry.id === id);
|
|
104
|
+
const runtimeOpts = schedulerInstallOptions(installOpts, installedEntry, deps, installedEntry ? false : input.rebind === true, `create scheduler entry for task "${id}"`);
|
|
105
|
+
let sourceRestoreArmed = false;
|
|
106
|
+
let installSucceeded = false;
|
|
74
107
|
try {
|
|
75
|
-
|
|
76
|
-
await
|
|
108
|
+
sourceRestoreArmed = true;
|
|
109
|
+
await writeAsset(writeTarget.source, writeTarget.config, ref, yaml);
|
|
110
|
+
await sched.install(task, runtimeOpts);
|
|
111
|
+
installSucceeded = true;
|
|
112
|
+
commitBoundary(writeTarget, `Update tasks/${id}`);
|
|
77
113
|
}
|
|
78
114
|
catch (err) {
|
|
79
|
-
|
|
80
|
-
|
|
115
|
+
const rollbackErrors = [];
|
|
116
|
+
let sourceRestored = false;
|
|
117
|
+
if (sourceRestoreArmed) {
|
|
118
|
+
try {
|
|
119
|
+
if (previousYaml === undefined) {
|
|
120
|
+
if (fs.existsSync(assetPath)) {
|
|
121
|
+
await deleteAsset(writeTarget.source, writeTarget.config, ref);
|
|
122
|
+
sourceRestored = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
await restoreTaskSourceBytes(writeAsset, writeTarget.source, writeTarget.config, ref, assetPath, previousYaml);
|
|
127
|
+
sourceRestored = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (rollbackError) {
|
|
131
|
+
rollbackErrors.push(rollbackError);
|
|
132
|
+
}
|
|
81
133
|
}
|
|
82
|
-
|
|
83
|
-
|
|
134
|
+
if (installSucceeded && !wasInstalled) {
|
|
135
|
+
try {
|
|
136
|
+
await sched.uninstall(id);
|
|
137
|
+
}
|
|
138
|
+
catch (rollbackError) {
|
|
139
|
+
rollbackErrors.push(rollbackError);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (installSucceeded && previousTask) {
|
|
143
|
+
try {
|
|
144
|
+
await sched.install(previousTask, runtimeOpts);
|
|
145
|
+
}
|
|
146
|
+
catch (rollbackError) {
|
|
147
|
+
rollbackErrors.push(rollbackError);
|
|
148
|
+
try {
|
|
149
|
+
if (typeof sched.setEnabled !== "function") {
|
|
150
|
+
throw new Error(`Scheduler backend "${sched.name}" cannot disable task "${id}".`);
|
|
151
|
+
}
|
|
152
|
+
await sched.setEnabled(id, false);
|
|
153
|
+
}
|
|
154
|
+
catch (disableError) {
|
|
155
|
+
rollbackErrors.push(disableError);
|
|
156
|
+
try {
|
|
157
|
+
await sched.uninstall(id);
|
|
158
|
+
}
|
|
159
|
+
catch (uninstallError) {
|
|
160
|
+
rollbackErrors.push(uninstallError);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (installSucceeded && wasInstalled) {
|
|
166
|
+
rollbackErrors.push(previousTaskError ?? new Error(`Prior task "${id}" could not be restored.`));
|
|
167
|
+
}
|
|
168
|
+
if (sourceRestored) {
|
|
169
|
+
try {
|
|
170
|
+
commitBoundary(writeTarget, `Restore tasks/${id}`);
|
|
171
|
+
}
|
|
172
|
+
catch (rollbackError) {
|
|
173
|
+
rollbackErrors.push(rollbackError);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (rollbackErrors.length > 0) {
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
throw new AggregateError([err, ...rollbackErrors], `${message}; rollback for task "${id}" was incomplete.`);
|
|
84
179
|
}
|
|
85
180
|
throw err;
|
|
86
181
|
}
|
|
87
|
-
commitWriteTargetBoundary(target, `Update task:${id}`);
|
|
88
182
|
return {
|
|
89
183
|
id,
|
|
90
|
-
ref:
|
|
184
|
+
ref: conceptIdFromTypeName("task", id),
|
|
91
185
|
path: assetPath,
|
|
92
|
-
stashDir,
|
|
186
|
+
bundleDir: stashDir,
|
|
93
187
|
schedule: task.schedule,
|
|
94
188
|
enabled: task.enabled,
|
|
95
189
|
backend,
|
|
96
190
|
target: task.target,
|
|
97
191
|
};
|
|
98
192
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* leftover `.md` files from 0.7.x would otherwise be silently skipped, which
|
|
103
|
-
* makes scheduled tasks vanish without operator notice. We do NOT auto-migrate
|
|
104
|
-
* — that is a separate workstream — but operators must see the affected files.
|
|
105
|
-
*
|
|
106
|
-
* `seen` is module-level so the warning is emitted at most once per process,
|
|
107
|
-
* even when both `akm tasks list` and `akm tasks sync` are invoked in the same
|
|
108
|
-
* akm run.
|
|
109
|
-
*/
|
|
110
|
-
const warnedLegacyMdDirs = new Set();
|
|
111
|
-
function warnLegacyMdTaskFiles(typeRoot) {
|
|
112
|
-
if (warnedLegacyMdDirs.has(typeRoot))
|
|
113
|
-
return;
|
|
114
|
-
let mdFiles;
|
|
193
|
+
export async function akmTasksRun(id, options = {}) {
|
|
194
|
+
const startedAt = new Date();
|
|
195
|
+
let normalised;
|
|
115
196
|
try {
|
|
116
|
-
|
|
197
|
+
normalised = parseTaskRef(id).id;
|
|
117
198
|
}
|
|
118
|
-
catch {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const affected = mdFiles.map((f) => `tasks/${f}`).join(", ");
|
|
125
|
-
process.stderr.write(`WARNING: ${mdFiles.length} task file(s) use the legacy .md format and were ignored.\n` +
|
|
126
|
-
` AKM 0.8.0 requires tasks as pure .yml. See docs/migration/v0.7-to-v0.8.md#task-definition-files-mdfrontmatter--yml.\n` +
|
|
127
|
-
` Affected: ${affected}\n`);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Reset the legacy `.md` task warning de-duplication state. Test-only escape
|
|
131
|
-
* hatch — production code should never call this.
|
|
132
|
-
*/
|
|
133
|
-
export function _resetLegacyMdTaskWarningStateForTests() {
|
|
134
|
-
warnedLegacyMdDirs.clear();
|
|
135
|
-
}
|
|
136
|
-
export async function akmTasksList() {
|
|
137
|
-
const stashDir = resolveStashDir();
|
|
138
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
139
|
-
if (!fs.existsSync(typeRoot))
|
|
140
|
-
return { tasks: [] };
|
|
141
|
-
const entries = fs.readdirSync(typeRoot);
|
|
142
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
143
|
-
const files = entries.filter((f) => f.endsWith(".yml"));
|
|
144
|
-
const tasks = [];
|
|
145
|
-
for (const file of files) {
|
|
146
|
-
const id = file.slice(0, -4);
|
|
147
|
-
const filePath = path.join(typeRoot, file);
|
|
148
|
-
let task;
|
|
149
|
-
try {
|
|
150
|
-
task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
continue; // skip malformed files; `akm tasks show <id>` will surface the error
|
|
154
|
-
}
|
|
155
|
-
tasks.push({
|
|
156
|
-
id: task.id,
|
|
157
|
-
ref: `task:${task.id}`,
|
|
158
|
-
path: filePath,
|
|
159
|
-
schedule: task.schedule,
|
|
160
|
-
enabled: task.enabled,
|
|
161
|
-
target: task.target,
|
|
162
|
-
name: task.name,
|
|
163
|
-
description: task.description,
|
|
164
|
-
when_to_use: task.when_to_use,
|
|
165
|
-
tags: task.tags,
|
|
199
|
+
catch (failure) {
|
|
200
|
+
recordTaskAttemptFailure({
|
|
201
|
+
taskId: INVALID_TASK_ATTEMPT_ID,
|
|
202
|
+
reason: "invalid_task_id",
|
|
203
|
+
failure,
|
|
204
|
+
startedAt,
|
|
166
205
|
});
|
|
206
|
+
throw failure;
|
|
167
207
|
}
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
export async function akmTasksShow(id) {
|
|
171
|
-
const normalised = normaliseTaskId(id);
|
|
172
|
-
const stashDir = resolveStashDir();
|
|
173
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
174
|
-
if (fs.existsSync(typeRoot))
|
|
175
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
176
|
-
const filePath = await resolveAssetPath(stashDir, "task", normalised);
|
|
177
|
-
const task = parseTaskDocument({
|
|
178
|
-
yaml: fs.readFileSync(filePath, "utf8"),
|
|
179
|
-
filePath,
|
|
180
|
-
id: normalised,
|
|
181
|
-
});
|
|
182
|
-
const spec = parseSchedule(task.schedule, backendNameForPlatform());
|
|
183
|
-
return {
|
|
184
|
-
id: task.id,
|
|
185
|
-
ref: `task:${task.id}`,
|
|
186
|
-
path: filePath,
|
|
187
|
-
schedule: task.schedule,
|
|
188
|
-
cron: translateToCron(spec),
|
|
189
|
-
enabled: task.enabled,
|
|
190
|
-
target: task.target,
|
|
191
|
-
name: task.name,
|
|
192
|
-
description: task.description,
|
|
193
|
-
when_to_use: task.when_to_use,
|
|
194
|
-
tags: task.tags,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
export async function akmTasksRemove(id) {
|
|
198
|
-
const normalised = normaliseTaskId(id);
|
|
199
|
-
const target = resolveTaskWriteTarget();
|
|
200
|
-
const stashDir = target.source.path;
|
|
201
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
202
|
-
if (fs.existsSync(typeRoot))
|
|
203
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
204
|
-
await resolveAssetPath(stashDir, "task", normalised);
|
|
205
|
-
const ref = taskAssetRef(normalised);
|
|
206
|
-
const sched = selectBackend();
|
|
207
|
-
let uninstallError;
|
|
208
|
-
let deleteError;
|
|
209
|
-
try {
|
|
210
|
-
await sched.uninstall(normalised);
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
uninstallError = err;
|
|
214
|
-
}
|
|
208
|
+
let stashDir;
|
|
215
209
|
try {
|
|
216
|
-
|
|
210
|
+
// No --bundle uses the primary stash. With --bundle, resolve (read-only)
|
|
211
|
+
// the named bundle so the task file and
|
|
212
|
+
// its relative asset refs load from that bundle's path.
|
|
213
|
+
stashDir =
|
|
214
|
+
options.target !== undefined
|
|
215
|
+
? resolveWriteTarget(loadConfig(), options.target, { requireWritable: false }).source.path
|
|
216
|
+
: resolveStashDir();
|
|
217
217
|
}
|
|
218
|
-
catch (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return { id: normalised, removed: true, backend: sched.name };
|
|
227
|
-
}
|
|
228
|
-
export async function akmTasksSetEnabled(id, enabled) {
|
|
229
|
-
const normalised = normaliseTaskId(id);
|
|
230
|
-
const target = resolveTaskWriteTarget();
|
|
231
|
-
const stashDir = target.source.path;
|
|
232
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
233
|
-
if (fs.existsSync(typeRoot))
|
|
234
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
235
|
-
const filePath = await resolveAssetPath(stashDir, "task", normalised);
|
|
236
|
-
const yaml = fs.readFileSync(filePath, "utf8");
|
|
237
|
-
const updated = setEnabledInYaml(yaml, enabled);
|
|
238
|
-
const ref = taskAssetRef(normalised);
|
|
239
|
-
await writeAssetToSource(target.source, target.config, ref, updated);
|
|
240
|
-
const sched = selectBackend();
|
|
241
|
-
try {
|
|
242
|
-
// Reinstall from the (just-updated) definition rather than only toggling
|
|
243
|
-
// the comment. A plain toggle leaves a stale schedule in place if the
|
|
244
|
-
// .yml's `schedule:` changed while the task was disabled — re-enabling
|
|
245
|
-
// would silently keep the old cron line. install() renders the block with
|
|
246
|
-
// both the current schedule and the new enabled state, and is idempotent.
|
|
247
|
-
const task = parseTaskDocument({ yaml: updated, filePath, id: normalised });
|
|
248
|
-
await sched.install(task);
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
// Roll the file back so the YAML source-of-truth and the OS
|
|
252
|
-
// scheduler don't diverge silently when the backend call fails.
|
|
253
|
-
await writeAssetToSource(target.source, target.config, ref, yaml);
|
|
254
|
-
throw err;
|
|
218
|
+
catch (failure) {
|
|
219
|
+
recordTaskAttemptFailure({
|
|
220
|
+
taskId: normalised,
|
|
221
|
+
reason: "task_load_failed",
|
|
222
|
+
failure,
|
|
223
|
+
startedAt,
|
|
224
|
+
});
|
|
225
|
+
throw failure;
|
|
255
226
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const normalised = normaliseTaskId(id);
|
|
261
|
-
const stashDir = resolveStashDir();
|
|
262
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
263
|
-
if (fs.existsSync(typeRoot))
|
|
264
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
265
|
-
const result = await runTask(normalised);
|
|
227
|
+
const result = await runTask(normalised, { stashDir, scheduled: options.scheduled === true });
|
|
228
|
+
const exitCode = result.status === "failed" && result.target.kind === "command" && result.detail?.exitCode === 78
|
|
229
|
+
? 78
|
|
230
|
+
: exitCodeForStatus(result.status);
|
|
266
231
|
return {
|
|
267
232
|
ok: result.status === "completed" || result.status === "disabled",
|
|
268
233
|
result,
|
|
269
|
-
exitCode
|
|
234
|
+
exitCode,
|
|
270
235
|
};
|
|
271
236
|
}
|
|
272
237
|
export async function akmTasksHistory(input) {
|
|
273
238
|
const limit = input.limit !== undefined && input.limit > 0 ? input.limit : 50;
|
|
274
239
|
const id = input.id ? normaliseTaskId(input.id) : undefined;
|
|
275
|
-
|
|
276
|
-
const typeRoot = path.join(stashDir, "tasks");
|
|
277
|
-
if (fs.existsSync(typeRoot))
|
|
278
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
240
|
+
// History rows are keyed by task id in state.db, not per bundle.
|
|
279
241
|
return { rows: readTaskHistory({ id, limit }) };
|
|
280
242
|
}
|
|
281
243
|
/**
|
|
282
|
-
* Reconcile the on-disk task files with the OS scheduler.
|
|
244
|
+
* Reconcile the on-disk task files of ONE bundle with the OS scheduler.
|
|
283
245
|
* • install missing tasks (after validating them — invalid files are
|
|
284
246
|
* skipped with a per-task reason rather than aborting the whole sync)
|
|
285
247
|
* • reinstall tasks whose schedule or enabled state changed in the .yml
|
|
286
248
|
* (drift detected by comparing the backend's installed signature against
|
|
287
249
|
* the signature the current definition would produce)
|
|
288
250
|
* • remove orphan scheduler entries that no longer have a backing file
|
|
251
|
+
*
|
|
252
|
+
* `--bundle <bundle>` scopes the reconciliation to that bundle: the file set is
|
|
253
|
+
* the bundle's `tasks/*.yml` and — crucially — the scheduler entries considered
|
|
254
|
+
* are ONLY those attributed to the same bundle (parsed from the installed
|
|
255
|
+
* `--bundle` token; absent ⇒ primary). This is the security boundary that keeps
|
|
256
|
+
* "registering a bundle never activates code": a plain (primary) sync never
|
|
257
|
+
* installs from, updates, or removes another bundle's entries, and sync never
|
|
258
|
+
* scans all bundles. Activation happens only through explicit `add --bundle`
|
|
259
|
+
* (or `sync --bundle` on a bundle whose task files are already present).
|
|
289
260
|
*/
|
|
290
|
-
export async function akmTasksSync(deps = {}) {
|
|
291
|
-
const stashDir =
|
|
261
|
+
export async function akmTasksSync(deps = {}, bundleTarget, options = {}) {
|
|
262
|
+
const stashDir = resolveTaskInspectDir(bundleTarget);
|
|
263
|
+
// Primary-bundle scheduler entries omit --bundle; other bundles carry it.
|
|
264
|
+
const syncTarget = bundleTarget !== undefined && !isPrimaryStashPath(stashDir) ? bundleTarget : undefined;
|
|
292
265
|
const typeRoot = path.join(stashDir, "tasks");
|
|
293
|
-
if (fs.existsSync(typeRoot))
|
|
294
|
-
warnLegacyMdTaskFiles(typeRoot);
|
|
295
266
|
const fileIds = fs.existsSync(typeRoot)
|
|
296
267
|
? fs
|
|
297
268
|
.readdirSync(typeRoot)
|
|
@@ -299,34 +270,58 @@ export async function akmTasksSync(deps = {}) {
|
|
|
299
270
|
.map((f) => f.slice(0, -4))
|
|
300
271
|
: [];
|
|
301
272
|
const sched = deps.backend ?? selectBackend();
|
|
302
|
-
const backend =
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
273
|
+
const backend = sched.name;
|
|
274
|
+
const installOpts = syncTarget !== undefined ? { target: syncTarget } : undefined;
|
|
275
|
+
const allEntries = options.rebind && sched.listForRebind ? await sched.listForRebind() : await sched.list();
|
|
276
|
+
// Attribution filter: only entries installed from THIS bundle are reconciled
|
|
277
|
+
// here. Entries carrying a `--bundle` for a different bundle are invisible to
|
|
278
|
+
// this sync — never removed, never touched.
|
|
279
|
+
const present = new Map(allEntries.filter((t) => sameBundle(t.target, syncTarget)).map((t) => [t.id, t]));
|
|
306
280
|
const installed = [];
|
|
307
281
|
const updated = [];
|
|
308
282
|
const unchanged = [];
|
|
309
283
|
const skipped = [];
|
|
284
|
+
const warnings = [];
|
|
310
285
|
for (const id of fileIds) {
|
|
311
286
|
const filePath = path.join(typeRoot, `${id}.yml`);
|
|
312
287
|
let task;
|
|
313
288
|
try {
|
|
314
289
|
task = parseTaskDocument({ yaml: fs.readFileSync(filePath, "utf8"), filePath, id });
|
|
315
|
-
}
|
|
316
|
-
catch (err) {
|
|
317
|
-
skipped.push({ id, reason: err instanceof Error ? err.message : String(err) });
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
try {
|
|
321
290
|
await validateTaskDocument(task, { backend, stashDir });
|
|
291
|
+
// A bare id can only be scheduled from ONE bundle at a time (scheduler ids
|
|
292
|
+
// are never namespaced). If this id is already scheduled from a different
|
|
293
|
+
// bundle, refuse rather than clobber it — surface it as a per-task skip so
|
|
294
|
+
// the rest of the sync still proceeds.
|
|
295
|
+
const foreign = allEntries.find((e) => e.id === id && !sameBundle(e.target, syncTarget));
|
|
296
|
+
if (foreign)
|
|
297
|
+
throw new UsageError(foreignScheduleMessage(id, foreign.target), "RESOURCE_ALREADY_EXISTS");
|
|
322
298
|
}
|
|
323
299
|
catch (err) {
|
|
324
300
|
skipped.push({ id, reason: err instanceof Error ? err.message : String(err) });
|
|
301
|
+
if (present.has(id)) {
|
|
302
|
+
try {
|
|
303
|
+
await sched.setEnabled(id, false);
|
|
304
|
+
}
|
|
305
|
+
catch (disableError) {
|
|
306
|
+
try {
|
|
307
|
+
await sched.uninstall(id);
|
|
308
|
+
}
|
|
309
|
+
catch (uninstallError) {
|
|
310
|
+
throw new AggregateError([err, disableError, uninstallError], `Task "${id}" is invalid and its installed scheduler entry could not be disabled or removed.`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
325
314
|
continue;
|
|
326
315
|
}
|
|
327
316
|
if (!present.has(id)) {
|
|
328
|
-
|
|
329
|
-
|
|
317
|
+
try {
|
|
318
|
+
const runtimeOpts = schedulerInstallOptions(installOpts, undefined, deps, options.rebind === true, `create scheduler entry for task "${id}"`, warnings);
|
|
319
|
+
await sched.install(task, runtimeOpts);
|
|
320
|
+
installed.push(id);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
skipped.push({ id, reason: error instanceof Error ? error.message : String(error) });
|
|
324
|
+
}
|
|
330
325
|
continue;
|
|
331
326
|
}
|
|
332
327
|
// Already installed — reconcile against the current definition. Compare the
|
|
@@ -334,13 +329,15 @@ export async function akmTasksSync(deps = {}) {
|
|
|
334
329
|
// When the backend can't produce a signature (no expectedSignature, or it
|
|
335
330
|
// didn't record one), reinstall unconditionally — install() is idempotent,
|
|
336
331
|
// so the cost is one crontab write and correctness is guaranteed.
|
|
337
|
-
const
|
|
338
|
-
const
|
|
332
|
+
const installedEntry = present.get(id);
|
|
333
|
+
const runtimeOpts = schedulerInstallOptions(installOpts, options.rebind ? undefined : installedEntry, deps, options.rebind === true, `rebind scheduler entry for task "${id}"`, warnings);
|
|
334
|
+
const installedSig = installedEntry.signature;
|
|
335
|
+
const expectedSig = sched.expectedSignature?.(task, runtimeOpts);
|
|
339
336
|
if (installedSig !== undefined && expectedSig !== undefined && installedSig === expectedSig) {
|
|
340
337
|
unchanged.push(id);
|
|
341
338
|
}
|
|
342
339
|
else {
|
|
343
|
-
await sched.install(task);
|
|
340
|
+
await sched.install(task, runtimeOpts);
|
|
344
341
|
updated.push(id);
|
|
345
342
|
}
|
|
346
343
|
}
|
|
@@ -351,39 +348,76 @@ export async function akmTasksSync(deps = {}) {
|
|
|
351
348
|
removed.push(installedId);
|
|
352
349
|
}
|
|
353
350
|
}
|
|
354
|
-
return {
|
|
351
|
+
return {
|
|
352
|
+
installed,
|
|
353
|
+
updated,
|
|
354
|
+
removed,
|
|
355
|
+
unchanged,
|
|
356
|
+
skipped,
|
|
357
|
+
backend: sched.name,
|
|
358
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
359
|
+
};
|
|
355
360
|
}
|
|
356
|
-
export async function akmTasksDoctor() {
|
|
361
|
+
export async function akmTasksDoctor(deps = {}) {
|
|
357
362
|
const warnings = [];
|
|
358
|
-
let invocation = {
|
|
363
|
+
let invocation = {
|
|
364
|
+
argv: [],
|
|
365
|
+
via: "unresolved",
|
|
366
|
+
};
|
|
359
367
|
try {
|
|
360
|
-
const r = resolveAkmInvocation();
|
|
361
|
-
invocation = { argv: r.argv, via: r.via };
|
|
368
|
+
const r = (deps.resolveInvocation ?? resolveAkmInvocation)();
|
|
369
|
+
invocation = { argv: r.argv, via: r.via, kind: r.kind, eligible: r.eligible };
|
|
362
370
|
}
|
|
363
371
|
catch (err) {
|
|
364
372
|
warnings.push(err instanceof Error ? err.message : String(err));
|
|
365
373
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
374
|
+
const skipNativeInspection = process.env.BUN_TEST === "1" && !deps.backend;
|
|
375
|
+
const sched = deps.backend ?? (skipNativeInspection ? undefined : selectBackend());
|
|
376
|
+
const backend = sched?.name ?? backendNameForPlatform();
|
|
377
|
+
let installed = [];
|
|
378
|
+
if (skipNativeInspection) {
|
|
379
|
+
warnings.push("Native scheduler inspection is skipped inside the bun test harness.");
|
|
371
380
|
}
|
|
372
|
-
|
|
373
|
-
|
|
381
|
+
else {
|
|
382
|
+
try {
|
|
383
|
+
installed = await sched.list();
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
warnings.push(`Unable to inspect installed ${backend} definitions: ${error instanceof Error ? error.message : String(error)}`);
|
|
387
|
+
}
|
|
374
388
|
}
|
|
375
|
-
const
|
|
389
|
+
const bindings = groupInstalledBindings(installed, invocation);
|
|
376
390
|
const config = loadConfig();
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
const profiles = config.profiles?.agent ? Object.keys(config.profiles.agent) : listAgentProfileNames(config);
|
|
391
|
+
const defaultEngine = config.defaults?.engine;
|
|
392
|
+
const engines = Object.keys(config.engines ?? {});
|
|
380
393
|
// §6.1: surface the effective triage settings for the default improve
|
|
381
|
-
//
|
|
382
|
-
const
|
|
383
|
-
|
|
394
|
+
// strategy. The struct is a fixed shape, so this is a deliberate addition.
|
|
395
|
+
const improveStrategyName = typeof config.defaults?.improveStrategy === "string" ? config.defaults.improveStrategy : "default";
|
|
396
|
+
// D8 — report the EFFECTIVE strategy, not the raw one. Resolving the raw
|
|
397
|
+
// strategy here would report `applyMode: "promote"` for a promote strategy
|
|
398
|
+
// under a review-first config, while the run actually uses "queue" — a doctor
|
|
399
|
+
// command lying about the thing it exists to diagnose.
|
|
400
|
+
const rawStrategy = resolveImproveStrategy(config.defaults?.improveStrategy, config).config;
|
|
401
|
+
const { config: effectiveStrategy, gated } = applyAutonomyGate(rawStrategy, config);
|
|
402
|
+
const autonomyEnabled = isImproveAutonomyEnabled(config);
|
|
403
|
+
// Memory cleanup has no strategy flag to downgrade, so add that direct lane
|
|
404
|
+
// to the strategy-derived gate report.
|
|
405
|
+
const allGated = autonomyEnabled ? [] : [...gated, ...describeGatedLanes(configuredDirectAutonomyLanes())];
|
|
406
|
+
const improveAutonomy = {
|
|
407
|
+
enabled: autonomyEnabled,
|
|
408
|
+
configKey: IMPROVE_AUTONOMY_CONFIG_KEY,
|
|
409
|
+
gatedLanes: allGated.map((entry) => ({ lane: entry.lane, reason: entry.reason })),
|
|
410
|
+
};
|
|
411
|
+
// Q-05 — report the workflow engine gate's state alongside the autonomy
|
|
412
|
+
// gate: both are `experimental.*` opt-ins doctor exists to surface.
|
|
413
|
+
const workflowEngine = {
|
|
414
|
+
enabled: isWorkflowEngineEnabled(config),
|
|
415
|
+
configKey: WORKFLOW_ENGINE_CONFIG_KEY,
|
|
416
|
+
};
|
|
417
|
+
const triage = effectiveStrategy.processes?.triage;
|
|
384
418
|
const improveTriage = triage
|
|
385
419
|
? {
|
|
386
|
-
|
|
420
|
+
defaultStrategy: improveStrategyName,
|
|
387
421
|
enabled: triage.enabled === true,
|
|
388
422
|
applyMode: triage.applyMode ?? "queue",
|
|
389
423
|
policy: triage.policy ?? "personal-stash",
|
|
@@ -392,36 +426,172 @@ export async function akmTasksDoctor() {
|
|
|
392
426
|
return {
|
|
393
427
|
backend,
|
|
394
428
|
akm: invocation,
|
|
429
|
+
caller: invocation,
|
|
430
|
+
bindings,
|
|
431
|
+
...(bindings.some((binding) => !binding.status.includes("ok"))
|
|
432
|
+
? { remediation: "akm task sync --rebind" }
|
|
433
|
+
: {}),
|
|
395
434
|
logDir: getTaskLogDir(),
|
|
396
435
|
historyDir: getTaskHistoryDir(),
|
|
397
|
-
|
|
436
|
+
engine: { defaultEngine, available: engines },
|
|
398
437
|
scheduleSubset: SCHEDULE_SUPPORTED_SUBSET_HINT,
|
|
399
438
|
warnings,
|
|
439
|
+
improveAutonomy,
|
|
440
|
+
workflowEngine,
|
|
400
441
|
...(improveTriage ? { improveTriage } : {}),
|
|
401
442
|
};
|
|
402
443
|
}
|
|
403
444
|
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
445
|
+
function schedulerInstallOptions(base, installed, deps, explicitRebind, operation, warnings = []) {
|
|
446
|
+
if (installed && !explicitRebind) {
|
|
447
|
+
return {
|
|
448
|
+
...base,
|
|
449
|
+
binding: installed.binding,
|
|
450
|
+
contextPath: installed.contextPath,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
// Injected backends can own their default runtime unless a resolver is supplied.
|
|
454
|
+
if (deps.backend && !deps.schedulerRuntime)
|
|
455
|
+
return base;
|
|
456
|
+
const runtime = deps.schedulerRuntime?.() ?? prepareSchedulerRuntime(explicitRebind, operation);
|
|
457
|
+
// --rebind bypasses the eligibility refusal in prepareSchedulerRuntime; warn once
|
|
458
|
+
// per sync run rather than silently writing a mutable/unproven binary into cron.
|
|
459
|
+
if (explicitRebind && runtime.eligible === false && warnings.length === 0) {
|
|
460
|
+
warnings.push(`--rebind bound scheduled tasks to an ineligible ${runtime.kind ?? "unknown"} invocation (${runtime.binding.join(" ")}); scheduled runs will invoke a mutable, unproven binary. Install akm via \`npm install --global akm-cli\` or a standalone release, then re-run \`akm task sync --rebind\`.`);
|
|
461
|
+
}
|
|
462
|
+
return { ...base, binding: runtime.binding, contextPath: runtime.contextPath };
|
|
463
|
+
}
|
|
464
|
+
export function prepareSchedulerRuntime(explicitRebind, operation, deps = {}) {
|
|
465
|
+
const invocation = (deps.resolveInvocation ?? resolveAkmInvocation)();
|
|
466
|
+
if (!invocation.eligible && !explicitRebind) {
|
|
467
|
+
throw new UsageError(`Refusing to ${operation} from an ineligible ${invocation.kind ?? "unknown"} invocation (${invocation.argv.join(" ")}).`, "INVALID_FLAG_VALUE", "npm-global ownership could not be verified. Run `npm install --global akm-cli` and use that launcher, use a standalone installation, or explicitly repeat the operation with --rebind.");
|
|
468
|
+
}
|
|
469
|
+
const contextPath = (deps.writeDescriptor ?? writeSchedulerContextDescriptor)(schedulerContextDescriptor());
|
|
470
|
+
return { binding: invocation.argv, contextPath, eligible: invocation.eligible, kind: invocation.kind };
|
|
471
|
+
}
|
|
472
|
+
function groupInstalledBindings(entries, invocation) {
|
|
473
|
+
const groups = new Map();
|
|
474
|
+
for (const entry of entries) {
|
|
475
|
+
const argv = entry.binding;
|
|
476
|
+
const status = inspectInstalledBinding(entry, invocation);
|
|
477
|
+
const key = JSON.stringify([argv, entry.contextPath, status]);
|
|
478
|
+
const existing = groups.get(key);
|
|
479
|
+
if (existing) {
|
|
480
|
+
existing.taskIds.push(entry.id);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
groups.set(key, {
|
|
484
|
+
argv,
|
|
485
|
+
contextPath: entry.contextPath,
|
|
486
|
+
taskIds: [entry.id],
|
|
487
|
+
status,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
return [...groups.values()].map((group) => ({ ...group, taskIds: group.taskIds.sort() }));
|
|
491
|
+
}
|
|
492
|
+
function inspectInstalledBinding(entry, invocation) {
|
|
493
|
+
const status = [];
|
|
494
|
+
const binding = entry.binding;
|
|
495
|
+
if (!(invocation.eligible === true && sameArgv(binding, invocation.argv)) &&
|
|
496
|
+
binding.some((part) => /(?:^|[\\/])src[\\/]cli\.ts$|(?:^|[\\/])dist[\\/](?:cli\.js|cli-node\.mjs)$/i.test(part) ||
|
|
497
|
+
(path.isAbsolute(part) && hasGitAncestor(part)))) {
|
|
498
|
+
status.push("checkout");
|
|
499
|
+
}
|
|
500
|
+
if (binding.some((part) => part === "akm" || part === "bun" || part === "node"))
|
|
501
|
+
status.push("path-selected");
|
|
502
|
+
try {
|
|
503
|
+
validateSchedulerContextDescriptor(entry.contextPath);
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
status.push("invalid-context");
|
|
507
|
+
}
|
|
508
|
+
const absolutePaths = [...binding.filter((part) => path.isAbsolute(part)), entry.contextPath];
|
|
509
|
+
if (absolutePaths.some((part) => !fs.existsSync(part)))
|
|
510
|
+
status.push("missing-path");
|
|
511
|
+
if (status.length === 0)
|
|
512
|
+
status.push("ok");
|
|
513
|
+
return status;
|
|
514
|
+
}
|
|
515
|
+
function sameArgv(left, right) {
|
|
516
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
517
|
+
}
|
|
518
|
+
function hasGitAncestor(file) {
|
|
519
|
+
let current;
|
|
520
|
+
try {
|
|
521
|
+
current = path.dirname(fs.realpathSync(file));
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
for (;;) {
|
|
527
|
+
if (fs.existsSync(path.join(current, ".git")))
|
|
528
|
+
return true;
|
|
529
|
+
const parent = path.dirname(current);
|
|
530
|
+
if (parent === current)
|
|
531
|
+
return false;
|
|
532
|
+
current = parent;
|
|
533
|
+
}
|
|
416
534
|
}
|
|
417
535
|
function taskAssetRef(id) {
|
|
418
536
|
return { type: "task", name: id };
|
|
419
537
|
}
|
|
420
|
-
function
|
|
421
|
-
|
|
538
|
+
async function restoreTaskSourceBytes(writeAsset, source, config, ref, filePath, yaml) {
|
|
539
|
+
await writeAsset(source, config, ref, yaml);
|
|
540
|
+
// The normal write path adds a trailing newline; rollback restores the raw snapshot exactly.
|
|
541
|
+
fs.writeFileSync(filePath, yaml, "utf8");
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Resolve the bundle a mutating/run task command targets. Returns the resolved
|
|
545
|
+
* write/read target, its stash path, and the `--bundle <bundle>` token to embed
|
|
546
|
+
* in scheduled invocations. The primary bundle uses the target-less form.
|
|
547
|
+
*/
|
|
548
|
+
function resolveTaskBundle(target, opts) {
|
|
549
|
+
const selected = resolveWriteTarget(loadConfig(), target, { requireWritable: opts.requireWritable });
|
|
550
|
+
const resolved = opts.requireWritable ? prepareWriteTargetForMutation(selected) : selected;
|
|
551
|
+
const stashDir = resolved.source.path;
|
|
552
|
+
const installTarget = isPrimaryStashPath(stashDir) ? undefined : (resolved.selector ?? resolved.source.name);
|
|
553
|
+
return { resolved, stashDir, installTarget };
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Resolve the tasks/ directory a read/inspect command operates on. No
|
|
557
|
+
* `--bundle` uses the primary stash; `--bundle X` resolves bundle X read-only.
|
|
558
|
+
*/
|
|
559
|
+
function resolveTaskInspectDir(target) {
|
|
560
|
+
if (target === undefined)
|
|
561
|
+
return resolveStashDir();
|
|
562
|
+
return resolveWriteTarget(loadConfig(), target, { requireWritable: false }).source.path;
|
|
563
|
+
}
|
|
564
|
+
/** True when `candidate` resolves to the same directory as the primary stash. */
|
|
565
|
+
function isPrimaryStashPath(candidate) {
|
|
566
|
+
let primary;
|
|
567
|
+
try {
|
|
568
|
+
primary = path.resolve(resolveStashDir());
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
return path.resolve(candidate) === primary;
|
|
574
|
+
}
|
|
575
|
+
/** Two bundle attributions match when both are the primary (undefined) or equal names. */
|
|
576
|
+
function sameBundle(a, b) {
|
|
577
|
+
return (a ?? undefined) === (b ?? undefined);
|
|
578
|
+
}
|
|
579
|
+
function foreignScheduleMessage(id, existingTarget) {
|
|
580
|
+
const where = existingTarget === undefined ? "the default bundle" : `bundle "${existingTarget}"`;
|
|
581
|
+
return `Task id "${id}" is already scheduled from ${where}; rename the task or disable the existing one first.`;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Refuse to schedule an id already installed from a DIFFERENT bundle. Scheduler
|
|
585
|
+
* ids are the bare task id (never namespaced), so a single id can be active from
|
|
586
|
+
* only one bundle at a time — a collision is a hard error, not an auto-rename.
|
|
587
|
+
*/
|
|
588
|
+
function assertNoForeignSchedule(entries, id, installTarget) {
|
|
589
|
+
const foreign = entries.find((entry) => entry.id === id && !sameBundle(entry.target, installTarget));
|
|
590
|
+
if (foreign)
|
|
591
|
+
throw new UsageError(foreignScheduleMessage(id, foreign.target), "RESOURCE_ALREADY_EXISTS");
|
|
422
592
|
}
|
|
423
593
|
function renderTaskYaml(input) {
|
|
424
|
-
const obj = { schedule: input.schedule };
|
|
594
|
+
const obj = { version: 2, schedule: input.schedule, enabled: input.enabled };
|
|
425
595
|
if (input.workflow) {
|
|
426
596
|
obj.workflow = input.workflow;
|
|
427
597
|
if (input.params) {
|
|
@@ -430,16 +600,21 @@ function renderTaskYaml(input) {
|
|
|
430
600
|
}
|
|
431
601
|
else if (input.prompt) {
|
|
432
602
|
obj.prompt = input.prompt;
|
|
433
|
-
if (input.
|
|
434
|
-
obj.
|
|
603
|
+
if (input.engine)
|
|
604
|
+
obj.engine = input.engine;
|
|
605
|
+
if (input.model)
|
|
606
|
+
obj.model = input.model;
|
|
607
|
+
if (input.timeoutMs !== undefined)
|
|
608
|
+
obj.timeoutMs = input.timeoutMs;
|
|
435
609
|
}
|
|
436
610
|
else if (input.command !== undefined) {
|
|
437
611
|
// Emit a string when given a string, an array when given an array. The
|
|
438
612
|
// parser accepts both forms; preserving the caller's shape keeps the YAML
|
|
439
613
|
// ergonomic for humans editing the file later.
|
|
440
614
|
obj.command = input.command;
|
|
615
|
+
if (input.timeoutMs !== undefined)
|
|
616
|
+
obj.timeoutMs = input.timeoutMs;
|
|
441
617
|
}
|
|
442
|
-
obj.enabled = input.enabled;
|
|
443
618
|
if (input.name)
|
|
444
619
|
obj.name = input.name;
|
|
445
620
|
if (input.description)
|
|
@@ -487,17 +662,25 @@ export function setEnabledInYaml(yaml, enabled) {
|
|
|
487
662
|
// Re-exported so tests can verify the validator path directly.
|
|
488
663
|
// Re-export error classes consumed by callers that want to instanceof-check.
|
|
489
664
|
// Re-export this so the CLI can decide what process exit code to use after
|
|
490
|
-
// `akm
|
|
665
|
+
// `akm task run` completes.
|
|
491
666
|
export { ConfigError, exitCodeForStatus, NotFoundError, parseTaskDocument, UsageError };
|
|
492
|
-
//
|
|
493
|
-
// user passes a ref, we accept the bare name part too.
|
|
667
|
+
// Accept a bare task id or the canonical `[bundle//]tasks/<id>` ref.
|
|
494
668
|
export function parseTaskRef(input) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
669
|
+
const trimmed = input.trim();
|
|
670
|
+
// Canonical conceptId form: `[bundle//]tasks/<id>`. A `/` unambiguously marks
|
|
671
|
+
// it — a bare task id can never contain `/` (`validateTaskId` forbids it) — so
|
|
672
|
+
// route it through the shared parser, which strips any bundle prefix and maps
|
|
673
|
+
// the `tasks/` stash-subdir back to the `task` type in one place.
|
|
674
|
+
if (trimmed.includes("/")) {
|
|
675
|
+
try {
|
|
676
|
+
const parsed = parseRefInput(trimmed);
|
|
677
|
+
if (parsed.type === "task")
|
|
678
|
+
return { id: normaliseTaskId(parsed.name) };
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
// fall through to the shared error below
|
|
499
682
|
}
|
|
500
|
-
|
|
683
|
+
throw new UsageError(`Expected a task id or tasks/<id> ref, got "${input}".`, "INVALID_FLAG_VALUE");
|
|
501
684
|
}
|
|
502
|
-
return { id: normaliseTaskId(
|
|
685
|
+
return { id: normaliseTaskId(trimmed) };
|
|
503
686
|
}
|