akm-cli 0.9.0-rc.1 → 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 +1190 -52
- 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/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 +13 -9
- package/dist/commands/env/env-cli.js +76 -159
- 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/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 +75 -228
- 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 -855
- 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/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 -73
- 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 -58
- 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 +8 -3
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +78 -13
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +48 -279
- package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
- package/dist/integrations/harnesses/aider/index.js +2 -12
- package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
- package/dist/integrations/harnesses/amazonq/index.js +3 -17
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +1 -14
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
- package/dist/integrations/harnesses/codex/index.js +2 -13
- package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
- package/dist/integrations/harnesses/copilot/index.js +1 -13
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
- package/dist/integrations/harnesses/gemini/index.js +1 -13
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +31 -33
- 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 +1 -18
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
- package/dist/integrations/harnesses/openhands/index.js +2 -12
- package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
- package/dist/integrations/harnesses/pi/index.js +3 -16
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +38 -34
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +24 -40
- 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 +15 -281
- 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 +8 -40
- 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 -1375
- 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 +1 -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 +68 -35
- 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/workflows/authoring/authoring.js +96 -148
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -20
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +25 -59
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +157 -94
- package/dist/workflows/exec/report.js +365 -200
- package/dist/workflows/exec/run-workflow.js +46 -40
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +239 -205
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/ir/compile.js +141 -283
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +537 -1
- package/dist/workflows/parser.js +878 -306
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +7 -10
- package/dist/workflows/renderer.js +99 -121
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +200 -113
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +4 -26
- 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/{dist/schemas → schemas}/akm-workflow.json +127 -82
- 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 -316
- 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 -1414
- 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 -25
- 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/schemas/akm-config.json +0 -14225
- package/dist/scripts/migrate-storage.js +0 -13169
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
- 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/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/db.js +0 -350
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -760
- package/dist/workflows/program/project.js +0 -105
- package/docs/data-and-telemetry.md +0 -227
- package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -44,9 +44,12 @@ import { UsageError } from "../../core/errors.js";
|
|
|
44
44
|
import { appendEvent } from "../../core/events.js";
|
|
45
45
|
import { validateJsonSchemaSubset } from "../../core/json-schema.js";
|
|
46
46
|
import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
47
|
+
import { canonicalJson as canonicalJsonString } from "../ir/plan-hash.js";
|
|
48
|
+
import { resolveReferenceString } from "../program/expressions.js";
|
|
49
|
+
import { WORKFLOW_MAX_MAP_EXPANSION } from "../resource-limits.js";
|
|
50
|
+
import { requireExecutableWorkflowPlan } from "../runtime/plan-classifier.js";
|
|
51
|
+
import { completeWorkflowStep } from "../runtime/runs.js";
|
|
52
|
+
import { GATE_EVALUATION_PHASE } from "../runtime/unit-phases.js";
|
|
50
53
|
import { enqueueUnitWrite } from "./unit-writer.js";
|
|
51
54
|
/**
|
|
52
55
|
* Default per-unit timeout. Deliberately NOT the 60 s agent default
|
|
@@ -67,12 +70,51 @@ const GATE_ARTIFACT_CLIP = 4_000;
|
|
|
67
70
|
* schema directive), and hash the resolved input. Same inputs ⇒ byte-identical
|
|
68
71
|
* ids/hashes/prompts — the invariant `brief` relies on to predict the engine.
|
|
69
72
|
*
|
|
70
|
-
* Whole-list failures (missing subgraph,
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
73
|
+
* Whole-list failures (missing subgraph, unresolvable / non-array `over`,
|
|
74
|
+
* null or duplicate fan-out items) return `{ ok: false }`. The per-unit
|
|
75
|
+
* `resolved: { ok: false }` branch is STRUCTURALLY UNREACHABLE in the unified
|
|
76
|
+
* format — prose is never scanned for references, and everything that CAN
|
|
77
|
+
* fail (map.over / route.input / inputs:) resolves once per step, failing the
|
|
78
|
+
* whole list above. The branch is retained because brief/report/executor all
|
|
79
|
+
* share the shape and defensively handle it; if a future unit kind
|
|
80
|
+
* reintroduces per-unit resolution (e.g. an exec/shell unit with real
|
|
81
|
+
* substitution), the cross-surface failure plumbing is already in place.
|
|
75
82
|
*/
|
|
83
|
+
/**
|
|
84
|
+
* Validate a fan-out item list BEFORE any identity/dispatch work: expansion
|
|
85
|
+
* within the resource limit, no null/undefined items, no canonical duplicates.
|
|
86
|
+
* Returns the failure message, or undefined when the list is dispatchable.
|
|
87
|
+
*
|
|
88
|
+
* Null items: producer garbage — there is nothing to hand the unit as its work
|
|
89
|
+
* item. The pre-unification format rejected them incidentally (substituting
|
|
90
|
+
* `${{ item }}` failed); with items attached as context instead of spliced,
|
|
91
|
+
* nothing later would stop a unit from being dispatched with "Item: null", so
|
|
92
|
+
* the rejection is explicit here. Duplicates: content-derived unit identity
|
|
93
|
+
* makes canonical duplicates collide on id — an authoring error caught
|
|
94
|
+
* deterministically, before dispatch.
|
|
95
|
+
*/
|
|
96
|
+
function validateFanOutItems(stepId, items) {
|
|
97
|
+
if (items.length > WORKFLOW_MAX_MAP_EXPANSION) {
|
|
98
|
+
return `Step "${stepId}" fan-out expands to ${items.length} units, exceeding the ${WORKFLOW_MAX_MAP_EXPANSION}-unit resource limit.`;
|
|
99
|
+
}
|
|
100
|
+
const nullIndex = items.findIndex((item) => item === null || item === undefined);
|
|
101
|
+
if (nullIndex !== -1) {
|
|
102
|
+
return (`Step "${stepId}" fan-out list contains a null item (index ${nullIndex}). ` +
|
|
103
|
+
`Every item must be a concrete value — fix the producing step's output.`);
|
|
104
|
+
}
|
|
105
|
+
const firstIndexByCanonical = new Map();
|
|
106
|
+
for (let i = 0; i < items.length; i++) {
|
|
107
|
+
const canonical = canonicalJson(items[i]) ?? "null";
|
|
108
|
+
const firstIndex = firstIndexByCanonical.get(canonical);
|
|
109
|
+
if (firstIndex !== undefined) {
|
|
110
|
+
return (`Step "${stepId}" fan-out list contains duplicate items (indices ${firstIndex} and ${i}: ` +
|
|
111
|
+
`${clip(canonical, 200)}). Content-derived unit identity requires distinct items — ` +
|
|
112
|
+
`deduplicate the list this workflow fans out over.`);
|
|
113
|
+
}
|
|
114
|
+
firstIndexByCanonical.set(canonical, i);
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
76
118
|
export function computeStepWorkList(plan, input) {
|
|
77
119
|
const root = plan.root;
|
|
78
120
|
// Route-only steps (YAML `route:`) carry no execution subgraph.
|
|
@@ -85,30 +127,29 @@ export function computeStepWorkList(plan, input) {
|
|
|
85
127
|
const template = root.kind === "map" ? root.template : root;
|
|
86
128
|
const reducer = root.kind === "map" ? root.reducer : "collect";
|
|
87
129
|
const scope = { params: input.params, stepOutputs: input.stepOutputs };
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
// Instructions are ALWAYS the step's body prose, byte-exact — never
|
|
131
|
+
// templated, never scanned for reference syntax (workflow-format-
|
|
132
|
+
// unification, spec §2.3). Only `map.over` / `route.input` / `inputs[]`
|
|
133
|
+
// carry the closed reference grammar.
|
|
134
|
+
// Resolve the step's declared `inputs:` ONCE (shared by every unit in this
|
|
135
|
+
// step — map items differ, declared inputs do not): prior-step artifacts
|
|
136
|
+
// attached to every dispatched unit as structured context.
|
|
137
|
+
const resolvedInputs = [];
|
|
138
|
+
for (const reference of template.inputs ?? []) {
|
|
139
|
+
const resolved = resolveReferenceString(reference, scope);
|
|
140
|
+
if (!resolved.ok) {
|
|
96
141
|
return {
|
|
97
142
|
ok: false,
|
|
98
|
-
error: `Step "${plan.stepId}"
|
|
99
|
-
parsedInstructions.errors.map((e) => e.message).join(" "),
|
|
143
|
+
error: `Step "${plan.stepId}" declared input "${reference}" failed to resolve: ${resolved.error.message}`,
|
|
100
144
|
};
|
|
101
145
|
}
|
|
102
|
-
|
|
146
|
+
resolvedInputs.push({ reference, value: resolved.value });
|
|
103
147
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
// Resolve fan-out items: `over` is a single whole-value `${{ … }}` reference
|
|
108
|
-
// naming its producer explicitly — no ambient key search.
|
|
148
|
+
// Resolve fan-out items: `over` is a single whole-value reference naming
|
|
149
|
+
// its producer explicitly — no ambient key search.
|
|
109
150
|
let items;
|
|
110
151
|
if (root.kind === "map") {
|
|
111
|
-
const source =
|
|
152
|
+
const source = resolveReferenceString(root.over, scope);
|
|
112
153
|
if (!source.ok) {
|
|
113
154
|
return {
|
|
114
155
|
ok: false,
|
|
@@ -126,94 +167,103 @@ export function computeStepWorkList(plan, input) {
|
|
|
126
167
|
else {
|
|
127
168
|
items = [undefined];
|
|
128
169
|
}
|
|
129
|
-
// Content-derived unit identity: compute every id up front. Duplicate items
|
|
130
|
-
// collide on identity — an authoring error caught HERE, deterministically.
|
|
131
170
|
const isFanOut = root.kind === "map";
|
|
171
|
+
const fanOutProblem = isFanOut ? validateFanOutItems(plan.stepId, items) : undefined;
|
|
172
|
+
if (fanOutProblem)
|
|
173
|
+
return { ok: false, error: fanOutProblem };
|
|
174
|
+
// Content-derived unit identity: compute every id up front (duplicate items
|
|
175
|
+
// were rejected above — identity requires distinct items).
|
|
132
176
|
const unitIds = items.map((item) => unitIdFor(template.id, item, isFanOut));
|
|
133
|
-
if (isFanOut) {
|
|
134
|
-
const firstIndexByCanonical = new Map();
|
|
135
|
-
for (let i = 0; i < items.length; i++) {
|
|
136
|
-
const canonical = canonicalJson(items[i]) ?? "null";
|
|
137
|
-
const firstIndex = firstIndexByCanonical.get(canonical);
|
|
138
|
-
if (firstIndex !== undefined) {
|
|
139
|
-
return {
|
|
140
|
-
ok: false,
|
|
141
|
-
error: `Step "${plan.stepId}" fan-out list contains duplicate items (indices ${firstIndex} and ${i}: ` +
|
|
142
|
-
`${clip(canonical, 200)}). Content-derived unit identity requires distinct items — ` +
|
|
143
|
-
`deduplicate the list this workflow fans out over.`,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
firstIndexByCanonical.set(canonical, i);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
177
|
const gateLoop = input.gateLoop ?? 1;
|
|
150
|
-
const
|
|
178
|
+
const frozenInvocation = template.invocation;
|
|
179
|
+
if (!frozenInvocation)
|
|
180
|
+
return { ok: false, error: `Step "${plan.stepId}" has no frozen invocation.` };
|
|
181
|
+
const frozenEngine = input.engines?.[frozenInvocation.engine];
|
|
182
|
+
if (!frozenEngine) {
|
|
183
|
+
return { ok: false, error: `Step "${plan.stepId}" references missing frozen engine "${frozenInvocation.engine}".` };
|
|
184
|
+
}
|
|
185
|
+
const runner = frozenEngine.kind === "llm" ? "llm" : frozenEngine.runnerKind;
|
|
186
|
+
const timeoutMs = frozenInvocation.timeoutMs;
|
|
151
187
|
const units = items.map((item, index) => {
|
|
152
188
|
const unitId = unitIds[index];
|
|
153
189
|
// Gate loops (>= 2) journal under `<unitId>~l<loop>` so loop 1's rows are
|
|
154
190
|
// never clobbered; the content-derived identity (and the prompt's
|
|
155
191
|
// {{UNIT_ID}}) stays the base id.
|
|
156
192
|
const journalBaseId = gateLoop > 1 ? `${unitId}~l${gateLoop}` : unitId;
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
193
|
+
// Context attachment (workflow-format-unification, spec §4): every unit
|
|
194
|
+
// receives the run params (already in the preamble), its item + index if
|
|
195
|
+
// it is a map unit, and the artifacts named by its step's `inputs:`.
|
|
196
|
+
// Instructions reach the unit byte-exact — never interpolated.
|
|
197
|
+
const prompt = buildUnitPrompt({
|
|
198
|
+
runId: input.runId,
|
|
199
|
+
stepId: plan.stepId,
|
|
200
|
+
unitId,
|
|
201
|
+
params: input.params,
|
|
202
|
+
...(isFanOut ? { item, itemIndex: index } : {}),
|
|
203
|
+
...(resolvedInputs.length > 0 ? { inputs: resolvedInputs } : {}),
|
|
204
|
+
...(input.gateFeedback ? { gateFeedback: input.gateFeedback } : {}),
|
|
205
|
+
...(template.schema ? { schema: template.schema } : {}),
|
|
206
|
+
instructions: template.instructions,
|
|
207
|
+
});
|
|
208
|
+
// Canonical dispatch-input envelope (reviewer finding #1). Every field
|
|
209
|
+
// here is a PLAN-FROZEN input that changes what the backend is actually
|
|
210
|
+
// asked to do, so a completed unit is reused ONLY when all of them match;
|
|
211
|
+
// a change to any of them re-dispatches. Key order is FIXED — it is the
|
|
212
|
+
// hash preimage (JSON.stringify preserves insertion order) — and shared
|
|
213
|
+
// by ALL surfaces, since this is the ONE place a unit's inputHash is
|
|
214
|
+
// computed (engine, brief, and report all call computeStepWorkList), so
|
|
215
|
+
// the byte-identical hash across surfaces is structural, not coincidental.
|
|
216
|
+
//
|
|
217
|
+
// Unit identity (workflow-format-unification, spec §2.3/§4) hashes the
|
|
218
|
+
// FROZEN TEMPLATE BYTES (`template.instructions`, byte-exact, never an
|
|
219
|
+
// instantiated/interpolated string) + the canonical item JSON + the
|
|
220
|
+
// declared-input artifact hashes + the params snapshot — instead of a
|
|
221
|
+
// resolved/spliced prompt string, since there is no more splicing. The
|
|
222
|
+
// assembled `prompt` above is what the harness SEES; the hash is over the
|
|
223
|
+
// plan-frozen INPUTS that determine it, which is the same replay contract
|
|
224
|
+
// the old resolved-prompt hash gave (same inputs ⇒ same hash) with the
|
|
225
|
+
// interpolation step removed.
|
|
226
|
+
//
|
|
227
|
+
// Included beyond the R4 baseline (template/runner/model/schema): resolved
|
|
228
|
+
// timeoutMs, the env asset ref NAMES, and isolation — each reaches
|
|
229
|
+
// dispatch (native-executor's UnitDispatchRequest) and a changed one
|
|
230
|
+
// yields a materially different call. `env` carries NAMES ONLY, never
|
|
231
|
+
// resolved values: hashing a resolved secret would leak it into a durable
|
|
232
|
+
// hash oracle and would spuriously re-dispatch on every secret rotation.
|
|
233
|
+
// `retry`/`onError` are DELIBERATELY excluded — they govern failed-unit
|
|
234
|
+
// re-dispatch and step-level failure reduction, not a COMPLETED unit's
|
|
235
|
+
// inputs/output, so a completed row stays valid across policy changes.
|
|
236
|
+
//
|
|
237
|
+
// `gateFeedback` IS included (conditionally, so a no-feedback unit's
|
|
238
|
+
// preimage is byte-identical to before): it is appended to the prompt by
|
|
239
|
+
// `buildUnitPrompt`, so a gate loop's retry is materially a different ask
|
|
240
|
+
// than the rejected attempt — omitting it made loop 1 and loop 2 journal
|
|
241
|
+
// identical hashes for different prompts, breaking the "changed inputs ⇒
|
|
242
|
+
// changed hash" audit contract. Replay-safe: feedback is re-derived from
|
|
243
|
+
// the journaled gate decision, so a resumed retry re-hashes identically.
|
|
244
|
+
//
|
|
245
|
+
// Ambient config is DELIBERATELY excluded — the model-alias table, the
|
|
246
|
+
// resolved backend/connection, and the working directory (`ctx.workDir` /
|
|
247
|
+
// process.cwd()) are NOT plan-frozen. The frozen plan is the identity
|
|
248
|
+
// boundary (redesign addendum determinism bar #2): config drift under an
|
|
249
|
+
// in-flight run is out of scope by design.
|
|
250
|
+
const dispatch = transitiveDispatchSnapshot(frozenEngine, input.engines ?? {});
|
|
251
|
+
const inputHash = createHash("sha256")
|
|
252
|
+
.update(canonicalJsonString({
|
|
253
|
+
hashVersion: 4,
|
|
254
|
+
template: template.instructions,
|
|
255
|
+
item: isFanOut ? (item ?? null) : null,
|
|
256
|
+
inputs: resolvedInputs,
|
|
257
|
+
params: input.params,
|
|
258
|
+
dispatch,
|
|
259
|
+
invocation: frozenInvocation,
|
|
260
|
+
schema: template.schema ?? null,
|
|
261
|
+
env: template.env ?? null,
|
|
262
|
+
isolation: template.isolation ?? "none",
|
|
263
|
+
...(input.gateFeedback ? { gateFeedback: input.gateFeedback } : {}),
|
|
264
|
+
}))
|
|
265
|
+
.digest("hex");
|
|
266
|
+
const resolved = { ok: true, prompt, inputHash };
|
|
217
267
|
return {
|
|
218
268
|
unitId,
|
|
219
269
|
nodeId: template.id,
|
|
@@ -221,9 +271,17 @@ export function computeStepWorkList(plan, input) {
|
|
|
221
271
|
item,
|
|
222
272
|
isFanOut,
|
|
223
273
|
journalBaseId,
|
|
224
|
-
runner
|
|
225
|
-
|
|
226
|
-
...(
|
|
274
|
+
runner,
|
|
275
|
+
engine: frozenEngine,
|
|
276
|
+
...(frozenEngine?.kind === "agent" &&
|
|
277
|
+
frozenEngine.fallbackLlmEngine &&
|
|
278
|
+
input.engines?.[frozenEngine.fallbackLlmEngine]?.kind === "llm"
|
|
279
|
+
? {
|
|
280
|
+
fallbackEngine: input.engines[frozenEngine.fallbackLlmEngine],
|
|
281
|
+
}
|
|
282
|
+
: {}),
|
|
283
|
+
invocation: frozenInvocation,
|
|
284
|
+
...(frozenInvocation.model ? { model: frozenInvocation.model } : {}),
|
|
227
285
|
timeoutMs,
|
|
228
286
|
...(template.schema ? { schema: template.schema } : {}),
|
|
229
287
|
...(template.env ? { env: template.env } : {}),
|
|
@@ -240,13 +298,16 @@ export function computeStepWorkList(plan, input) {
|
|
|
240
298
|
};
|
|
241
299
|
}
|
|
242
300
|
/**
|
|
243
|
-
* Assemble the final prompt: engine preamble +
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
301
|
+
* Assemble the final prompt: engine preamble (run params + item/index +
|
|
302
|
+
* declared-input artifacts, all as structured JSON context) + the step's
|
|
303
|
+
* BYTE-EXACT prose instructions (+ gate feedback on loop re-executions, +
|
|
304
|
+
* schema directive). Instructions are NEVER interpolated (workflow-format-
|
|
305
|
+
* unification, spec §2.3) — data reaches the unit as attached context, not
|
|
306
|
+
* string splices; only the ENGINE's own preamble placeholders are substituted
|
|
307
|
+
* here.
|
|
247
308
|
*/
|
|
248
309
|
export function buildUnitPrompt(input) {
|
|
249
|
-
const { runId, stepId, unitId, params, gateFeedback, schema, instructions } = input;
|
|
310
|
+
const { runId, stepId, unitId, params, itemIndex, item, inputs, gateFeedback, schema, instructions } = input;
|
|
250
311
|
// Function replacements throughout: a string replacement would interpret
|
|
251
312
|
// GetSubstitution patterns ($&, $$, $', $`) inside VALUES and silently
|
|
252
313
|
// corrupt the prompt (e.g. a param value containing "$&").
|
|
@@ -255,6 +316,15 @@ export function buildUnitPrompt(input) {
|
|
|
255
316
|
.replaceAll("{{STEP_ID}}", () => stepId)
|
|
256
317
|
.replaceAll("{{UNIT_ID}}", () => unitId)
|
|
257
318
|
.replaceAll("{{PARAMS_JSON}}", () => safeJson(params));
|
|
319
|
+
// Map-unit context: the item this unit was given, plus its index. Attached
|
|
320
|
+
// as structured JSON — the engine never splices it into the instructions.
|
|
321
|
+
const itemBlock = itemIndex !== undefined
|
|
322
|
+
? `\n\n## Item (index ${itemIndex})\nYou were given this item from the fan-out list:\n${safeJson(item)}`
|
|
323
|
+
: "";
|
|
324
|
+
// Declared `inputs:` context: the prior-step artifacts this step named.
|
|
325
|
+
const inputsBlock = inputs && inputs.length > 0
|
|
326
|
+
? `\n\n## Declared inputs\n${inputs.map((i) => `### ${i.reference}\n${safeJson(i.value)}`).join("\n\n")}`
|
|
327
|
+
: "";
|
|
258
328
|
// Gate-loop feedback (R2 max_loops): the judge's rejection is appended so
|
|
259
329
|
// the re-executed unit can address it — and so the input hash changes,
|
|
260
330
|
// making the loop's re-dispatch natural instead of a durable-row reuse.
|
|
@@ -269,7 +339,7 @@ export function buildUnitPrompt(input) {
|
|
|
269
339
|
const schemaDirective = schema
|
|
270
340
|
? `\n\nRespond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${safeJson(schema)}`
|
|
271
341
|
: "";
|
|
272
|
-
return `${preamble}\n${instructions}${gateBlock}${schemaDirective}`;
|
|
342
|
+
return `${preamble}\n${instructions}${itemBlock}${inputsBlock}${gateBlock}${schemaDirective}`;
|
|
273
343
|
}
|
|
274
344
|
/**
|
|
275
345
|
* Content-derived unit identity (module doc): `<node_id>:<hash12>` for a
|
|
@@ -284,6 +354,16 @@ export function unitIdFor(nodeId, item, isFanOut) {
|
|
|
284
354
|
const canonical = canonicalJson(item) ?? "null";
|
|
285
355
|
return `${nodeId}:${createHash("sha256").update(canonical).digest("hex").slice(0, 12)}`;
|
|
286
356
|
}
|
|
357
|
+
/** Include an SDK fallback in v3 call identity without copying catalog entries onto nodes. */
|
|
358
|
+
function transitiveDispatchSnapshot(engine, engines) {
|
|
359
|
+
if (engine.kind !== "agent" || !engine.fallbackLlmEngine)
|
|
360
|
+
return engine;
|
|
361
|
+
const fallback = engines[engine.fallbackLlmEngine];
|
|
362
|
+
if (!fallback || fallback.kind !== "llm") {
|
|
363
|
+
throw new UsageError(`Frozen agent engine "${engine.name}" has no valid LLM fallback snapshot.`);
|
|
364
|
+
}
|
|
365
|
+
return { engine, fallback };
|
|
366
|
+
}
|
|
287
367
|
// ── Step outputs + reducers + typed artifacts ────────────────────────────────
|
|
288
368
|
/**
|
|
289
369
|
* The value `${{ steps.<id>.output }}` resolves to for ONE step, given that
|
|
@@ -558,8 +638,8 @@ export function isRetryEligibleFailure(workUnit, row, failureReason) {
|
|
|
558
638
|
* it)? True for a unit with no terminal row (pending), a still-`running` row (a
|
|
559
639
|
* live/stale claim another driver holds), or a FAILED row that is still
|
|
560
640
|
* retry-eligible. False for a COMPLETED row, a terminal non-retry-eligible
|
|
561
|
-
* FAILURE, or an UNRESOLVABLE unit (
|
|
562
|
-
* never reportable). The best terminal attempt (base + `~r<n>` retries) is the
|
|
641
|
+
* FAILURE, or an UNRESOLVABLE unit (a currently-unreachable defensive branch —
|
|
642
|
+
* see the computeStepWorkList doc — never reportable). The best terminal attempt (base + `~r<n>` retries) is the
|
|
563
643
|
* one consulted, the SAME reuse the engine and reducer apply.
|
|
564
644
|
*/
|
|
565
645
|
export function unitStillNeedsReport(workUnit, dispatchRows) {
|
|
@@ -615,15 +695,8 @@ function sortKeys(value) {
|
|
|
615
695
|
// `brief` recovers it from the journal so its loop-N work-list matches the
|
|
616
696
|
// engine's (redesign addendum R3, task item 2). `native-executor.test.ts`
|
|
617
697
|
// asserts the round-trip identity.
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
* dots (`PROGRAM_STEP_ID_PATTERN`), so a step can never be NAMED `x.gate` and
|
|
621
|
-
* the synthetic `<stepId>.gate` node id is collision-free against user step
|
|
622
|
-
* ids. The phase column is nonetheless the discriminator we key on — an
|
|
623
|
-
* explicit marker, not a `node_id` suffix match, so recovery stays robust even
|
|
624
|
-
* if the id scheme evolves. Dispatch rows always journal `phase: null`.
|
|
625
|
-
*/
|
|
626
|
-
export const GATE_EVALUATION_PHASE = "gate";
|
|
698
|
+
// GATE_EVALUATION_PHASE moved to ../runtime/unit-phases.ts (leaf) so
|
|
699
|
+
// unit-checkin can key on it without closing the exec ↔ runtime cycle.
|
|
627
700
|
/** The unit id of a step's gate-evaluation row for a given 1-based loop. */
|
|
628
701
|
export function gateUnitId(stepId, loop) {
|
|
629
702
|
return `${stepId}.gate:l${loop}`;
|
|
@@ -737,8 +810,9 @@ export async function journalGateEvaluationStart(gate) {
|
|
|
737
810
|
// seed in `driveRun` skips these so resume accounting matches live.
|
|
738
811
|
phase: GATE_EVALUATION_PHASE,
|
|
739
812
|
runner: "llm",
|
|
740
|
-
|
|
741
|
-
|
|
813
|
+
engine: gate.invocation.engine,
|
|
814
|
+
model: gate.invocation.model,
|
|
815
|
+
inputHash: gate.inputHash,
|
|
742
816
|
startedAt: new Date().toISOString(),
|
|
743
817
|
})));
|
|
744
818
|
appendEvent({
|
|
@@ -751,10 +825,7 @@ export async function journalGateEvaluationStart(gate) {
|
|
|
751
825
|
* Finish the gate-evaluation unit row with the verdict as observed from the
|
|
752
826
|
* completion outcome: a rejection journals `{ complete: false, missing,
|
|
753
827
|
* feedback }`; a pass journals `{ complete: true, missing: [] }`; a judge that
|
|
754
|
-
* threw
|
|
755
|
-
* failed row with a NULL verdict. A NON-required errored gate then fails OPEN
|
|
756
|
-
* inside `validateStepSummary`; a REQUIRED errored gate BLOCKS the step
|
|
757
|
-
* (`finalizeExecutedStep`, Codex round-3 finding A).
|
|
828
|
+
* threw journals a failed row with a NULL verdict and validation is skipped.
|
|
758
829
|
*/
|
|
759
830
|
export async function journalGateEvaluationFinish(gate, errored, rejection) {
|
|
760
831
|
const unitId = gateUnitId(gate.stepId, gate.loop);
|
|
@@ -785,7 +856,7 @@ export async function journalGateEvaluationFinish(gate, errored, rejection) {
|
|
|
785
856
|
* comparison is exact string equality against the declared `when:` matches.
|
|
786
857
|
*/
|
|
787
858
|
export function evaluateRoute(route, scope) {
|
|
788
|
-
const resolved =
|
|
859
|
+
const resolved = resolveReferenceString(route.input, scope);
|
|
789
860
|
if (!resolved.ok) {
|
|
790
861
|
return { ok: false, error: `route input ${route.input} failed to resolve: ${resolved.error.message}` };
|
|
791
862
|
}
|
|
@@ -988,27 +1059,9 @@ export async function finalizeExecutedStep(input) {
|
|
|
988
1059
|
return { kind: "failed", summary: result.summary };
|
|
989
1060
|
}
|
|
990
1061
|
// Resolve the completion-criteria judge ONCE (reused by the gate below). A
|
|
991
|
-
//
|
|
992
|
-
//
|
|
993
|
-
|
|
994
|
-
const innerJudge = input.summaryJudge === undefined ? buildDefaultSummaryJudge() : input.summaryJudge;
|
|
995
|
-
// Reviewer #18: a REQUIRED completion gate must actually be judged. When the
|
|
996
|
-
// gate carries criteria but no judge is available, `validateStepSummary` would
|
|
997
|
-
// fail OPEN and silently pass the gate — exactly the offline/misconfigured
|
|
998
|
-
// bypass a required gate exists to prevent. BLOCK the step instead (a human
|
|
999
|
-
// resolves it via the documented manual path), rather than advance the spine
|
|
1000
|
-
// on an unjudged gate. `gate.required` rides the frozen plan (both surfaces);
|
|
1001
|
-
// `requireGates` is the engine's run-wide `--require-gates` override. Checked
|
|
1002
|
-
// BEFORE route evaluation so a blocked step journals no route decision.
|
|
1003
|
-
const gateRequired = stepPlan.gate.required === true || input.requireGates === true;
|
|
1004
|
-
if (gateRequired && completionCriteria.length > 0 && innerJudge === null) {
|
|
1005
|
-
const notes = `Step "${stepId}" has a REQUIRED completion gate but no summary-validation judge is available ` +
|
|
1006
|
-
`(no LLM is configured, or default LLM resolution failed). A required gate must be judged — refusing to fail ` +
|
|
1007
|
-
`open and silently pass it. The step is BLOCKED: configure an LLM, then \`akm workflow resume ${runId}\` to ` +
|
|
1008
|
-
`re-evaluate the gate, or advance the step manually with \`akm workflow complete\`.`;
|
|
1009
|
-
await completeWorkflowStep({ runId, stepId, status: "blocked", notes, evidence: result.evidence, ...lease });
|
|
1010
|
-
return { kind: "blocked", summary: notes };
|
|
1011
|
-
}
|
|
1062
|
+
// A frozen plan either supplies its judge at the dispatch boundary or has no
|
|
1063
|
+
// judge. Re-selecting defaults here would let config drift change a run.
|
|
1064
|
+
const innerJudge = input.summaryJudge ?? null;
|
|
1012
1065
|
// Route evaluation BEFORE completion: an unroutable value is an
|
|
1013
1066
|
// authoring/config failure that must fail the step deterministically.
|
|
1014
1067
|
let summaryOverride;
|
|
@@ -1040,12 +1093,40 @@ export async function finalizeExecutedStep(input) {
|
|
|
1040
1093
|
// Journal engine-driven judge calls as unit rows (they are LLM calls). The
|
|
1041
1094
|
// wrapper's `invoked` stays false when the gate is fail-open (no criteria / no
|
|
1042
1095
|
// judge) — nothing is journaled, and human approvals are never cached.
|
|
1043
|
-
const
|
|
1096
|
+
const frozenGate = innerJudge
|
|
1097
|
+
? await withWorkflowRunsRepo((repo) => {
|
|
1098
|
+
const row = repo.getRunById(runId);
|
|
1099
|
+
if (!row)
|
|
1100
|
+
throw new UsageError(`Workflow run ${runId} was not found.`);
|
|
1101
|
+
const plan = requireExecutableWorkflowPlan(row);
|
|
1102
|
+
const invocation = plan.steps.find((step) => step.stepId === stepId)?.gate.judge ?? null;
|
|
1103
|
+
return invocation ? { invocation, engine: plan.execution?.engines[invocation.engine] ?? null } : null;
|
|
1104
|
+
})
|
|
1105
|
+
: null;
|
|
1106
|
+
const gateInvocation = frozenGate?.invocation ?? null;
|
|
1107
|
+
let gateUnit;
|
|
1044
1108
|
const judgeState = { invoked: false, errored: false };
|
|
1045
1109
|
const summaryJudge = innerJudge
|
|
1046
1110
|
? async (prompt) => {
|
|
1047
1111
|
judgeState.invoked = true;
|
|
1048
|
-
|
|
1112
|
+
if (gateInvocation) {
|
|
1113
|
+
gateUnit = {
|
|
1114
|
+
runId,
|
|
1115
|
+
workflowRef,
|
|
1116
|
+
stepId,
|
|
1117
|
+
loop: gateLoop,
|
|
1118
|
+
invocation: gateInvocation,
|
|
1119
|
+
inputHash: createHash("sha256")
|
|
1120
|
+
.update(canonicalJsonString({
|
|
1121
|
+
hashVersion: 3,
|
|
1122
|
+
dispatch: frozenGate?.engine ?? null,
|
|
1123
|
+
invocation: gateInvocation,
|
|
1124
|
+
prompt,
|
|
1125
|
+
}))
|
|
1126
|
+
.digest("hex"),
|
|
1127
|
+
};
|
|
1128
|
+
await journalGateEvaluationStart(gateUnit);
|
|
1129
|
+
}
|
|
1049
1130
|
try {
|
|
1050
1131
|
return await innerJudge(prompt);
|
|
1051
1132
|
}
|
|
@@ -1058,8 +1139,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1058
1139
|
// Reviewer #6: once the judge is invoked, its gate row is journaled `running`
|
|
1059
1140
|
// (journalGateEvaluationStart) and MUST be finished on every exit. The
|
|
1060
1141
|
// already-fixed window is the judge itself throwing (caught inside
|
|
1061
|
-
// validateStepSummary — `judgeState.errored` records it
|
|
1062
|
-
// fails open, a required gate blocks below). The remaining
|
|
1142
|
+
// validateStepSummary — `judgeState.errored` records it). The remaining
|
|
1063
1143
|
// window is `completeWorkflowStep` throwing AFTER the judge ran — a stolen
|
|
1064
1144
|
// lease, a concurrent state change, a DB error — which would otherwise skip the
|
|
1065
1145
|
// finish and strand the gate row in `running`. Finish it as an errored row (the
|
|
@@ -1073,38 +1153,17 @@ export async function finalizeExecutedStep(input) {
|
|
|
1073
1153
|
summary,
|
|
1074
1154
|
evidence: result.evidence,
|
|
1075
1155
|
summaryJudge,
|
|
1076
|
-
// Codex round-3 finding A: mark this completion's gate REQUIRED so
|
|
1077
|
-
// `validateStepSummary` does NOT fail open when the judge throws / is
|
|
1078
|
-
// unreachable / returns garbage — it flags `errored` and we block below.
|
|
1079
|
-
...(gateRequired ? { requireGate: true } : {}),
|
|
1080
1156
|
...lease,
|
|
1081
1157
|
});
|
|
1082
1158
|
}
|
|
1083
1159
|
catch (err) {
|
|
1084
|
-
if (
|
|
1160
|
+
if (gateUnit)
|
|
1085
1161
|
await journalGateEvaluationFinish(gateUnit, true, undefined);
|
|
1086
1162
|
throw err;
|
|
1087
1163
|
}
|
|
1088
1164
|
const rejection = "ok" in completion && completion.ok === false ? completion : undefined;
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
// observed outcome is honest, driven by EITHER the wrapper catching a throw OR
|
|
1092
|
-
// validateStepSummary flagging an unparseable verdict.
|
|
1093
|
-
const gateErrored = judgeState.errored || rejection?.errored === true;
|
|
1094
|
-
if (judgeState.invoked) {
|
|
1095
|
-
await journalGateEvaluationFinish(gateUnit, gateErrored, rejection);
|
|
1096
|
-
}
|
|
1097
|
-
// Codex round-3 finding A: a REQUIRED gate that could not be judged (the judge
|
|
1098
|
-
// threw, was unreachable, or returned an unparseable verdict) must NOT fail
|
|
1099
|
-
// open and advance. The gate row is journaled errored above; BLOCK the step (a
|
|
1100
|
-
// human resolves it) instead of silently passing an unjudged required gate.
|
|
1101
|
-
if (rejection?.errored) {
|
|
1102
|
-
const notes = `Step "${stepId}" has a REQUIRED completion gate but its summary-validation judge failed to return a verdict ` +
|
|
1103
|
-
`(the LLM threw, was unreachable, or returned an unparseable response). A required gate must be judged — refusing ` +
|
|
1104
|
-
`to fail open and silently pass it. The step is BLOCKED: fix the LLM/connection, then \`akm workflow resume ${runId}\` ` +
|
|
1105
|
-
`to re-evaluate the gate, or advance the step manually with \`akm workflow complete\`.`;
|
|
1106
|
-
await completeWorkflowStep({ runId, stepId, status: "blocked", notes, evidence: result.evidence, ...lease });
|
|
1107
|
-
return { kind: "blocked", summary: notes };
|
|
1165
|
+
if (gateUnit) {
|
|
1166
|
+
await journalGateEvaluationFinish(gateUnit, judgeState.errored, rejection);
|
|
1108
1167
|
}
|
|
1109
1168
|
if (!rejection) {
|
|
1110
1169
|
return { kind: "advanced", ...(summaryOverride !== undefined ? { summaryOverride } : {}) };
|
|
@@ -1117,31 +1176,6 @@ export async function finalizeExecutedStep(input) {
|
|
|
1117
1176
|
gateRejection: { stepId, missing: rejection.missing, feedback: rejection.feedback },
|
|
1118
1177
|
};
|
|
1119
1178
|
}
|
|
1120
|
-
// ── Frozen plan parse + integrity check (shared) ─────────────────────────────
|
|
1121
|
-
/**
|
|
1122
|
-
* Parse and integrity-check a run's frozen plan JSON (migration 006). Shared by
|
|
1123
|
-
* the engine loop's plan loader (`run-workflow.ts`) and the R3 brief/report
|
|
1124
|
-
* surfaces so all three apply the SAME corruption + hash checks — the frozen
|
|
1125
|
-
* plan the engine executes is the exact plan brief describes and report
|
|
1126
|
-
* validates against. A NULL `plan_json` is the CALLER's decision (the engine
|
|
1127
|
-
* warns and compiles from the asset; brief/report error), so this helper only
|
|
1128
|
-
* handles a PRESENT plan string.
|
|
1129
|
-
*/
|
|
1130
|
-
export function parseFrozenPlan(runId, planJson, planHash) {
|
|
1131
|
-
let plan;
|
|
1132
|
-
try {
|
|
1133
|
-
plan = JSON.parse(planJson);
|
|
1134
|
-
}
|
|
1135
|
-
catch {
|
|
1136
|
-
throw new UsageError(`Workflow run ${runId} has a corrupt frozen plan (plan_json is not valid JSON). ` +
|
|
1137
|
-
`The journaled plan cannot be executed — start a new run.`);
|
|
1138
|
-
}
|
|
1139
|
-
if (computePlanHash(plan) !== planHash) {
|
|
1140
|
-
throw new UsageError(`Workflow run ${runId} failed the frozen-plan integrity check: plan_json does not match plan_hash. ` +
|
|
1141
|
-
`The journaled plan was modified after the run started — refusing to execute it. Start a new run.`);
|
|
1142
|
-
}
|
|
1143
|
-
return plan;
|
|
1144
|
-
}
|
|
1145
1179
|
// ── Small helpers ────────────────────────────────────────────────────────────
|
|
1146
1180
|
function safeJson(value) {
|
|
1147
1181
|
try {
|