akm-cli 0.9.0-rc.9 → 0.9.1-beta.1
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 +1770 -44
- package/README.md +38 -47
- package/SECURITY.md +14 -1
- package/STABILITY.md +499 -0
- package/dist/akm +148 -35
- package/dist/{akm-migrate-storage → akm-migrate} +6 -9
- package/dist/assets/hints/cli-hints-full.md +223 -95
- package/dist/assets/hints/cli-hints-short.md +85 -22
- package/dist/assets/improve-strategies/default.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/stash-skeleton/README.md +6 -5
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
- package/dist/assets/tasks/core/extract.yml +1 -1
- package/dist/assets/tasks/core/version-check.yml +1 -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 +1 -3
- package/dist/assets/workflows/workflow-template.md +32 -15
- package/dist/cli/invocation.js +40 -15
- package/dist/cli/parse-args.js +0 -22
- package/dist/cli/retired-commands.js +121 -0
- package/dist/cli/shared.js +154 -22
- package/dist/cli/unknown-flags.js +236 -0
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +696 -258
- package/dist/commands/agent/agent-dispatch.js +14 -3
- package/dist/commands/agent/contribute-cli.js +74 -89
- package/dist/commands/completions.js +79 -22
- package/dist/commands/config-cli.js +17 -150
- package/dist/commands/env/env-cli.js +59 -143
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +36 -66
- package/dist/commands/env/secret.js +24 -57
- package/dist/commands/feedback-cli.js +148 -88
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +3 -4
- package/dist/commands/health/checks.js +85 -23
- package/dist/commands/health/html-report.js +7 -10
- package/dist/commands/health/improve-metrics.js +25 -83
- package/dist/commands/health/llm-usage.js +2 -1
- package/dist/commands/health/md-report.js +5 -9
- package/dist/commands/health/metrics.js +62 -20
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +4 -5
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +4 -122
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/commands/health/types-improve.js +7 -0
- package/dist/commands/health.js +160 -35
- package/dist/commands/improve/anti-collapse.js +2 -2
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +41 -40
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +140 -1000
- package/dist/commands/improve/distill/promote-memory.js +12 -12
- package/dist/commands/improve/distill/quality-gate.js +11 -6
- package/dist/commands/improve/distill.js +58 -69
- package/dist/commands/improve/eligibility.js +101 -55
- package/dist/commands/improve/extract-cli.js +14 -133
- package/dist/commands/improve/improve-cli.js +98 -114
- package/dist/commands/improve/improve-result-file.js +1 -28
- package/dist/commands/improve/improve-strategies.js +8 -5
- package/dist/commands/improve/improve.js +254 -101
- package/dist/commands/improve/locks.js +7 -0
- package/dist/commands/improve/loop-stages.js +182 -20
- package/dist/commands/improve/memory/derived-ref.js +45 -43
- package/dist/commands/improve/memory/memory-belief.js +1 -1
- package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
- package/dist/commands/improve/memory/memory-improve.js +14 -5
- package/dist/commands/improve/outcome-loop.js +22 -65
- package/dist/commands/improve/preparation.js +114 -123
- package/dist/commands/improve/proactive-maintenance.js +2 -5
- package/dist/commands/improve/reflect.js +56 -160
- package/dist/commands/improve/run-context.js +5 -0
- package/dist/commands/improve/salience.js +11 -122
- package/dist/commands/improve/session-asset.js +4 -0
- package/dist/commands/improve/source-identity.js +10 -38
- package/dist/commands/lint/base-linter.js +51 -131
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +444 -88
- package/dist/commands/lint/types.js +22 -1
- package/dist/commands/{events.js → log.js} +33 -38
- package/dist/commands/migrate-cli.js +92 -12
- package/dist/commands/migration-tool.js +46 -0
- package/dist/commands/observability-cli.js +70 -209
- package/dist/commands/proposal/drain.js +101 -29
- package/dist/commands/proposal/proposal-cli.js +76 -48
- package/dist/commands/proposal/proposal.js +54 -18
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +23 -15
- package/dist/commands/proposal/repository.js +718 -279
- package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
- package/dist/commands/proposal/validators/proposal-validators.js +55 -7
- package/dist/commands/proposal/validators/proposals.js +4 -7
- package/dist/commands/read/curate.js +34 -53
- package/dist/commands/read/knowledge.js +150 -95
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +42 -15
- package/dist/commands/read/search-cli.js +180 -78
- package/dist/commands/read/search.js +58 -43
- package/dist/commands/read/show.js +197 -141
- package/dist/commands/registry-cli.js +12 -51
- package/dist/commands/remember.js +14 -57
- package/dist/commands/sources/add-cli.js +100 -31
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +7 -2
- package/dist/commands/sources/info.js +30 -7
- package/dist/commands/sources/init.js +12 -12
- package/dist/commands/sources/installed-stashes.js +387 -98
- package/dist/commands/sources/schema-repair.js +3 -2
- package/dist/commands/sources/self-update.js +131 -38
- package/dist/commands/sources/source-add.js +72 -17
- package/dist/commands/sources/source-clone.js +129 -45
- package/dist/commands/sources/source-manage.js +43 -23
- package/dist/commands/sources/sources-cli.js +57 -208
- package/dist/commands/sources/stash-cli.js +46 -53
- package/dist/commands/tasks/tasks-cli.js +91 -97
- package/dist/commands/tasks/tasks.js +284 -423
- package/dist/commands/workflow-cli.js +181 -450
- package/dist/core/abort-deadline.js +28 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
- package/dist/core/adapter/adapters/akm-adapter.js +60 -38
- package/dist/core/adapter/adapters/akm-lint.js +109 -42
- package/dist/core/adapter/adapters/akm-metadata.js +15 -44
- package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
- package/dist/core/adapter/adapters/index.js +6 -6
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
- package/dist/core/adapter/adapters/okf-adapter.js +187 -19
- package/dist/core/adapter/adapters/shared.js +3 -19
- package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +6 -4
- package/dist/core/adapter/validate-context.js +214 -0
- package/dist/core/asset/akm-markdown.js +63 -0
- package/dist/core/asset/asset-placement.js +20 -6
- package/dist/core/asset/asset-ref.js +11 -9
- package/dist/core/asset/frontmatter-lint.js +30 -0
- package/dist/core/asset/frontmatter.js +29 -9
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +89 -18
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +218 -44
- package/dist/core/concurrent.js +32 -0
- package/dist/core/config/config-io.js +12 -1
- package/dist/core/config/config-schema.js +35 -8
- package/dist/core/config/config-sources.js +55 -11
- package/dist/core/config/config-walker.js +25 -9
- package/dist/core/config/config.js +9 -48
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +5 -1
- package/dist/core/config/schema/experimental.js +30 -0
- package/dist/core/config/schema/improve-processes.js +0 -6
- package/dist/core/config/schema/improve.js +21 -3
- package/dist/core/config/schema/index-config.js +8 -15
- package/dist/core/config/schema/output.js +4 -1
- package/dist/core/config/schema/setup.js +9 -18
- package/dist/core/config/schema/sources-bundles.js +49 -33
- package/dist/core/config/schema/workflow.js +14 -3
- package/dist/core/env-secret-ref.js +76 -46
- package/dist/core/errors.js +43 -12
- package/dist/core/events.js +76 -152
- package/dist/core/file-change.js +6 -5
- package/dist/core/file-lock.js +7 -1
- package/dist/core/fs-txn.js +83 -7
- package/dist/core/git-message.js +2 -2
- package/dist/core/improve-result.js +6 -100
- package/dist/core/json-schema.js +327 -9
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +2 -1
- package/dist/core/loopback.js +89 -0
- package/dist/core/migration-operation.js +33 -2
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/parse.js +4 -1
- package/dist/core/path-access.js +107 -0
- package/dist/core/paths.js +17 -20
- package/dist/core/recognition-util.js +12 -14
- package/dist/core/redaction.js +98 -17
- package/dist/core/spawn-env.js +234 -0
- package/dist/core/standards/resolve-standards-context.js +2 -14
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +2 -2
- package/dist/core/state/migrations.js +41 -18
- package/dist/core/state-db-scope.js +134 -0
- package/dist/core/state-db.js +5 -14
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +182 -36
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/type-presentation.js +3 -3
- package/dist/core/warn.js +0 -3
- package/dist/core/write-provenance.js +85 -0
- package/dist/core/write-source.js +778 -95
- package/dist/indexer/bundle-identity-guard.js +3 -2
- package/dist/indexer/db/graph-db.js +17 -30
- package/dist/indexer/ensure-index.js +11 -3
- package/dist/indexer/graph/graph-boost.js +9 -34
- package/dist/indexer/graph/graph-extraction.js +8 -5
- package/dist/indexer/index-writer-lock.js +53 -17
- package/dist/indexer/index-written-assets.js +33 -24
- package/dist/indexer/indexer.js +551 -253
- package/dist/indexer/installations.js +14 -96
- package/dist/indexer/passes/dir-staleness.js +16 -9
- package/dist/indexer/passes/memory-inference.js +15 -9
- package/dist/indexer/passes/metadata.js +113 -47
- package/dist/indexer/scan/doc-to-entry.js +38 -1
- package/dist/indexer/scan/drain-dir.js +13 -23
- package/dist/indexer/search/db-search.js +107 -69
- package/dist/indexer/search/fts-query.js +47 -24
- package/dist/indexer/search/ranking-contributors.js +42 -20
- package/dist/indexer/search/ranking.js +18 -99
- package/dist/indexer/search/search-fields.js +7 -2
- package/dist/indexer/search/search-source.js +82 -93
- package/dist/indexer/usage/usage-events.js +0 -89
- package/dist/indexer/walk/file-context.js +2 -1
- package/dist/indexer/walk/matchers.js +30 -43
- package/dist/indexer/walk/path-resolver.js +7 -2
- package/dist/indexer/walk/walker.js +38 -12
- package/dist/integrations/agent/builders.js +0 -6
- package/dist/integrations/agent/config.js +2 -2
- package/dist/integrations/agent/detect.js +49 -19
- package/dist/integrations/agent/engine-fallback.js +76 -0
- package/dist/integrations/agent/engine-resolution.js +24 -11
- package/dist/integrations/agent/model-aliases.js +1 -1
- package/dist/integrations/agent/profiles.js +23 -1
- package/dist/integrations/agent/prompts.js +12 -8
- package/dist/integrations/agent/runner-dispatch.js +4 -2
- package/dist/integrations/agent/runner.js +0 -1
- package/dist/integrations/agent/spawn.js +20 -93
- package/dist/integrations/github.js +1 -1
- package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
- package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
- package/dist/integrations/harnesses/claude/session-log.js +0 -10
- package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
- package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
- package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
- package/dist/integrations/harnesses/index.js +3 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
- package/dist/integrations/harnesses/opencode/session-log.js +0 -15
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
- package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
- package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
- package/dist/integrations/lockfile.js +132 -8
- package/dist/integrations/session-logs/index.js +3 -28
- package/dist/llm/client.js +136 -100
- package/dist/llm/embedders/remote.js +13 -5
- package/dist/llm/feature-gate.js +4 -12
- package/dist/llm/graph-extract.js +5 -11
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +5 -7
- package/dist/llm/structured-call.js +1 -1
- package/dist/llm/usage-persist.js +26 -5
- package/dist/llm/usage-telemetry.js +25 -2
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +259 -0
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -36
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +56 -83
- package/dist/output/shapes/migrate.js +8 -0
- package/dist/output/shapes/passthrough.js +7 -41
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes.js +2 -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 +164 -96
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/health-format.js +103 -0
- package/dist/output/text/health.js +7 -0
- package/dist/output/text/helpers.js +10 -8
- package/dist/output/text/lint-format.js +56 -0
- package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
- package/dist/output/text/migrate.js +88 -0
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +44 -72
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/show-directives.js +15 -7
- package/dist/output/text/status-list.js +32 -0
- package/dist/output/text/{save.js → sync.js} +2 -2
- package/dist/output/text/workflow-format.js +24 -203
- package/dist/output/text/workflow.js +1 -7
- package/dist/output/text.js +16 -17
- 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 +13 -23
- package/dist/registry/resolve.js +42 -7
- package/dist/registry/semver.js +34 -84
- package/dist/runtime.js +2 -23
- package/dist/scripts/akm-migrate-node.js +60972 -0
- package/dist/scripts/akm-migrate.js +60310 -0
- package/dist/setup/detect.js +42 -15
- package/dist/setup/registry-stash-loader.js +2 -2
- package/dist/setup/setup.js +236 -136
- package/dist/setup/steps/connection.js +7 -9
- package/dist/setup/steps/platforms.js +9 -9
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +12 -13
- package/dist/setup/steps/stashdir.js +2 -3
- package/dist/setup/steps/tasks.js +237 -120
- package/dist/sources/freshness.js +1 -1
- 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 +25 -23
- package/dist/sources/providers/git-stash.js +414 -106
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +7 -4
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +6 -1
- package/dist/sources/resolve.js +6 -5
- package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
- package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
- package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
- package/dist/sources/snapshot-fetchers/github.js +100 -0
- package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
- package/dist/sources/snapshot-fetchers/registry.js +17 -1
- package/dist/sources/snapshot-fetchers/robots.js +348 -0
- package/dist/sources/snapshot-fetchers/rss.js +282 -0
- package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
- package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
- package/dist/sources/snapshot-fetchers/x.js +910 -0
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +23 -111
- package/dist/storage/managed-db.js +21 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +27 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -12
- package/dist/storage/repositories/index-connection.js +44 -8
- package/dist/storage/repositories/index-entries-repository.js +157 -241
- package/dist/storage/repositories/index-entry-mapper.js +15 -11
- package/dist/storage/repositories/index-fts-repository.js +5 -2
- package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
- package/dist/storage/repositories/index-meta-repository.js +2 -3
- package/dist/storage/repositories/index-schema.js +10 -25
- package/dist/storage/repositories/index-utility-repository.js +15 -28
- package/dist/storage/repositories/index-vec-repository.js +6 -1
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +296 -59
- package/dist/storage/repositories/registry-cache.js +19 -0
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +15 -13
- package/dist/storage/repositories/workflow-runs-repository.js +118 -53
- package/dist/tasks/backends/cron.js +105 -15
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +85 -38
- package/dist/tasks/backends/schtasks.js +135 -15
- package/dist/tasks/embedded.js +56 -40
- package/dist/tasks/log-redaction.js +156 -0
- package/dist/tasks/parser.js +87 -160
- package/dist/tasks/resolve-akm-bin.js +137 -59
- package/dist/tasks/runner.js +209 -44
- package/dist/tasks/scheduler-invocation.js +220 -10
- package/dist/tasks/schema.js +89 -1
- package/dist/tasks/task-id.js +1 -3
- package/dist/tasks/validator.js +20 -6
- package/dist/workflows/authoring/authoring.js +94 -143
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/concurrency-policy.js +95 -1
- package/dist/workflows/exec/dispatch-redaction.js +114 -0
- package/dist/workflows/exec/exec-unit.js +542 -0
- package/dist/workflows/exec/frozen-judge.js +130 -32
- package/dist/workflows/exec/native-executor.js +513 -274
- package/dist/workflows/exec/param-secrets.js +12 -11
- package/dist/workflows/exec/run-workflow.js +496 -237
- package/dist/workflows/exec/step-work.js +616 -412
- package/dist/workflows/exec/unit-dispatch.js +34 -0
- package/dist/workflows/exec/unit-writer.js +53 -13
- package/dist/workflows/exec/worktree.js +454 -41
- package/dist/workflows/ir/compile.js +166 -271
- package/dist/workflows/ir/freeze.js +119 -42
- package/dist/workflows/ir/params.js +135 -11
- package/dist/workflows/ir/plan-hash.js +1 -1
- package/dist/workflows/ir/schema.js +129 -45
- package/dist/workflows/parser.js +1070 -307
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +31 -10
- package/dist/workflows/renderer.js +123 -68
- package/dist/workflows/resource-limits.js +184 -0
- package/dist/workflows/runtime/checkin.js +3 -3
- package/dist/workflows/runtime/plan-classifier.js +16 -75
- package/dist/workflows/runtime/runs.js +331 -132
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +2 -2
- package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +47 -38
- package/dist/workflows/validator.js +21 -62
- package/docs/README.md +110 -0
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +11 -11
- package/docs/migration/release-notes/0.9.0.md +209 -27
- package/docs/migration/v0.7-to-v0.8.md +48 -48
- package/docs/migration/v0.8-to-v0.9.md +567 -209
- package/docs/migration/v0.9.0-troubleshooting.md +561 -0
- package/docs/reference/README.md +19 -0
- package/docs/reference/bundle-types.md +19 -0
- package/docs/reference/cli.md +2342 -0
- package/docs/reference/configuration.md +371 -0
- package/docs/reference/data-and-telemetry.md +129 -46
- package/docs/reference/supported-formats.md +50 -0
- package/docs/reference/workflow-schema.md +1014 -0
- package/docs/reference/workflows.md +51 -0
- package/package.json +31 -13
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +89 -128
- package/schemas/akm-task.json +27 -5
- package/schemas/akm-workflow.json +157 -81
- package/dist/assets/tasks/core/backup.yml +0 -5
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/cli/config-migrate.js +0 -1878
- package/dist/cli/config-validate.js +0 -41
- package/dist/commands/backup-cli.js +0 -56
- package/dist/commands/bundle/bundle-cli.js +0 -68
- package/dist/commands/bundle/bundle.js +0 -219
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -489
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/mv-cli.js +0 -1221
- package/dist/commands/sources/history.js +0 -201
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/core/migration-backup.js +0 -1231
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/migrate/legacy/config-source-migration.js +0 -257
- package/dist/migrate/legacy/content-migration.js +0 -350
- package/dist/migrate/legacy/legacy-layout.js +0 -779
- package/dist/migrate/legacy/legacy-paths.js +0 -25
- package/dist/migrate/legacy/legacy-stash-json.js +0 -80
- package/dist/migrate/legacy/proposal-fs-import.js +0 -168
- package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
- package/dist/migrate/legacy/three-db-cutover.js +0 -845
- package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
- package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
- package/dist/migrate/legacy-ref-grammar.js +0 -214
- 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/registry/build-index.js +0 -382
- package/dist/schemas/akm-config.json +0 -4704
- package/dist/schemas/akm-task.json +0 -87
- package/dist/schemas/akm-workflow.json +0 -372
- package/dist/scripts/migrate-storage.js +0 -3816
- package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/cli.js +0 -53
- package/dist/workflows/exec/brief.js +0 -481
- package/dist/workflows/exec/report.js +0 -1460
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -813
- package/dist/workflows/program/project.js +0 -104
|
@@ -28,24 +28,12 @@ function refType(ref) {
|
|
|
28
28
|
const body = ref.includes("//") ? ref.slice(ref.indexOf("//") + 2) : ref;
|
|
29
29
|
// 0.9.0 conceptId `<stash-subdir>/<name>`: delegate to the D-R2 reverse table
|
|
30
30
|
// so the leading stash subdir maps back to its asset type (the canonical path).
|
|
31
|
-
|
|
32
|
-
if (conceptType !== undefined)
|
|
33
|
-
return conceptType;
|
|
34
|
-
// DOCUMENTED EXCEPTION (ref-grammar decision D-R3 migration window): a tolerant
|
|
35
|
-
// legacy `type:name` arm survives ONLY because live callers still hand this
|
|
36
|
-
// recognition-only seam the old spelling — `propose.ts` builds
|
|
37
|
-
// `${options.type}:${options.name}`, and a pre-migration stored ref may still
|
|
38
|
-
// reach here before the 0.10.0 grammar removal. It never crosses a storage
|
|
39
|
-
// boundary, so it stays until those feeders flip.
|
|
40
|
-
const colon = body.indexOf(":");
|
|
41
|
-
if (colon > 0)
|
|
42
|
-
return body.slice(0, colon).trim() || undefined;
|
|
43
|
-
return undefined;
|
|
31
|
+
return typeNameFromConceptId(body)?.type;
|
|
44
32
|
}
|
|
45
33
|
/**
|
|
46
34
|
* Resolve the standards context for a write target identified by its asset ref.
|
|
47
35
|
*
|
|
48
|
-
* @param ref Canonical asset ref of the write target (e.g. `
|
|
36
|
+
* @param ref Canonical asset ref of the write target (e.g. `skills/foo`).
|
|
49
37
|
* When undefined, the target is a general authoring flow.
|
|
50
38
|
* @param stashRoot Stash root directory.
|
|
51
39
|
*/
|
|
@@ -53,14 +53,14 @@ function collectMarkdownFiles(dir) {
|
|
|
53
53
|
return results;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* Derive a fact ref (`
|
|
56
|
+
* Derive a fact ref (`facts/conventions/naming`) from an absolute markdown path
|
|
57
57
|
* relative to the facts root. Mirrors the canonical-name derivation in
|
|
58
58
|
* `asset-spec.ts` (POSIX separators, `.md` stripped).
|
|
59
59
|
*/
|
|
60
60
|
function toFactRef(factsRoot, absPath) {
|
|
61
61
|
const rel = path.relative(factsRoot, absPath).split(path.sep).join("/");
|
|
62
62
|
const name = rel.endsWith(".md") ? rel.slice(0, -3) : rel;
|
|
63
|
-
return
|
|
63
|
+
return `${FACTS_SUBDIR}/${name}`;
|
|
64
64
|
}
|
|
65
65
|
export function resolveStashStandards(stashRoot) {
|
|
66
66
|
const factsRoot = path.join(stashRoot, FACTS_SUBDIR);
|
|
@@ -28,9 +28,9 @@ import { placementTypes } from "../asset/asset-placement.js";
|
|
|
28
28
|
import { parseFrontmatter } from "../asset/frontmatter.js";
|
|
29
29
|
/** Sub-path (under the stash root) for per-type SOFT convention facts. */
|
|
30
30
|
export const TYPE_CONVENTIONS_SUBDIR = path.join("facts", "conventions", "assets");
|
|
31
|
-
/** The
|
|
31
|
+
/** The slash conceptId for a per-type convention, e.g. `facts/conventions/assets/skill`. */
|
|
32
32
|
export function typeConventionRef(type) {
|
|
33
|
-
return `
|
|
33
|
+
return `facts/conventions/assets/${type}`;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Read the SOFT authoring-convention body for asset type `type`, if a stash
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
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
|
-
import { runMigrations as runSqliteMigrations } from "../../storage/engines/sqlite-migrations.js";
|
|
4
|
+
import { assertMigrationRegistry, runMigrations as runSqliteMigrations, } from "../../storage/engines/sqlite-migrations.js";
|
|
5
5
|
export const STATE_MIGRATIONS = [
|
|
6
6
|
// ── Migration 001 — initial schema ──────────────────────────────────────────
|
|
7
7
|
{
|
|
@@ -59,7 +59,7 @@ export const STATE_MIGRATIONS = [
|
|
|
59
59
|
-- id TEXT PK — UUID (crypto.randomUUID()); stable directory name.
|
|
60
60
|
-- stash_dir TEXT — absolute stash root; multi-stash installs need
|
|
61
61
|
-- this to partition proposal lists per stash.
|
|
62
|
-
-- ref TEXT — target asset ref (e.g. "
|
|
62
|
+
-- ref TEXT — target asset ref (e.g. "lessons/alpha");
|
|
63
63
|
-- indexed for ref-scoped queue views.
|
|
64
64
|
-- status TEXT — "pending" | "accepted" | "rejected"; indexed
|
|
65
65
|
-- so pending-queue queries are fast.
|
|
@@ -76,7 +76,7 @@ export const STATE_MIGRATIONS = [
|
|
|
76
76
|
-- metadata_json TEXT — JSON object for future proposal fields.
|
|
77
77
|
-- Current fields stored here: sourceRun,
|
|
78
78
|
-- review, confidence, gateDecision (#577),
|
|
79
|
-
-- backupContent
|
|
79
|
+
-- backupContent.
|
|
80
80
|
--
|
|
81
81
|
-- ADD COLUMN extension points (future migrations):
|
|
82
82
|
-- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
|
|
@@ -350,11 +350,12 @@ export const STATE_MIGRATIONS = [
|
|
|
350
350
|
//
|
|
351
351
|
// VESTIGIAL as of the Chunk-8 fold: the legacy `proposal.json` import moved
|
|
352
352
|
// OUT of the live per-operation path and INTO the one-time migrator
|
|
353
|
-
// (`
|
|
353
|
+
// (`scripts/akm-migrate/migrate/legacy/proposal-fs-import.ts`, wired through
|
|
354
|
+
// `akm-migrate apply`),
|
|
354
355
|
// whose idempotency is INSERT OR IGNORE on the proposal UUID plus migrate-apply's
|
|
355
|
-
//
|
|
356
|
-
// stays because
|
|
357
|
-
//
|
|
356
|
+
// incomplete sentinel — it no longer reads or writes this ledger. The CREATE TABLE
|
|
357
|
+
// stays because migration IDs are append-only: removing a released migration
|
|
358
|
+
// would make the schema_migrations ledger of an
|
|
358
359
|
// already-migrated rc database stop being an exact ordered prefix, and the
|
|
359
360
|
// runner would refuse to open it. The empty table is harmless.
|
|
360
361
|
//
|
|
@@ -832,17 +833,17 @@ export const STATE_MIGRATIONS = [
|
|
|
832
833
|
//
|
|
833
834
|
// The state.db half of the three-DB merge (plan §3.2/§8, normative §11.4,
|
|
834
835
|
// chunk-8 cutover design §1). This migration is PURE,
|
|
835
|
-
//
|
|
836
|
+
// IDEMPOTENT DDL ONLY — `CREATE TABLE IF NOT EXISTS` (+ indexes),
|
|
836
837
|
// never a DROP or a data move. The actual data movement (the workflow.db merge,
|
|
837
838
|
// the usage_events rescue from index.db, the full old-ref→item_ref re-key, and
|
|
838
|
-
// the workflow.db delete / index.db quarantine) is
|
|
839
|
-
//
|
|
840
|
-
//
|
|
841
|
-
// carve-out note in `src/core/state-db.ts`.
|
|
839
|
+
// the workflow.db delete / index.db quarantine) is idempotent migrate-apply
|
|
840
|
+
// code driven by
|
|
841
|
+
// `scripts/akm-migrate/migrate/legacy/three-db-cutover.ts`. See the no-DROP
|
|
842
|
+
// contract carve-out note in `src/core/state-db.ts`.
|
|
842
843
|
//
|
|
843
844
|
// The three workflow tables are the 10 pre-cutover workflow migrations
|
|
844
|
-
// (the frozen `
|
|
845
|
-
// 001–010) folded into one baseline at their FINAL post-010 shape — column
|
|
845
|
+
// (the frozen `scripts/akm-migrate/migrate/legacy/workflow-migrations-bodies.ts`
|
|
846
|
+
// base schema + 001–010) folded into one baseline at their FINAL post-010 shape — column
|
|
846
847
|
// lists, CHECK constraints, and indexes copied verbatim. `usage_events` mirrors
|
|
847
848
|
// index.db's former `ensureUsageEventsSchema` (`src/indexer/usage/usage-events.ts`),
|
|
848
849
|
// its new durable home. `legacy_state` mirrors `ensureLegacyStateTable`
|
|
@@ -958,7 +959,32 @@ export const STATE_MIGRATIONS = [
|
|
|
958
959
|
);
|
|
959
960
|
`,
|
|
960
961
|
},
|
|
962
|
+
// ── Migration 021 — asset_salience / asset_outcome: missing_since (#733) ────
|
|
963
|
+
//
|
|
964
|
+
// Orphan-GC grace clock (Workstream C / #733,
|
|
965
|
+
// docs/architecture/specs/0.9.0-close-out-plan.md). Neither table carries a
|
|
966
|
+
// column that records when a row's ref STOPPED resolving against
|
|
967
|
+
// `entries.item_ref` — `updated_at` is bumped by improve itself on every
|
|
968
|
+
// touch, so it cannot double as a "went missing" timestamp.
|
|
969
|
+
//
|
|
970
|
+
// `missing_since` is stamped by `runOrphanStateGcPass`
|
|
971
|
+
// (src/commands/improve/loop-stages.ts) the first time a row's ref fails to
|
|
972
|
+
// resolve against the live index, cleared the moment the ref resolves
|
|
973
|
+
// again, and used to gate deletion once `STATE_GC_GRACE_MS` (7 days) has
|
|
974
|
+
// elapsed — and ONLY when `improve.stateGc.collect` is true (default
|
|
975
|
+
// false). Mirrors the additive ADD-COLUMN style of migration 011.
|
|
976
|
+
//
|
|
977
|
+
// NULL = never missing (the steady-state default for every existing row —
|
|
978
|
+
// both a fresh row and a pre-migration row read back NULL here).
|
|
979
|
+
{
|
|
980
|
+
id: "021-asset-state-missing-since",
|
|
981
|
+
up: `
|
|
982
|
+
ALTER TABLE asset_salience ADD COLUMN missing_since INTEGER DEFAULT NULL;
|
|
983
|
+
ALTER TABLE asset_outcome ADD COLUMN missing_since INTEGER DEFAULT NULL;
|
|
984
|
+
`,
|
|
985
|
+
},
|
|
961
986
|
];
|
|
987
|
+
assertMigrationRegistry(STATE_MIGRATIONS);
|
|
962
988
|
/**
|
|
963
989
|
* Apply every pending migration in a single transaction per migration.
|
|
964
990
|
*
|
|
@@ -968,8 +994,5 @@ export const STATE_MIGRATIONS = [
|
|
|
968
994
|
* Called automatically by `openStateDatabase()`.
|
|
969
995
|
*/
|
|
970
996
|
export function runMigrations(db, options) {
|
|
971
|
-
runSqliteMigrations(db, STATE_MIGRATIONS, {
|
|
972
|
-
applyPending: options?.applyPending,
|
|
973
|
-
generationMarker: options?.generationMarker,
|
|
974
|
-
});
|
|
997
|
+
runSqliteMigrations(db, STATE_MIGRATIONS, { applyPending: options?.applyPending });
|
|
975
998
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Ambient, run-scoped state.db connection reuse.
|
|
6
|
+
*
|
|
7
|
+
* `openStateDatabase` is NOT cheap: every call registers a `state-db`
|
|
8
|
+
* maintenance activity (a lockfile create under the maintenance barrier), opens
|
|
9
|
+
* a throwaway read-only preflight handle to assert the migration ledger, then
|
|
10
|
+
* opens the real handle and applies pragmas. Paying that per repository call
|
|
11
|
+
* — twice per dispatched workflow unit (insert + finish), plus once per
|
|
12
|
+
* `appendEvent` (two events per unit) — is the dominant cost of a wide `map`
|
|
13
|
+
* fan-out.
|
|
14
|
+
*
|
|
15
|
+
* This module adds the ONE thing the codebase was missing: a way to say "for
|
|
16
|
+
* the duration of this async operation, everything that talks to state.db
|
|
17
|
+
* shares a single handle". It deliberately does NOT introduce a pool, a cache
|
|
18
|
+
* with an eviction policy, or a background keep-alive timer — those all leak
|
|
19
|
+
* handles across the test harness's per-test data-dir swaps. The scope owns
|
|
20
|
+
* exactly one connection and closes it in a `finally`.
|
|
21
|
+
*
|
|
22
|
+
* ## Why this is safe
|
|
23
|
+
*
|
|
24
|
+
* SQLite connections must not be shared across THREADS. This is a single
|
|
25
|
+
* process running a single-threaded JS event loop: `bun:sqlite` statement
|
|
26
|
+
* execution and `withImmediateTransaction`'s `BEGIN IMMEDIATE … COMMIT` bodies
|
|
27
|
+
* are fully synchronous, so two logically concurrent units can never interleave
|
|
28
|
+
* statements on the shared handle — the runtime cannot preempt a synchronous
|
|
29
|
+
* transaction body. Sharing one handle therefore REMOVES in-process writer
|
|
30
|
+
* contention (`SQLITE_BUSY` against ourselves) rather than creating it.
|
|
31
|
+
* Cross-process behaviour is untouched: WAL mode, the 30 s `busy_timeout` and
|
|
32
|
+
* the run-lease protocol all still arbitrate between processes exactly as
|
|
33
|
+
* before.
|
|
34
|
+
*
|
|
35
|
+
* The one hazard of an {@link AsyncLocalStorage}-carried handle is async work
|
|
36
|
+
* that ESCAPES the scope: the context propagates into a promise that settles
|
|
37
|
+
* after the scope's `finally` closed the handle. {@link borrowScopedStateDb}
|
|
38
|
+
* guards that with a `closed` flag — once a scope is torn down it stops
|
|
39
|
+
* lending, and escapee callers transparently fall back to opening their own
|
|
40
|
+
* connection (the pre-existing behaviour). A use-after-close is structurally
|
|
41
|
+
* impossible.
|
|
42
|
+
*/
|
|
43
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
44
|
+
import path from "node:path";
|
|
45
|
+
import { getStateDbPath, openStateDatabase } from "./state-db.js";
|
|
46
|
+
const scopeStorage = new AsyncLocalStorage();
|
|
47
|
+
/** Every scope with a live handle, so the exit backstop can close them all. */
|
|
48
|
+
const liveScopes = new Set();
|
|
49
|
+
let exitHookInstalled = false;
|
|
50
|
+
function installExitBackstop() {
|
|
51
|
+
if (exitHookInstalled)
|
|
52
|
+
return;
|
|
53
|
+
exitHookInstalled = true;
|
|
54
|
+
// Idempotent, synchronous backstop: a process that exits mid-scope (a
|
|
55
|
+
// `process.exit()` from a command, an uncaught fatal) still releases the
|
|
56
|
+
// handle and its maintenance-activity lockfile. Normal teardown happens in
|
|
57
|
+
// the scope's own `finally`, which removes the scope from `liveScopes`
|
|
58
|
+
// first, so this never double-closes.
|
|
59
|
+
process.on("exit", closeAllStateDbScopes);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Close every scope-owned handle. Idempotent and safe to call at any time: a
|
|
63
|
+
* closed scope stops lending, so in-flight borrowers fall back to their own
|
|
64
|
+
* connections instead of using a dead handle. Module-private: the per-scope
|
|
65
|
+
* `finally` plus the process-exit backstop above are what guarantee no handle
|
|
66
|
+
* leak — export this the day a caller actually wires deterministic teardown.
|
|
67
|
+
*/
|
|
68
|
+
function closeAllStateDbScopes() {
|
|
69
|
+
for (const scope of [...liveScopes])
|
|
70
|
+
closeScope(scope);
|
|
71
|
+
}
|
|
72
|
+
function closeScope(scope) {
|
|
73
|
+
scope.closed = true;
|
|
74
|
+
liveScopes.delete(scope);
|
|
75
|
+
const db = scope.db;
|
|
76
|
+
scope.db = undefined;
|
|
77
|
+
if (!db)
|
|
78
|
+
return;
|
|
79
|
+
try {
|
|
80
|
+
db.close();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// A close failure must never mask the caller's own error (or wedge exit).
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The ambient scoped handle for `dbPath` (default: the canonical state.db), or
|
|
88
|
+
* `undefined` when there is no live scope for that exact path.
|
|
89
|
+
*
|
|
90
|
+
* The path comparison matters: the test harness repoints `AKM_DATA_DIR` between
|
|
91
|
+
* tests, and a scope entered against one data dir must never lend its handle to
|
|
92
|
+
* a caller resolving a different one.
|
|
93
|
+
*/
|
|
94
|
+
export function borrowScopedStateDb(dbPath) {
|
|
95
|
+
const scope = scopeStorage.getStore();
|
|
96
|
+
if (!scope || scope.closed)
|
|
97
|
+
return undefined;
|
|
98
|
+
if (path.resolve(dbPath ?? getStateDbPath()) !== scope.dbPath)
|
|
99
|
+
return undefined;
|
|
100
|
+
scope.db ??= openStateDatabase(scope.dbPath);
|
|
101
|
+
liveScopes.add(scope);
|
|
102
|
+
return scope.db;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Run `fn` with one shared state.db connection ambient for its whole async
|
|
106
|
+
* extent. The handle is opened on FIRST borrow (never eagerly) and closed once
|
|
107
|
+
* `fn` settles.
|
|
108
|
+
*
|
|
109
|
+
* Nesting is a no-op join: an inner scope for the same path reuses the outer
|
|
110
|
+
* scope's handle and does not close it, so a caller can enter a scope without
|
|
111
|
+
* knowing whether an outer frame already did.
|
|
112
|
+
*/
|
|
113
|
+
export async function withStateDbScope(fn, opts) {
|
|
114
|
+
const dbPath = path.resolve(opts?.path ?? getStateDbPath());
|
|
115
|
+
const outer = scopeStorage.getStore();
|
|
116
|
+
if (outer && !outer.closed && outer.dbPath === dbPath)
|
|
117
|
+
return fn();
|
|
118
|
+
const scope = { dbPath, db: undefined, closed: false };
|
|
119
|
+
installExitBackstop();
|
|
120
|
+
try {
|
|
121
|
+
return await scopeStorage.run(scope, fn);
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
closeScope(scope);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Test seam: how many scope-owned handles are currently open. Must return to 0. */
|
|
128
|
+
export function openScopedStateDbCount() {
|
|
129
|
+
let count = 0;
|
|
130
|
+
for (const scope of liveScopes)
|
|
131
|
+
if (scope.db)
|
|
132
|
+
count++;
|
|
133
|
+
return count;
|
|
134
|
+
}
|
package/dist/core/state-db.js
CHANGED
|
@@ -45,11 +45,10 @@
|
|
|
45
45
|
* merge-target tables at their final shape. The one-time, filesystem-derived,
|
|
46
46
|
* fail-closed DATA movement (the workflow.db merge, the usage_events rescue, the
|
|
47
47
|
* full old-ref→item_ref re-key, and the workflow.db unlink / index.db quarantine
|
|
48
|
-
* rename)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* under the backup-verified-restorable fail-closed gate.
|
|
48
|
+
* rename) runs as idempotent code in the migrate-apply coordinator
|
|
49
|
+
* (`scripts/akm-migrate/migrate/legacy/three-db-cutover.ts`). So the no-DROP
|
|
50
|
+
* contract here is intact: physical workflow.db deletion happens outside the
|
|
51
|
+
* ledger DDL, after a verified backup and committed data transaction.
|
|
53
52
|
*
|
|
54
53
|
* ## Schema design: indexed columns vs. metadata_json
|
|
55
54
|
*
|
|
@@ -73,7 +72,7 @@ import fs from "node:fs";
|
|
|
73
72
|
import path from "node:path";
|
|
74
73
|
import { openDatabase } from "../storage/database.js";
|
|
75
74
|
import { assertCurrentMigrationLedger, assertMigrationLedger } from "../storage/engines/sqlite-migrations.js";
|
|
76
|
-
import { openManagedDatabase, withManagedDb
|
|
75
|
+
import { openManagedDatabase, withManagedDb } from "../storage/managed-db.js";
|
|
77
76
|
import { acquireMaintenanceActivitySync } from "./maintenance-barrier.js";
|
|
78
77
|
import { assertNoPendingMigrationOperation } from "./migration-operation.js";
|
|
79
78
|
import { getDataDir } from "./paths.js";
|
|
@@ -182,14 +181,6 @@ export function openStateDatabase(dbPath) {
|
|
|
182
181
|
export function withStateDb(fn, opts) {
|
|
183
182
|
return withManagedDb(() => openStateDatabase(opts?.path), fn, opts);
|
|
184
183
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Async sibling of {@link withStateDb} — for `fn`s that hold the handle across
|
|
187
|
-
* an `await` (the sync version would close it too early). Same borrow/own +
|
|
188
|
-
* optional `path` override semantics.
|
|
189
|
-
*/
|
|
190
|
-
export function withStateDbAsync(fn, opts) {
|
|
191
|
-
return withManagedDbAsync(() => openStateDatabase(opts?.path), fn, opts);
|
|
192
|
-
}
|
|
193
184
|
/**
|
|
194
185
|
* Fire-and-forget telemetry write to state.db (Chunk-8 WI-8.3: usage_events'
|
|
195
186
|
* durable home). Skips entirely when state.db does not exist yet (never
|
package/dist/core/structured.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Layering: this file lives in `core/` — NOT `llm/` — because the agent path
|
|
17
17
|
* must be able to reach it and `agent/ ⇏ llm/` is an enforced invariant
|
|
18
|
-
* (`tests/architecture/agent-no-llm-sdk-guard.test.ts`). The transport is
|
|
18
|
+
* (`tests/integration/architecture/agent-no-llm-sdk-guard.test.ts`). The transport is
|
|
19
19
|
* always injected; this module never performs IO of its own.
|
|
20
20
|
*
|
|
21
21
|
* Retry semantics: only parse/validation misses are retried (with corrective
|
package/dist/core/subprocess.js
CHANGED
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
* kill reaps the whole descendant tree — no orphaned children.
|
|
13
13
|
* • A SIGTERM→SIGKILL kill ladder on timeout/abort — a child that ignores
|
|
14
14
|
* SIGTERM is force-killed after a grace period instead of wedging forever.
|
|
15
|
-
* •
|
|
16
|
-
* wall budget even when the child leaves a pipe endpoint open
|
|
15
|
+
* • Time-bounded output capture ({@link readStream}) that cannot block past
|
|
16
|
+
* the wall budget even when the child leaves a pipe endpoint open, plus an
|
|
17
|
+
* OPT-IN RETENTION cap (`maxOutputBytes`); see {@link readStream} for what
|
|
18
|
+
* the cap does and does not bound.
|
|
17
19
|
* • Injectable `spawnFn`/`setTimeoutFn`/`clearTimeoutFn` seams so callers
|
|
18
20
|
* can drive the machinery deterministically in tests.
|
|
19
21
|
*
|
|
@@ -70,33 +72,115 @@ export function scheduleKillLadder(proc, opts) {
|
|
|
70
72
|
if (typeof sigkillTimer !== "number")
|
|
71
73
|
sigkillTimer.unref?.();
|
|
72
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* The joined, human-readable reason ONE managed run's capture is incomplete, or
|
|
77
|
+
* `undefined` when both pipes drained cleanly.
|
|
78
|
+
*
|
|
79
|
+
* Shared so every caller that promotes captured output treats an incomplete
|
|
80
|
+
* capture the same way. A pipe that errored, or that hit the stream-drain
|
|
81
|
+
* timeout because a background descendant kept the fd open after the leader
|
|
82
|
+
* exited 0, yields a PARTIAL string — and a caller that reads only `exitCode`
|
|
83
|
+
* would promote that partial as if it were the command's whole output.
|
|
84
|
+
* `maxBytes` overflow is deliberately NOT reported here: it is a distinct,
|
|
85
|
+
* caller-classified condition (see {@link StreamReadResult.overflowed}), not a
|
|
86
|
+
* drain malfunction.
|
|
87
|
+
*/
|
|
88
|
+
export function streamCaptureFailure(stdout, stderr) {
|
|
89
|
+
const failures = [];
|
|
90
|
+
if (stdout.error)
|
|
91
|
+
failures.push(`stdout read failed: ${errorText(stdout.error)}`);
|
|
92
|
+
if (stderr.error)
|
|
93
|
+
failures.push(`stderr read failed: ${errorText(stderr.error)}`);
|
|
94
|
+
if (stdout.timedOut)
|
|
95
|
+
failures.push("stdout drain timed out");
|
|
96
|
+
if (stderr.timedOut)
|
|
97
|
+
failures.push("stderr drain timed out");
|
|
98
|
+
return failures.length === 0 ? undefined : failures.join("; ");
|
|
99
|
+
}
|
|
100
|
+
function errorText(error) {
|
|
101
|
+
return error instanceof Error ? error.message : String(error);
|
|
102
|
+
}
|
|
73
103
|
const STREAM_READ_TIMEOUT = Symbol("stream-read-timeout");
|
|
104
|
+
/**
|
|
105
|
+
* Index at or before `limit` where a UTF-8 CHARACTER starts, so a retention cut
|
|
106
|
+
* never lands inside a multi-byte sequence. Continuation bytes are `10xxxxxx`;
|
|
107
|
+
* a well-formed sequence has at most three of them, so this walks back at most
|
|
108
|
+
* three positions.
|
|
109
|
+
*/
|
|
110
|
+
function utf8BoundaryAtOrBefore(value, limit) {
|
|
111
|
+
let cut = limit;
|
|
112
|
+
while (cut > 0 && (value[cut] & 0xc0) === 0x80)
|
|
113
|
+
cut--;
|
|
114
|
+
return cut;
|
|
115
|
+
}
|
|
74
116
|
/**
|
|
75
117
|
* Drain a readable stream to text, optionally racing each read against a
|
|
76
118
|
* timeout so a process that is killed via SIGTERM/SIGKILL but whose pipe
|
|
77
119
|
* endpoints stay open (e.g. background threads still holding the fd) cannot
|
|
78
120
|
* block the caller indefinitely. On timeout we return whatever was decoded
|
|
79
121
|
* before the pipe stopped draining.
|
|
122
|
+
*
|
|
123
|
+
* ## The `maxBytes` cap bounds MEMORY, not the child
|
|
124
|
+
*
|
|
125
|
+
* The drain ALWAYS runs to the end of the stream. `maxBytes` caps only what is
|
|
126
|
+
* RETAINED: once the cap is reached the loop keeps calling `reader.read()` and
|
|
127
|
+
* throws the bytes away. That is what makes the cap safe to impose on a
|
|
128
|
+
* process that is still running — a reader that stopped pulling would fill the
|
|
129
|
+
* pipe buffer and BLOCK the child on its next write, turning "the output got
|
|
130
|
+
* long" into "the command hangs until the wall timeout". Draining and
|
|
131
|
+
* discarding costs nothing but the reads, and the child finishes normally.
|
|
80
132
|
*/
|
|
81
133
|
export async function readStream(stream, opts) {
|
|
82
134
|
if (!stream)
|
|
83
|
-
return { text: "", timedOut: false };
|
|
135
|
+
return { text: "", timedOut: false, overflowed: false, bytesRead: 0, retainedBytes: 0 };
|
|
84
136
|
const reader = stream.getReader();
|
|
85
137
|
const decoder = new TextDecoder();
|
|
138
|
+
const maxBytes = opts?.maxBytes;
|
|
86
139
|
let text = "";
|
|
140
|
+
let bytesRead = 0;
|
|
141
|
+
let retainedBytes = 0;
|
|
142
|
+
let overflowed = false;
|
|
143
|
+
/**
|
|
144
|
+
* Common per-chunk accumulate. Uncapped it appends everything. Capped, it
|
|
145
|
+
* appends until the cap and then only COUNTS — the caller's loop keeps reading
|
|
146
|
+
* either way, which is the whole point.
|
|
147
|
+
*/
|
|
148
|
+
const absorb = (value) => {
|
|
149
|
+
bytesRead += value.byteLength;
|
|
150
|
+
if (maxBytes === undefined) {
|
|
151
|
+
retainedBytes += value.byteLength;
|
|
152
|
+
text += decoder.decode(value, { stream: true });
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Already past the cap: discard. Never resume retaining — a gap in the
|
|
156
|
+
// middle would splice two disjoint regions into one string.
|
|
157
|
+
if (overflowed)
|
|
158
|
+
return;
|
|
159
|
+
if (retainedBytes + value.byteLength <= maxBytes) {
|
|
160
|
+
retainedBytes += value.byteLength;
|
|
161
|
+
text += decoder.decode(value, { stream: true });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
overflowed = true;
|
|
165
|
+
const keep = utf8BoundaryAtOrBefore(value, maxBytes - retainedBytes);
|
|
166
|
+
if (keep > 0) {
|
|
167
|
+
retainedBytes += keep;
|
|
168
|
+
text += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
169
|
+
}
|
|
170
|
+
};
|
|
87
171
|
if (!opts?.timeoutMs) {
|
|
88
172
|
try {
|
|
89
173
|
while (true) {
|
|
90
174
|
const chunk = await reader.read();
|
|
91
175
|
if (chunk.done)
|
|
92
176
|
break;
|
|
93
|
-
|
|
177
|
+
absorb(chunk.value);
|
|
94
178
|
}
|
|
95
179
|
text += decoder.decode();
|
|
96
|
-
return { text, timedOut: false };
|
|
180
|
+
return { text, timedOut: false, overflowed, bytesRead, retainedBytes };
|
|
97
181
|
}
|
|
98
182
|
catch (error) {
|
|
99
|
-
return { text, timedOut: false, error };
|
|
183
|
+
return { text, timedOut: false, error, overflowed, bytesRead, retainedBytes };
|
|
100
184
|
}
|
|
101
185
|
finally {
|
|
102
186
|
try {
|
|
@@ -109,33 +193,46 @@ export async function readStream(stream, opts) {
|
|
|
109
193
|
}
|
|
110
194
|
const setTimeoutImpl = opts.setTimeoutFn ?? setTimeout;
|
|
111
195
|
const clearTimeoutImpl = opts.clearTimeoutFn ?? clearTimeout;
|
|
196
|
+
const timeoutMs = opts.timeoutMs;
|
|
112
197
|
let timer;
|
|
198
|
+
let drained = false;
|
|
113
199
|
const timeoutPromise = new Promise((resolve) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
timer
|
|
200
|
+
const arm = () => {
|
|
201
|
+
// The drain already finished while the deadline was still waiting to be
|
|
202
|
+
// armed; a timer started now would belong to nobody.
|
|
203
|
+
if (drained)
|
|
204
|
+
return;
|
|
205
|
+
timer = setTimeoutImpl(() => {
|
|
206
|
+
timer = undefined;
|
|
207
|
+
resolve(STREAM_READ_TIMEOUT);
|
|
208
|
+
}, timeoutMs);
|
|
209
|
+
if (typeof timer !== "number")
|
|
210
|
+
timer.unref?.();
|
|
211
|
+
};
|
|
212
|
+
if (opts.armTimeoutAfter)
|
|
213
|
+
void opts.armTimeoutAfter.then(arm, arm);
|
|
214
|
+
else
|
|
215
|
+
arm();
|
|
120
216
|
});
|
|
121
217
|
try {
|
|
122
218
|
while (true) {
|
|
123
219
|
const chunk = await Promise.race([reader.read(), timeoutPromise]);
|
|
124
220
|
if (chunk === STREAM_READ_TIMEOUT) {
|
|
125
221
|
void reader.cancel().catch(() => { });
|
|
126
|
-
return { text, timedOut: true };
|
|
222
|
+
return { text, timedOut: true, overflowed, bytesRead, retainedBytes };
|
|
127
223
|
}
|
|
128
224
|
if (chunk.done)
|
|
129
225
|
break;
|
|
130
|
-
|
|
226
|
+
absorb(chunk.value);
|
|
131
227
|
}
|
|
132
228
|
text += decoder.decode();
|
|
133
|
-
return { text, timedOut: false };
|
|
229
|
+
return { text, timedOut: false, overflowed, bytesRead, retainedBytes };
|
|
134
230
|
}
|
|
135
231
|
catch (error) {
|
|
136
|
-
return { text, timedOut: false, error };
|
|
232
|
+
return { text, timedOut: false, error, overflowed, bytesRead, retainedBytes };
|
|
137
233
|
}
|
|
138
234
|
finally {
|
|
235
|
+
drained = true;
|
|
139
236
|
if (timer !== undefined) {
|
|
140
237
|
clearTimeoutImpl(timer);
|
|
141
238
|
}
|
|
@@ -147,7 +244,29 @@ export async function readStream(stream, opts) {
|
|
|
147
244
|
}
|
|
148
245
|
}
|
|
149
246
|
}
|
|
150
|
-
const EMPTY_READ = {
|
|
247
|
+
const EMPTY_READ = {
|
|
248
|
+
text: "",
|
|
249
|
+
timedOut: false,
|
|
250
|
+
overflowed: false,
|
|
251
|
+
bytesRead: 0,
|
|
252
|
+
retainedBytes: 0,
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Drain deadline for a run with no wall budget, counted from the child's EXIT
|
|
256
|
+
* (never from capture — see {@link runManagedSubprocess}). It bounds only the
|
|
257
|
+
* window in which a descendant can keep an inherited pipe open after the leader
|
|
258
|
+
* is gone, so it can be generous without ever capping the run itself.
|
|
259
|
+
*/
|
|
260
|
+
const UNBOUNDED_STREAM_READ_SAFETY_MS = 60 * 60 * 1000;
|
|
261
|
+
/**
|
|
262
|
+
* Drain deadline for a run WITH a wall budget, counted from the moment the
|
|
263
|
+
* pipe's owner left — the child exited, or (a child that survived its own kill
|
|
264
|
+
* ladder) the budget expired. Anything still holding the fd open then is a
|
|
265
|
+
* background descendant, not the command, so this is the same 2 s the old
|
|
266
|
+
* capture-anchored deadline allowed past the budget; only its starting point
|
|
267
|
+
* moved earlier.
|
|
268
|
+
*/
|
|
269
|
+
const POST_EXIT_STREAM_DRAIN_GRACE_MS = 2_000;
|
|
151
270
|
function toError(err) {
|
|
152
271
|
return err instanceof Error ? err : new Error(String(err));
|
|
153
272
|
}
|
|
@@ -206,8 +325,18 @@ export async function runManagedSubprocess(cmd, opts) {
|
|
|
206
325
|
// Skipped entirely when timeoutMs is null.
|
|
207
326
|
let timedOut = false;
|
|
208
327
|
let timer;
|
|
328
|
+
// Settles when a bounded run's wall budget expires — the second way (after
|
|
329
|
+
// the child's own exit) a captured pipe can stop belonging to a live command.
|
|
330
|
+
// See the drain-deadline note below.
|
|
331
|
+
let onBudgetExpiry;
|
|
332
|
+
const budgetExpired = capture && timeoutMs !== null
|
|
333
|
+
? new Promise((resolve) => {
|
|
334
|
+
onBudgetExpiry = resolve;
|
|
335
|
+
})
|
|
336
|
+
: undefined;
|
|
209
337
|
if (timeoutMs !== null) {
|
|
210
338
|
timer = setTimeoutImpl(() => {
|
|
339
|
+
onBudgetExpiry?.();
|
|
211
340
|
scheduleKillLadder(proc, {
|
|
212
341
|
onKill: () => {
|
|
213
342
|
timedOut = true;
|
|
@@ -238,24 +367,33 @@ export async function runManagedSubprocess(cmd, opts) {
|
|
|
238
367
|
else
|
|
239
368
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
240
369
|
}
|
|
241
|
-
// Stream-drain timeout:
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
370
|
+
// Stream-drain timeout: a short grace once nothing living owns the pipe any
|
|
371
|
+
// more, or a one-hour safety bound when execution itself is unbounded.
|
|
372
|
+
//
|
|
373
|
+
// Neither is armed while the command is still running: every byte up to then
|
|
374
|
+
// is a live process's, and cancelling the reader would discard the output of
|
|
375
|
+
// work that is still going. What ends that ownership is where the two cases
|
|
376
|
+
// differ. Unbounded, the caller asked for NO cap on the run, so only the
|
|
377
|
+
// child's own exit can end it. Bounded, the wall budget ends it too — a child
|
|
378
|
+
// that outlived its own kill ladder is not something akm can keep waiting on
|
|
379
|
+
// — which leaves the SAME budget + grace ceiling as before for a command that
|
|
380
|
+
// really did spend its whole budget, and cuts the common case that used to
|
|
381
|
+
// stall: a leader exiting in milliseconds no longer holds the drain (and with
|
|
382
|
+
// it the unit, and with it a fan-out slot) open for a budget it never came
|
|
383
|
+
// close to spending.
|
|
384
|
+
const streamDrainTimeoutMs = timeoutMs !== null ? POST_EXIT_STREAM_DRAIN_GRACE_MS : UNBOUNDED_STREAM_READ_SAFETY_MS;
|
|
385
|
+
// Settles on a rejected `proc.exited` too: either way the child is gone.
|
|
386
|
+
const childSettled = capture ? proc.exited.catch(() => undefined) : undefined;
|
|
387
|
+
const pipeOwnerGone = childSettled && budgetExpired ? Promise.race([childSettled, budgetExpired]) : childSettled;
|
|
388
|
+
const readOpts = {
|
|
389
|
+
timeoutMs: streamDrainTimeoutMs,
|
|
390
|
+
setTimeoutFn: setTimeoutImpl,
|
|
391
|
+
clearTimeoutFn: clearTimeoutImpl,
|
|
392
|
+
...(pipeOwnerGone ? { armTimeoutAfter: pipeOwnerGone } : {}),
|
|
393
|
+
...(opts.maxOutputBytes !== undefined ? { maxBytes: opts.maxOutputBytes } : {}),
|
|
394
|
+
};
|
|
395
|
+
const stdoutPromise = capture ? readStream(proc.stdout ?? null, readOpts) : Promise.resolve(EMPTY_READ);
|
|
396
|
+
const stderrPromise = capture ? readStream(proc.stderr ?? null, readOpts) : Promise.resolve(EMPTY_READ);
|
|
259
397
|
// Optional stdin payload (captured mode only). Race the write/close against
|
|
260
398
|
// proc.exited so a child that never drains stdin cannot pin us past the
|
|
261
399
|
// timeout.
|
|
@@ -299,5 +437,13 @@ export async function runManagedSubprocess(cmd, opts) {
|
|
|
299
437
|
clearTimeoutImpl(timer);
|
|
300
438
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
301
439
|
const [stdoutRead, stderrRead] = await Promise.all([stdoutPromise, stderrPromise]);
|
|
302
|
-
return {
|
|
440
|
+
return {
|
|
441
|
+
exitCode,
|
|
442
|
+
stdout: stdoutRead.text,
|
|
443
|
+
stderr: stderrRead.text,
|
|
444
|
+
timedOut,
|
|
445
|
+
aborted,
|
|
446
|
+
stdoutRead,
|
|
447
|
+
stderrRead,
|
|
448
|
+
};
|
|
303
449
|
}
|