akm-cli 0.9.0-rc.4 → 0.9.0-rc.6
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 +122 -26
- package/README.md +7 -7
- package/SECURITY.md +11 -10
- package/dist/akm +2 -2
- package/dist/akm-migrate-storage +2 -2
- package/dist/assets/hints/cli-hints-full.md +60 -73
- package/dist/assets/hints/cli-hints-short.md +8 -12
- package/dist/assets/improve-strategies/default.json +5 -7
- package/dist/assets/improve-strategies/frequent.json +2 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +2 -2
- package/dist/assets/improve-strategies/quick.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +3 -3
- package/dist/assets/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/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 +10 -11
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -2
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +1 -1
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +18 -18
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +8 -9
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +13 -15
- package/dist/assets/tasks/core/extract.yml +2 -2
- package/dist/assets/tasks/core/improve.yml +1 -1
- package/dist/assets/templates/html/health.html +2 -3
- package/dist/cli/config-migrate.js +1328 -101
- package/dist/cli/invocation.js +255 -0
- package/dist/cli/parse-args.js +20 -83
- package/dist/cli/shared.js +15 -37
- package/dist/cli.js +18 -49
- package/dist/commands/agent/agent-dispatch.js +2 -2
- package/dist/commands/agent/contribute-cli.js +5 -4
- package/dist/commands/bundle/bundle-cli.js +68 -0
- package/dist/commands/bundle/bundle.js +219 -0
- package/dist/commands/completions.js +2 -2
- package/dist/commands/config-cli.js +6 -3
- package/dist/commands/env/env-binding.js +13 -9
- package/dist/commands/env/env-cli.js +49 -48
- package/dist/commands/env/secret-cli.js +24 -10
- package/dist/commands/events.js +2 -2
- package/dist/commands/feedback-cli.js +59 -33
- package/dist/commands/graph/graph.js +6 -4
- package/dist/commands/health/advisories.js +4 -4
- package/dist/commands/health/html-report.js +190 -593
- package/dist/commands/health/improve-metrics.js +240 -188
- package/dist/commands/health/metrics.js +9 -17
- package/dist/commands/health/report-view-model.js +509 -0
- package/dist/commands/health/surfaces.js +6 -3
- package/dist/commands/health/types-checks.js +4 -0
- package/dist/commands/health/types-improve.js +22 -0
- package/dist/commands/health/types-metrics.js +4 -0
- 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 +1 -2
- package/dist/commands/health.js +219 -161
- package/dist/commands/improve/collapse-detector.js +25 -14
- package/dist/commands/improve/consolidate/chunking.js +7 -5
- package/dist/commands/improve/consolidate.js +640 -740
- 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 +79 -49
- package/dist/commands/improve/distill/quality-gate.js +113 -35
- package/dist/commands/improve/distill-promotion-policy.js +24 -885
- package/dist/commands/improve/distill.js +565 -337
- package/dist/commands/improve/eligibility.js +75 -36
- package/dist/commands/improve/extract-cli.js +6 -6
- package/dist/commands/improve/extract-prompt.js +5 -34
- package/dist/commands/improve/extract.js +526 -357
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +11 -20
- package/dist/commands/improve/improve-result-file.js +7 -6
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +3 -9
- package/dist/commands/improve/improve.js +822 -727
- package/dist/commands/improve/locks.js +27 -95
- package/dist/commands/improve/loop-stages.js +941 -963
- package/dist/commands/improve/memory/derived-ref.js +122 -0
- package/dist/commands/improve/memory/memory-contradiction-detect.js +35 -38
- package/dist/commands/improve/memory/memory-improve.js +24 -37
- package/dist/commands/improve/outcome-loop.js +13 -33
- package/dist/commands/improve/preparation.js +889 -691
- package/dist/commands/improve/proactive-maintenance.js +37 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +974 -588
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +8 -4
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -40
- package/dist/commands/improve/source-identity.js +39 -26
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +288 -295
- package/dist/commands/lint/index.js +132 -19
- package/dist/commands/mv-cli.js +257 -234
- package/dist/commands/observability-cli.js +2 -1
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +17 -16
- package/dist/commands/proposal/proposal-cli.js +40 -77
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +39 -1
- package/dist/commands/proposal/propose.js +41 -19
- package/dist/commands/proposal/repository.js +566 -486
- package/dist/commands/proposal/validators/proposal-quality-validators.js +22 -6
- package/dist/commands/proposal/validators/proposal-validators.js +6 -5
- package/dist/commands/proposal/validators/proposals.js +5 -4
- package/dist/commands/read/curate.js +50 -28
- package/dist/commands/read/knowledge.js +25 -16
- package/dist/commands/read/remember-cli.js +12 -4
- package/dist/commands/read/search-cli.js +33 -33
- package/dist/commands/read/search.js +68 -55
- package/dist/commands/read/show.js +54 -135
- package/dist/commands/remember.js +7 -5
- package/dist/commands/sources/add-cli.js +10 -24
- package/dist/commands/sources/bundle-config-ops.js +58 -0
- package/dist/commands/sources/history.js +13 -8
- package/dist/commands/sources/info.js +10 -11
- package/dist/commands/sources/init.js +12 -21
- package/dist/commands/sources/installed-stashes.js +151 -138
- package/dist/commands/sources/schema-repair.js +5 -3
- package/dist/commands/sources/self-update.js +9 -7
- package/dist/commands/sources/source-add.js +116 -154
- package/dist/commands/sources/source-clone.js +33 -14
- package/dist/commands/sources/source-manage.js +43 -32
- package/dist/commands/sources/sources-cli.js +12 -7
- package/dist/commands/sources/stash-cli.js +12 -5
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/default-tasks.js +13 -13
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +71 -31
- package/dist/commands/workflow-cli.js +22 -26
- 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 +509 -0
- package/dist/core/adapter/adapters/akm-lint.js +381 -0
- package/dist/core/adapter/adapters/akm-metadata.js +416 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +150 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +196 -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 +117 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +413 -0
- package/dist/core/adapter/adapters/okf-adapter.js +223 -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 +212 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +154 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/recognize-match.js +42 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/asset-placement.js +229 -0
- package/dist/core/asset/asset-ref.js +107 -78
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +1 -1
- package/dist/core/asset/resolve-ref.js +223 -0
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/common.js +141 -49
- package/dist/core/config/config-io.js +0 -108
- package/dist/core/config/config-schema.js +94 -1039
- package/dist/core/config/config-sources.js +115 -36
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-walker.js +4 -2
- package/dist/core/config/config.js +21 -26
- package/dist/core/config/engine-semantics.js +0 -4
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +395 -0
- package/dist/core/config/schema/improve.js +76 -0
- package/dist/core/config/schema/index-config.js +183 -0
- package/dist/core/config/schema/output.js +15 -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 +27 -0
- package/dist/core/config/schema/sources-bundles.js +164 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +113 -8
- package/dist/core/errors.js +1 -1
- package/dist/core/events-types.js +4 -0
- package/dist/core/extra-params.js +1 -0
- package/dist/core/file-change.js +16 -0
- package/dist/core/fs-txn.js +316 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +9 -1
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +16 -0
- package/dist/core/migration-backup.js +332 -160
- package/dist/core/paths.js +3 -6
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +130 -0
- package/dist/core/redaction.js +2 -2
- package/dist/core/standards/resolve-standards-context.js +47 -64
- package/dist/core/standards/resolve-type-conventions.js +3 -3
- package/dist/core/state/migrations.js +201 -8
- package/dist/core/state-db.js +38 -2
- package/dist/core/subprocess.js +303 -0
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/write-source.js +39 -67
- package/dist/indexer/bundle-identity-guard.js +91 -0
- package/dist/indexer/db/graph-db.js +1 -1
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +29 -9
- package/dist/indexer/graph/graph-boost.js +28 -24
- package/dist/indexer/graph/graph-extraction.js +5 -6
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-written-assets.js +44 -17
- package/dist/indexer/indexer.js +289 -153
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +224 -0
- package/dist/indexer/passes/dir-staleness.js +2 -1
- package/dist/indexer/passes/memory-inference.js +8 -4
- package/dist/indexer/passes/metadata.js +96 -243
- package/dist/indexer/scan/doc-to-entry.js +123 -0
- package/dist/indexer/scan/drain-dir.js +144 -0
- package/dist/indexer/search/db-search.js +171 -99
- package/dist/indexer/search/fts-query.js +1 -1
- package/dist/indexer/search/ranking-contributors.js +57 -7
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +31 -6
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +109 -52
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/unmigrated-vaults-guard.js +2 -1
- package/dist/indexer/usage/usage-events.js +72 -9
- package/dist/indexer/walk/file-context.js +1 -44
- package/dist/indexer/walk/matchers.js +16 -32
- package/dist/indexer/walk/path-resolver.js +6 -5
- package/dist/indexer/walk/walker.js +4 -2
- package/dist/integrations/agent/engine-resolution.js +22 -0
- package/dist/integrations/agent/model-aliases.js +1 -1
- package/dist/integrations/agent/prompts.js +35 -11
- package/dist/integrations/agent/spawn.js +41 -270
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -2
- package/dist/integrations/harnesses/aider/index.js +2 -11
- package/dist/integrations/harnesses/amazonq/agent-builder.js +1 -9
- package/dist/integrations/harnesses/amazonq/index.js +3 -16
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +1 -12
- package/dist/integrations/harnesses/claude/session-log.js +27 -65
- package/dist/integrations/harnesses/codex/agent-builder.js +1 -2
- package/dist/integrations/harnesses/codex/index.js +2 -12
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +1 -12
- package/dist/integrations/harnesses/gemini/agent-builder.js +1 -3
- package/dist/integrations/harnesses/gemini/index.js +1 -12
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +27 -2
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +1 -12
- package/dist/integrations/harnesses/opencode/session-log.js +67 -110
- package/dist/integrations/harnesses/opencode-sdk/harness.js +2 -11
- package/dist/integrations/harnesses/openhands/agent-builder.js +3 -3
- package/dist/integrations/harnesses/openhands/index.js +2 -11
- package/dist/integrations/harnesses/pi/agent-builder.js +3 -11
- package/dist/integrations/harnesses/pi/index.js +3 -15
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +37 -2
- package/dist/integrations/lockfile.js +36 -0
- package/dist/integrations/session-logs/index.js +21 -12
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +7 -5
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +8 -4
- package/dist/llm/graph-extract.js +7 -2
- package/dist/llm/metadata-enhance.js +43 -26
- package/dist/llm/structured-call.js +15 -7
- package/dist/migrate/legacy/config-source-migration.js +223 -0
- package/dist/migrate/legacy/content-migration.js +305 -0
- package/dist/migrate/legacy/legacy-layout.js +779 -0
- package/dist/migrate/legacy/legacy-paths.js +25 -0
- package/dist/migrate/legacy/legacy-stash-json.js +72 -0
- package/dist/migrate/legacy/proposal-fs-import.js +168 -0
- package/dist/migrate/legacy/task-target-ref-migration.js +272 -0
- package/dist/migrate/legacy/three-db-cutover.js +840 -0
- package/dist/migrate/legacy/workflow-migrations-bodies.js +52 -0
- package/dist/migrate/legacy/workflow-migrations-frozen.js +21 -0
- package/dist/migrate/legacy-ref-grammar.js +209 -0
- package/dist/output/command-registry.js +27 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/renderers.js +32 -276
- package/dist/output/shapes/passthrough.js +3 -9
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/text/command-format.js +547 -0
- package/dist/output/text/helpers.js +15 -1377
- package/dist/output/text/proposal-format.js +230 -0
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +107 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/workflow-format.js +345 -0
- package/dist/output/text.js +1 -3
- package/dist/registry/build-index.js +13 -17
- package/dist/registry/providers/static-index.js +2 -2
- package/dist/registry/resolve.js +4 -90
- package/dist/registry/semver.js +93 -0
- package/dist/runtime.js +90 -0
- package/dist/schemas/akm-config.json +2956 -14890
- package/dist/schemas/akm-task.json +2 -2
- package/dist/schemas/akm-workflow.json +2 -2
- package/dist/scripts/migrate-storage.js +2312 -19642
- package/dist/setup/detect.js +5 -7
- package/dist/setup/engine-config.js +2 -1
- package/dist/setup/registry-stash-loader.js +1 -1
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +109 -39
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +50 -274
- package/dist/setup/steps/platforms.js +1 -0
- package/dist/setup/steps/sources.js +13 -6
- package/dist/setup/steps/stashdir.js +5 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/providers/git-provider.js +29 -33
- package/dist/sources/providers/git-stash.js +10 -5
- package/dist/sources/providers/provider-utils.js +40 -18
- package/dist/sources/providers/website.js +1 -1
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +105 -39
- package/dist/storage/database.js +47 -3
- package/dist/storage/engines/sqlite-migrations.js +34 -16
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/improve-runs-repository.js +0 -20
- package/dist/storage/repositories/index-connection.js +80 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +952 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +15 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +164 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +109 -0
- package/dist/storage/repositories/index-meta-repository.js +50 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +241 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +369 -0
- package/dist/storage/repositories/index-vec-repository.js +245 -0
- package/dist/storage/repositories/proposals-repository.js +41 -50
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/workflow-runs-repository.js +17 -11
- package/dist/tasks/backends/cron.js +5 -5
- package/dist/tasks/backends/exec-utils.js +21 -0
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +16 -17
- package/dist/tasks/backends/schtasks.js +33 -41
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/parser.js +25 -5
- package/dist/tasks/runner.js +33 -39
- package/dist/tasks/validator.js +5 -6
- package/dist/workflows/authoring/authoring.js +8 -11
- package/dist/workflows/exec/brief.js +10 -7
- package/dist/workflows/exec/report.js +261 -156
- package/dist/workflows/exec/run-workflow.js +2 -1
- package/dist/workflows/exec/step-work.js +3 -9
- package/dist/workflows/program/parser.js +4 -60
- package/dist/workflows/renderer.js +11 -60
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +7 -4
- package/dist/workflows/runtime/runs.js +11 -10
- 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 +27 -16
- package/dist/workflows/validate-summary.js +2 -2
- package/dist/workflows/validator.js +18 -2
- package/docs/migration/release-notes/0.9.0.md +45 -3
- package/docs/migration/v0.8-to-v0.9.md +466 -262
- package/docs/{data-and-telemetry.md → reference/data-and-telemetry.md} +61 -17
- package/package.json +10 -9
- package/schemas/akm-config.json +2956 -14890
- package/schemas/akm-task.json +2 -2
- package/schemas/akm-workflow.json +2 -2
- 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/improve-strategies/recombine-only.json +0 -23
- package/dist/assets/improve-strategies/synthesize.json +0 -17
- 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/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/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -262
- package/dist/commands/improve/procedural.js +0 -403
- package/dist/commands/improve/recombine.js +0 -843
- 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/wiki-cli.js +0 -291
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -316
- 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 -1585
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
- package/dist/output/text/wiki.js +0 -16
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -18228
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- 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 -1013
- package/dist/workflows/db.js +0 -413
- package/docs/README.md +0 -103
- 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
|
@@ -9,163 +9,40 @@
|
|
|
9
9
|
* strict superset of the skill's report.html; this module computes the 17
|
|
10
10
|
* `%%TOKEN%%` replacements it consumes.
|
|
11
11
|
*
|
|
12
|
+
* Structure (chunk-9 WI-9.5a / H2, anchors C.6): `./report-view-model.ts`
|
|
13
|
+
* holds the pure `AkmHealthResult`→`HealthReportViewModel` extractor — every
|
|
14
|
+
* computed number/string/series the report shows, with NO HTML. This module
|
|
15
|
+
* holds the thin VM→HTML fragment renderers (one per template section) plus
|
|
16
|
+
* `buildHealthHtmlReplacements`, the extract-then-render glue that produces
|
|
17
|
+
* the 17-token replacement map.
|
|
18
|
+
*
|
|
12
19
|
* Determinism: nothing here depends on Date.now()/Math.random(). Runs are
|
|
13
20
|
* sorted by startedAt; `%%GENERATED_AT%%` derives from the latest run (or the
|
|
14
21
|
* window anchor), so output is byte-identical for identical inputs.
|
|
22
|
+
*
|
|
23
|
+
* ECharts delivery (chunk-9 WI-9.4d, anchors C.1): CDN-only. The vendored
|
|
24
|
+
* `~1MB` `echarts.min.js` self-contained/"inline" mode was removed along with
|
|
25
|
+
* the asset itself — `buildEchartsTag` always emits a `<script src>` tag
|
|
26
|
+
* pointed at the jsDelivr CDN, so viewing the report now requires network
|
|
27
|
+
* access. BEHAVIOR CHANGE (plan-accepted, ledgered): previously
|
|
28
|
+
* `AKM_ECHARTS=cdn` opted IN to the CDN with "inline" as the default; there is
|
|
29
|
+
* no more env var or option to opt back into an offline report.
|
|
15
30
|
*/
|
|
16
|
-
import fs from "node:fs";
|
|
17
|
-
import path from "node:path";
|
|
18
31
|
import { escapeHtml } from "../../output/html-render.js";
|
|
19
|
-
import { getDirname } from "../../runtime.js";
|
|
20
32
|
import { pkgVersion } from "../../version.js";
|
|
21
|
-
import {
|
|
22
|
-
/**
|
|
23
|
-
* Distill skip-reasons hidden from the breakdown chart. `no new signal since
|
|
24
|
-
* last proposal` is the steady-state "nothing changed, nothing to do" outcome —
|
|
25
|
-
* it dominates the histogram and drowns out the actionable reasons, so it is
|
|
26
|
-
* intentionally excluded from the chart (the count still lives in the data).
|
|
27
|
-
*/
|
|
28
|
-
const DISTILL_REASONS_HIDDEN = new Set(["no new signal since last proposal"]);
|
|
29
|
-
const ECHARTS_CDN = "https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js";
|
|
30
|
-
const ECHARTS_VENDOR_PATH = path.join(getDirname(import.meta.url), "../../assets/templates/html/vendor/echarts.min.js");
|
|
31
|
-
// ── Small formatters (ports of render.py helpers) ───────────────────────────
|
|
33
|
+
import { buildHealthReportViewModel, compact, fmtMs, humanize, num, } from "./report-view-model.js";
|
|
32
34
|
const esc = escapeHtml;
|
|
33
35
|
/** Emit a <time> element that the browser's JS will reformat to the viewer's local timezone. */
|
|
34
36
|
function isoTimeTag(iso) {
|
|
35
37
|
const fallback = iso.slice(0, 16).replace("T", " ");
|
|
36
38
|
return `<time data-iso="${esc(iso)}">${esc(fallback)}</time>`;
|
|
37
39
|
}
|
|
38
|
-
function num(value) {
|
|
39
|
-
return Math.round(value).toLocaleString("en-US");
|
|
40
|
-
}
|
|
41
|
-
/** Compact token/count formatter, e.g. 127345 → "127K", 1_500_000 → "1.5M". */
|
|
42
|
-
function compact(value) {
|
|
43
|
-
const v = Math.round(value);
|
|
44
|
-
if (Math.abs(v) >= 1_000_000)
|
|
45
|
-
return `${(v / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
|
|
46
|
-
if (Math.abs(v) >= 1_000)
|
|
47
|
-
return `${(v / 1_000).toFixed(1).replace(/\.0$/, "")}K`;
|
|
48
|
-
return String(v);
|
|
49
|
-
}
|
|
50
|
-
/** Humanize a camelCase / kebab / snake enum into reader-facing text. */
|
|
51
|
-
function humanize(raw) {
|
|
52
|
-
return raw
|
|
53
|
-
.replace(/[-_]/g, " ")
|
|
54
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
55
|
-
.replace(/\s+/g, " ")
|
|
56
|
-
.trim()
|
|
57
|
-
.replace(/^./, (c) => c.toUpperCase());
|
|
58
|
-
}
|
|
59
|
-
function fmtMs(ms) {
|
|
60
|
-
return ms ? `${(ms / 60000).toFixed(1)}m` : "—";
|
|
61
|
-
}
|
|
62
|
-
function pct(rate, digits) {
|
|
63
|
-
return `${(rate * 100).toFixed(digits)}%`;
|
|
64
|
-
}
|
|
65
40
|
function trendClass(direction) {
|
|
66
41
|
return direction === "up" ? "trend-up" : direction === "down" ? "trend-down" : "trend-flat";
|
|
67
42
|
}
|
|
68
43
|
function trendLabel(direction) {
|
|
69
44
|
return direction === "up" ? "▲ up" : direction === "down" ? "▼ watch" : "— flat";
|
|
70
45
|
}
|
|
71
|
-
/** pctChange may be a number or an "+inf"/"-inf" sentinel (prior window was 0). */
|
|
72
|
-
function coercePct(raw) {
|
|
73
|
-
if (typeof raw === "number")
|
|
74
|
-
return raw;
|
|
75
|
-
if (typeof raw !== "string")
|
|
76
|
-
return undefined;
|
|
77
|
-
const s = raw.trim().toLowerCase();
|
|
78
|
-
if (s === "+inf" || s === "inf" || s === "infinity" || s === "+infinity")
|
|
79
|
-
return 1e9;
|
|
80
|
-
if (s === "-inf" || s === "-infinity")
|
|
81
|
-
return -1e9;
|
|
82
|
-
const parsed = Number.parseFloat(s.replace(/%$/, ""));
|
|
83
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
84
|
-
}
|
|
85
|
-
function reshapeRun(r) {
|
|
86
|
-
const cons = r.consolidation;
|
|
87
|
-
const mi = r.memoryInference;
|
|
88
|
-
const ge = r.graphExtraction;
|
|
89
|
-
const wall = r.wallTimeMs || 0;
|
|
90
|
-
const consMs = cons.durationMs || 0;
|
|
91
|
-
const miMs = mi.durationMs || 0;
|
|
92
|
-
const geMs = ge.durationMs || 0;
|
|
93
|
-
return {
|
|
94
|
-
id: r.id,
|
|
95
|
-
resultStatus: r.resultStatus ?? "valid",
|
|
96
|
-
resultComplete: r.resultComplete ?? (r.resultStatus === undefined || r.resultStatus === "valid"),
|
|
97
|
-
taskId: r.taskId ?? "manual",
|
|
98
|
-
strategy: r.strategy,
|
|
99
|
-
legacyProfile: r.legacyProfile,
|
|
100
|
-
startedAt: r.startedAt,
|
|
101
|
-
completedAt: r.completedAt,
|
|
102
|
-
wallTimeMs: wall,
|
|
103
|
-
ok: r.ok,
|
|
104
|
-
mode: r.scope.mode,
|
|
105
|
-
consDurationMs: consMs,
|
|
106
|
-
miDurationMs: miMs,
|
|
107
|
-
geDurationMs: geMs,
|
|
108
|
-
otherMs: Math.max(0, wall - consMs - miMs - geMs),
|
|
109
|
-
consRan: cons.ran,
|
|
110
|
-
promoted: cons.promoted,
|
|
111
|
-
merged: cons.merged,
|
|
112
|
-
deleted: cons.deleted,
|
|
113
|
-
contradicted: cons.contradicted,
|
|
114
|
-
judgedNoAction: cons.judgedNoAction,
|
|
115
|
-
processed: cons.processed,
|
|
116
|
-
failedChunks: cons.failedChunks,
|
|
117
|
-
totalChunks: cons.totalChunks,
|
|
118
|
-
miWritten: mi.written || mi.writes || 0,
|
|
119
|
-
miConsidered: mi.considered,
|
|
120
|
-
miYieldRate: mi.yieldRate,
|
|
121
|
-
miCacheHits: mi.cacheHits,
|
|
122
|
-
geEntities: ge.entities,
|
|
123
|
-
geRelations: ge.relations,
|
|
124
|
-
geCacheHitRate: ge.cacheHitRate,
|
|
125
|
-
geFailures: ge.failures,
|
|
126
|
-
distillSkipped: r.actions.distill.skipped,
|
|
127
|
-
distillQueued: r.actions.distill.queued,
|
|
128
|
-
distillLlmFailed: r.actions.distill.llmFailed,
|
|
129
|
-
distillByReason: r.actions.distill.skippedByReason,
|
|
130
|
-
reflectOk: r.actions.reflect.ok,
|
|
131
|
-
reflectFailed: r.actions.reflect.failed,
|
|
132
|
-
derived: r.memorySummary.derived,
|
|
133
|
-
eligible: r.memorySummary.eligible,
|
|
134
|
-
lintFlagged: r.lintFlagged,
|
|
135
|
-
lintFixed: r.lintFixed,
|
|
136
|
-
reflectsWithErrorContext: r.reflectsWithErrorContext,
|
|
137
|
-
orphansPurged: r.orphansPurged,
|
|
138
|
-
evalCasesWritten: r.evalCasesWritten,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
// ── Trend classification (port of collect.py classify) ──────────────────────
|
|
142
|
-
function classify(deltas, metricKeys, lowerIsBetter = false) {
|
|
143
|
-
const changes = metricKeys
|
|
144
|
-
.map((key) => coercePct(deltas[key]?.pctChange))
|
|
145
|
-
.filter((c) => c !== undefined);
|
|
146
|
-
if (changes.length === 0)
|
|
147
|
-
return "flat";
|
|
148
|
-
const avg = changes.reduce((acc, c) => acc + c, 0) / changes.length;
|
|
149
|
-
if (Math.abs(avg) < 5)
|
|
150
|
-
return "flat";
|
|
151
|
-
const direction = avg > 0 ? "up" : "down";
|
|
152
|
-
if (lowerIsBetter)
|
|
153
|
-
return avg > 0 ? "down" : "up";
|
|
154
|
-
return direction;
|
|
155
|
-
}
|
|
156
|
-
function buildTrend(deltas) {
|
|
157
|
-
const decisionQuality = classify(deltas, ["improve.memoryInference.yieldRate", "improve.consolidation.promoted"]);
|
|
158
|
-
const outputVolume = classify(deltas, [
|
|
159
|
-
"improve.consolidation.promoted",
|
|
160
|
-
"improve.memoryInference.written",
|
|
161
|
-
"improve.sessionExtraction.proposalsCreated",
|
|
162
|
-
]);
|
|
163
|
-
const failures = classify(deltas, ["improve.graphExtraction.failures"], true);
|
|
164
|
-
const latency = classify(deltas, ["improve.wallTime.medianMs", "improve.wallTime.p95Ms"], true);
|
|
165
|
-
const score = [decisionQuality, outputVolume, failures, latency].reduce((acc, d) => acc + (d === "up" ? 1 : d === "down" ? -1 : 0), 0);
|
|
166
|
-
const overall = score >= 1 ? "improving" : score <= -1 ? "degrading" : "mixed";
|
|
167
|
-
return { decisionQuality, outputVolume, failures, latency, overall };
|
|
168
|
-
}
|
|
169
46
|
function deltaPill(deltas, key, lowerIsBetter = false) {
|
|
170
47
|
const raw = deltas[key]?.pctChange;
|
|
171
48
|
if (raw === undefined)
|
|
@@ -196,249 +73,107 @@ function passCard(title, desc) {
|
|
|
196
73
|
`<div class="title">${esc(title)}</div>` +
|
|
197
74
|
`<div class="desc">${esc(desc)}</div></div></div>`);
|
|
198
75
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const blocked = check.status !== "pass" || status.toLowerCase().includes("block");
|
|
206
|
-
const entries = typeof evidence.entryCount === "number" ? evidence.entryCount : 0;
|
|
207
|
-
const embeddings = typeof evidence.embeddingCount === "number" ? evidence.embeddingCount : 0;
|
|
208
|
-
return { blocked, detail: `${num(entries)} entries, ${num(embeddings)} embeddings` };
|
|
209
|
-
}
|
|
76
|
+
// ── Status badges (presentation constants) ───────────────────────────────────
|
|
77
|
+
const badgeByStatus = {
|
|
78
|
+
pass: { badge: "badge-pass", dot: "dot-pass", label: "PASS" },
|
|
79
|
+
warn: { badge: "badge-warn", dot: "dot-warn", label: "WARN" },
|
|
80
|
+
fail: { badge: "badge-fail", dot: "dot-fail", label: "FAIL" },
|
|
81
|
+
};
|
|
210
82
|
// ── ECharts delivery ─────────────────────────────────────────────────────────
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return 0;
|
|
216
|
-
const n = Number(m[1]);
|
|
217
|
-
const mult = { m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 }[m[2].toLowerCase()] ?? 0;
|
|
218
|
-
return n * mult;
|
|
83
|
+
const ECHARTS_CDN = "https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js";
|
|
84
|
+
/** Always CDN — see the module docstring's chunk-9 WI-9.4d note. */
|
|
85
|
+
function buildEchartsTag() {
|
|
86
|
+
return `<script src="${ECHARTS_CDN}"></script>`;
|
|
219
87
|
}
|
|
220
88
|
/**
|
|
221
|
-
* Build the time-slice `<option>`s for the report's filter bar, DERIVED from
|
|
222
|
-
* actual report window so the choices always make
|
|
223
|
-
* 1d–21d list was useless on a 24h or 7d report).
|
|
224
|
-
* sub-window options carry their cutoff in
|
|
225
|
-
* filteredRuns), largest first
|
|
89
|
+
* Build the time-slice `<option>`s for the report's filter bar, DERIVED from
|
|
90
|
+
* the actual report window (`vm.sliceCandidates`) so the choices always make
|
|
91
|
+
* sense (the old hard-coded 1d–21d list was useless on a 24h or 7d report).
|
|
92
|
+
* "All" is the default; the sub-window options carry their cutoff in
|
|
93
|
+
* milliseconds (consumed by filteredRuns), largest first.
|
|
226
94
|
*/
|
|
227
|
-
function buildSliceOptions(
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const DAY = 86_400_000;
|
|
231
|
-
const candidates = [
|
|
232
|
-
[6 * HOUR, "6h"],
|
|
233
|
-
[12 * HOUR, "12h"],
|
|
234
|
-
[DAY, "1d"],
|
|
235
|
-
[3 * DAY, "3d"],
|
|
236
|
-
[7 * DAY, "7d"],
|
|
237
|
-
[14 * DAY, "14d"],
|
|
238
|
-
];
|
|
239
|
-
const subs = candidates
|
|
240
|
-
.filter(([ms]) => windowMs > 0 && ms < windowMs)
|
|
241
|
-
.sort((a, b) => b[0] - a[0])
|
|
242
|
-
.slice(0, 4);
|
|
243
|
-
const opts = [`<option value="all" selected>All (${esc(window)})</option>`];
|
|
244
|
-
for (const [ms, label] of subs)
|
|
95
|
+
function buildSliceOptions(vm) {
|
|
96
|
+
const opts = [`<option value="all" selected>All (${esc(vm.window)})</option>`];
|
|
97
|
+
for (const [ms, label] of vm.sliceCandidates)
|
|
245
98
|
opts.push(`<option value="${ms}">Last ${label}</option>`);
|
|
246
99
|
return opts.join("\n ");
|
|
247
100
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
101
|
+
// ── Fragment renderers (VM → HTML; one per template section) ────────────────
|
|
102
|
+
function renderSinceHuman(vm) {
|
|
103
|
+
return vm.sinceIso ? `${isoTimeTag(vm.sinceIso)} → now` : `last ${esc(vm.window)}`;
|
|
104
|
+
}
|
|
105
|
+
function renderStatusBadgeHtml(vm) {
|
|
106
|
+
const badge = badgeByStatus[vm.status];
|
|
107
|
+
const statusBadge = `<span class="badge-pill ${badge.badge}"><span class="dot ${badge.dot}"></span>${badge.label}</span>`;
|
|
108
|
+
const failBadge = `<span class="badge-pill ${vm.failOk ? "badge-pass" : "badge-warn"}">` +
|
|
109
|
+
`<span class="dot ${vm.failOk ? "dot-pass" : "dot-warn"}"></span>${vm.taskFailRate} Fail Rate</span>`;
|
|
110
|
+
return `${statusBadge}\n ${failBadge}`;
|
|
256
111
|
}
|
|
257
|
-
// ── Main builder ─────────────────────────────────────────────────────────────
|
|
258
112
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* fragments, matching the skill template. Advisories and "what to watch" are
|
|
263
|
-
* merged + de-duplicated into a single prioritized `%%ACTION_ITEMS_HTML%%`.
|
|
113
|
+
* Executive-summary block: quick numbers, trend pills, period-over-period
|
|
114
|
+
* deltas, the current-run snapshot, the window facts, the synthesized verdict
|
|
115
|
+
* sentence, and the freshness line.
|
|
264
116
|
*/
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
const compareRuns = (a, b) => a.startedAt.localeCompare(b.startedAt) || a.completedAt.localeCompare(b.completedAt) || a.id.localeCompare(b.id);
|
|
268
|
-
const allRuns = (result.runs ?? []).map(reshapeRun).sort(compareRuns);
|
|
269
|
-
const invalidRuns = allRuns.filter((run) => run.resultStatus === "invalid");
|
|
270
|
-
const runs = allRuns.filter((run) => run.resultStatus !== "invalid");
|
|
271
|
-
const improve = result.improve;
|
|
272
|
-
const proposals = opts.proposals;
|
|
273
|
-
const sem = readSemSearch(result.advisories);
|
|
274
|
-
const trend = buildTrend(deltas);
|
|
275
|
-
// ── Aggregates (collect.py step 6) ─────────────────────────────────────────
|
|
276
|
-
const cons = improve.consolidation;
|
|
277
|
-
const mi = improve.memoryInference;
|
|
278
|
-
const ge = improve.graphExtraction;
|
|
279
|
-
const wallTime = improve.wallTime;
|
|
280
|
-
// WS-5: perf telemetry, coverage, and degradation metrics.
|
|
281
|
-
const perf = improve.perfTelemetry ?? {
|
|
282
|
-
dedupPoolSize: 0,
|
|
283
|
-
llmPoolSize: 0,
|
|
284
|
-
judgedCacheSkipped: 0,
|
|
285
|
-
embedMs: 0,
|
|
286
|
-
embedCacheHits: 0,
|
|
287
|
-
embedCacheMisses: 0,
|
|
288
|
-
overBudgetRuns: 0,
|
|
289
|
-
runsWithTelemetry: 0,
|
|
290
|
-
};
|
|
291
|
-
const coverage = improve.coverage;
|
|
292
|
-
const degradation = improve.degradation;
|
|
293
|
-
const minting = improve.enrichmentMinting;
|
|
294
|
-
// #576: real per-stage LLM token/time accounting (replaces the GPU-time
|
|
295
|
-
// proxy). Optional-guarded so reports built from older health JSON without
|
|
296
|
-
// the aggregate still render.
|
|
297
|
-
const llm = result.metrics.llmUsage ?? {
|
|
298
|
-
calls: 0,
|
|
299
|
-
totalDurationMs: 0,
|
|
300
|
-
promptTokens: 0,
|
|
301
|
-
completionTokens: 0,
|
|
302
|
-
totalTokens: 0,
|
|
303
|
-
reasoningTokens: 0,
|
|
304
|
-
byStage: {},
|
|
305
|
-
byProcess: {},
|
|
306
|
-
byEngine: {},
|
|
307
|
-
};
|
|
308
|
-
const totalRuns = runs.length;
|
|
309
|
-
const failedRuns = runs.filter((r) => !r.ok).length;
|
|
310
|
-
const invoked = improve.invoked || totalRuns;
|
|
311
|
-
const completed = improve.completed || totalRuns - failedRuns;
|
|
312
|
-
const miWritten = mi.written || mi.writes || 0;
|
|
313
|
-
const completionRate = invoked ? `${Math.round((100 * completed) / invoked)}%` : "0%";
|
|
314
|
-
const taskFailRate = pct(result.metrics.taskFailRate, 1);
|
|
315
|
-
const agentFailRate = pct(result.metrics.agentFailureRate, 2);
|
|
316
|
-
const miYieldRate = pct(mi.yieldRate, 1);
|
|
317
|
-
const medianDurMin = (wallTime.medianMs / 60000).toFixed(1);
|
|
318
|
-
const p95DurMin = (wallTime.p95Ms / 60000).toFixed(1);
|
|
319
|
-
const avgPromoted = totalRuns ? String(Math.round(cons.promoted / totalRuns)) : "0";
|
|
320
|
-
const chunkFail = `${num(cons.failedChunks)} / ${num(cons.totalChunks)}`;
|
|
321
|
-
// ── Meta (collect.py steps 10-11) ──────────────────────────────────────────
|
|
322
|
-
const sinceIso = result.since;
|
|
323
|
-
const reportDate = sinceIso.slice(0, 10);
|
|
324
|
-
const sinceHuman = sinceIso ? `${isoTimeTag(sinceIso)} → now` : `last ${esc(opts.window)}`;
|
|
325
|
-
const reportTitle = reportDate ? `AKM Health Report — ${reportDate}` : "AKM Health Report";
|
|
326
|
-
const lastRun = runs[runs.length - 1];
|
|
327
|
-
const generatedAt = lastRun ? lastRun.completedAt || lastRun.startedAt || sinceIso : sinceIso;
|
|
328
|
-
const latest = [...runs]
|
|
329
|
-
.filter((run) => run.resultComplete)
|
|
330
|
-
.sort(compareRuns)
|
|
331
|
-
.at(-1);
|
|
332
|
-
// Freshness: surface the newest run's timestamp + the generated-at anchor in
|
|
333
|
-
// the exec card. Staleness is computed deterministically (no Date.now()) from
|
|
334
|
-
// the gap between the window start (`since`) and the newest run we have: if no
|
|
335
|
-
// run landed in the window, or the newest run sits in the first 25% of a
|
|
336
|
-
// window wider than the 6h threshold (i.e. a long idle tail), we flag stale.
|
|
337
|
-
const STALE_MS = 6 * 60 * 60 * 1000;
|
|
338
|
-
const latestRunMs = lastRun ? Date.parse(lastRun.completedAt || lastRun.startedAt) : NaN;
|
|
339
|
-
const sinceMs = Date.parse(sinceIso);
|
|
340
|
-
const generatedMs = Date.parse(generatedAt);
|
|
341
|
-
const idleTailMs = Number.isFinite(latestRunMs) && Number.isFinite(generatedMs) ? Math.max(0, generatedMs - latestRunMs) : 0;
|
|
342
|
-
const isStale = totalRuns === 0 || (Number.isFinite(latestRunMs) && Number.isFinite(sinceMs) && idleTailMs > STALE_MS);
|
|
343
|
-
const latestRunHuman = lastRun ? isoTimeTag(lastRun.completedAt || lastRun.startedAt) : "—";
|
|
344
|
-
// ── Status badges ──────────────────────────────────────────────────────────
|
|
345
|
-
const badgeByStatus = {
|
|
346
|
-
pass: { badge: "badge-pass", dot: "dot-pass", label: "PASS" },
|
|
347
|
-
warn: { badge: "badge-warn", dot: "dot-warn", label: "WARN" },
|
|
348
|
-
fail: { badge: "badge-fail", dot: "dot-fail", label: "FAIL" },
|
|
349
|
-
};
|
|
350
|
-
const badge = badgeByStatus[result.status];
|
|
351
|
-
const statusBadge = `<span class="badge-pill ${badge.badge}"><span class="dot ${badge.dot}"></span>${badge.label}</span>`;
|
|
352
|
-
const failOk = result.metrics.taskFailRate < TASK_FAIL_RATE_WARN;
|
|
353
|
-
const failBadge = `<span class="badge-pill ${failOk ? "badge-pass" : "badge-warn"}">` +
|
|
354
|
-
`<span class="dot ${failOk ? "dot-pass" : "dot-warn"}"></span>${taskFailRate} Fail Rate</span>`;
|
|
355
|
-
// ── Executive summary ──────────────────────────────────────────────────────
|
|
117
|
+
function renderExecSummary(vm) {
|
|
118
|
+
const badge = badgeByStatus[vm.status];
|
|
356
119
|
const li = (k, vHtml) => `<li><span class="k">${esc(k)}</span><span class="v">${vHtml}</span></li>`;
|
|
357
120
|
const trendLi = (k, d) => li(k, `<span class="trend-pill ${d === "flat" ? "flat" : d}">${d}</span>`);
|
|
358
121
|
const quickNumbers = [
|
|
359
|
-
li("Task fail rate", taskFailRate),
|
|
360
|
-
li("Agent fail rate", agentFailRate),
|
|
361
|
-
li("Improve completion", `${num(completed)} / ${num(invoked)} (${completionRate})`),
|
|
362
|
-
li("MI yield rate", miYieldRate),
|
|
363
|
-
li("MI written", num(miWritten)),
|
|
364
|
-
li("Consolidation promoted", num(
|
|
365
|
-
li("Consolidation judged: no action", `<abbr title="Candidates the consolidator reviewed but intentionally left unchanged (the 'judgedNoAction' field).">${num(
|
|
366
|
-
li("Chunk failure", chunkFail),
|
|
367
|
-
li("Median wall time", fmtMs(wallTime.medianMs)),
|
|
368
|
-
li("P95 wall time", fmtMs(wallTime.p95Ms)),
|
|
122
|
+
li("Task fail rate", vm.taskFailRate),
|
|
123
|
+
li("Agent fail rate", vm.agentFailRate),
|
|
124
|
+
li("Improve completion", `${num(vm.completed)} / ${num(vm.invoked)} (${vm.completionRate})`),
|
|
125
|
+
li("MI yield rate", vm.miYieldRate),
|
|
126
|
+
li("MI written", num(vm.miWritten)),
|
|
127
|
+
li("Consolidation promoted", num(vm.consolidation.promoted)),
|
|
128
|
+
li("Consolidation judged: no action", `<abbr title="Candidates the consolidator reviewed but intentionally left unchanged (the 'judgedNoAction' field).">${num(vm.consolidation.judgedNoAction)}</abbr>`),
|
|
129
|
+
li("Chunk failure", vm.chunkFail),
|
|
130
|
+
li("Median wall time", fmtMs(vm.wallTime.medianMs)),
|
|
131
|
+
li("P95 wall time", fmtMs(vm.wallTime.p95Ms)),
|
|
369
132
|
].join("");
|
|
370
133
|
const trendRows = [
|
|
371
|
-
trendLi("Decision quality", trend.decisionQuality),
|
|
372
|
-
trendLi("Output volume", trend.outputVolume),
|
|
373
|
-
trendLi("Failures", trend.failures),
|
|
374
|
-
trendLi("Latency", trend.latency),
|
|
134
|
+
trendLi("Decision quality", vm.trend.decisionQuality),
|
|
135
|
+
trendLi("Output volume", vm.trend.outputVolume),
|
|
136
|
+
trendLi("Failures", vm.trend.failures),
|
|
137
|
+
trendLi("Latency", vm.trend.latency),
|
|
375
138
|
].join("");
|
|
376
139
|
const deltaRows = [
|
|
377
|
-
li("Promoted", deltaPill(deltas, "improve.consolidation.promoted")),
|
|
378
|
-
li("MI written", deltaPill(deltas, "improve.memoryInference.written")),
|
|
379
|
-
li("MI yield", deltaPill(deltas, "improve.memoryInference.yieldRate")),
|
|
380
|
-
li("Median wall", deltaPill(deltas, "improve.wallTime.medianMs", true)),
|
|
381
|
-
li("P95 wall", deltaPill(deltas, "improve.wallTime.p95Ms", true)),
|
|
140
|
+
li("Promoted", deltaPill(vm.deltas, "improve.consolidation.promoted")),
|
|
141
|
+
li("MI written", deltaPill(vm.deltas, "improve.memoryInference.written")),
|
|
142
|
+
li("MI yield", deltaPill(vm.deltas, "improve.memoryInference.yieldRate")),
|
|
143
|
+
li("Median wall", deltaPill(vm.deltas, "improve.wallTime.medianMs", true)),
|
|
144
|
+
li("P95 wall", deltaPill(vm.deltas, "improve.wallTime.p95Ms", true)),
|
|
382
145
|
].join("");
|
|
383
|
-
const snapRows = latest
|
|
146
|
+
const snapRows = vm.latest
|
|
384
147
|
? [
|
|
385
|
-
li("Run id", `<code>${esc(latest.id.slice(0, 28))}</code>`),
|
|
386
|
-
li("Completed", isoTimeTag(latest.completedAt || latest.startedAt)),
|
|
387
|
-
li("Status", latest.ok ? "✅ ok" : "❌ failed"),
|
|
388
|
-
li("Wall time", fmtMs(latest.wallTimeMs)),
|
|
389
|
-
li("Reflect ok/fail", `${latest.reflectOk} / ${latest.reflectFailed}`),
|
|
390
|
-
li("Promoted", String(latest.promoted)),
|
|
391
|
-
li("Judged: no action", `<abbr title="Candidates reviewed but intentionally left unchanged on this run.">${latest.judgedNoAction}</abbr>`),
|
|
392
|
-
li("MI written", String(latest.miWritten)),
|
|
393
|
-
li("Graph entities/relations", `${latest.geEntities} / ${latest.geRelations}`),
|
|
148
|
+
li("Run id", `<code>${esc(vm.latest.id.slice(0, 28))}</code>`),
|
|
149
|
+
li("Completed", isoTimeTag(vm.latest.completedAt || vm.latest.startedAt)),
|
|
150
|
+
li("Status", vm.latest.ok ? "✅ ok" : "❌ failed"),
|
|
151
|
+
li("Wall time", fmtMs(vm.latest.wallTimeMs)),
|
|
152
|
+
li("Reflect ok/fail", `${vm.latest.reflectOk} / ${vm.latest.reflectFailed}`),
|
|
153
|
+
li("Promoted", String(vm.latest.promoted)),
|
|
154
|
+
li("Judged: no action", `<abbr title="Candidates reviewed but intentionally left unchanged on this run.">${vm.latest.judgedNoAction}</abbr>`),
|
|
155
|
+
li("MI written", String(vm.latest.miWritten)),
|
|
156
|
+
li("Graph entities/relations", `${vm.latest.geEntities} / ${vm.latest.geRelations}`),
|
|
394
157
|
].join("")
|
|
395
158
|
: '<li><span class="k">No runs in window</span><span class="v">—</span></li>';
|
|
396
159
|
const windowRows = [
|
|
397
|
-
li("Report window", esc(
|
|
398
|
-
li("Compare window", esc(
|
|
399
|
-
li("Runs", `${num(totalRuns)} (${failedRuns} failed)`),
|
|
400
|
-
li("Included result rows", num(
|
|
401
|
-
li("Normalized result rows", num(
|
|
402
|
-
li("Invalid result rows skipped", num(
|
|
403
|
-
li("Stash derived", `<abbr title="Whole-stash recount of derived assets at report time — not a per-run sum.">${num(
|
|
404
|
-
li("Stash eligible", `<abbr title="Whole-stash recount of eligible assets at report time — not a per-run sum.">${num(
|
|
405
|
-
li("Pending proposals", String(proposals.length)),
|
|
406
|
-
li("Semantic search", sem.blocked ? "BLOCKED" : "OK"),
|
|
160
|
+
li("Report window", esc(vm.window)),
|
|
161
|
+
li("Compare window", esc(vm.compare)),
|
|
162
|
+
li("Runs", `${num(vm.totalRuns)} (${vm.failedRuns} failed)`),
|
|
163
|
+
li("Included result rows", num(vm.includedResultRows)),
|
|
164
|
+
li("Normalized result rows", num(vm.normalizedResultRows)),
|
|
165
|
+
li("Invalid result rows skipped", num(vm.skippedInvalidResultRows)),
|
|
166
|
+
li("Stash derived", `<abbr title="Whole-stash recount of derived assets at report time — not a per-run sum.">${num(vm.memorySummary.derived)}</abbr>`),
|
|
167
|
+
li("Stash eligible", `<abbr title="Whole-stash recount of eligible assets at report time — not a per-run sum.">${num(vm.memorySummary.eligible)}</abbr>`),
|
|
168
|
+
li("Pending proposals", String(vm.proposals.length)),
|
|
169
|
+
li("Semantic search", vm.sem.blocked ? "BLOCKED" : "OK"),
|
|
407
170
|
].join("");
|
|
408
|
-
const overallEmoji = trend.overall === "improving" ? "📈" : trend.overall === "degrading" ? "📉" : "↔️";
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
// alongside the trend; only a degraded status (WARN/FAIL) leads with them.
|
|
415
|
-
const concerns = [];
|
|
416
|
-
if (failedRuns > 0)
|
|
417
|
-
concerns.push(`${failedRuns} failed run${failedRuns === 1 ? "" : "s"}`);
|
|
418
|
-
if (sem.blocked)
|
|
419
|
-
concerns.push("semantic search blocked");
|
|
420
|
-
if (proposals.length > 0)
|
|
421
|
-
concerns.push(`${proposals.length} pending proposal${proposals.length === 1 ? "" : "s"}`);
|
|
422
|
-
const trendClause = trend.overall === "improving"
|
|
423
|
-
? "throughput and latency improving"
|
|
424
|
-
: trend.overall === "degrading"
|
|
425
|
-
? "throughput or latency degrading"
|
|
426
|
-
: "throughput and latency steady";
|
|
427
|
-
const verdictWord = badge.label;
|
|
428
|
-
let verdictRest;
|
|
429
|
-
if (result.status === "pass") {
|
|
430
|
-
const watch = concerns.length > 0 ? `; watch: ${concerns.slice(0, 3).join(", ")}` : "";
|
|
431
|
-
verdictRest = `healthy, ${trendClause}${watch}`;
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
const lead = concerns.length > 0 ? concerns.slice(0, 3).join("; ") : trendClause;
|
|
435
|
-
verdictRest = trend.overall === "degrading" && concerns.length > 0 ? `${lead}; ${trendClause}` : lead;
|
|
436
|
-
}
|
|
437
|
-
const verdictSentence = `${verdictWord} — ${esc(verdictRest)}.`;
|
|
438
|
-
const verdictHtml = `<div class="verdict ${result.status}"><b>Verdict:</b> ${verdictSentence}</div>`;
|
|
439
|
-
// ── Freshness line ─────────────────────────────────────────────────────────
|
|
440
|
-
const freshnessHtml = `<div class="freshness${isStale ? " stale" : ""}">${isStale ? "⚠️ Stale: " : ""}Latest run ${latestRunHuman} · generated ${isoTimeTag(generatedAt)}${isStale ? " — no recent activity (newest run older than the 6h freshness threshold)." : "."}</div>`;
|
|
441
|
-
const execSummary = `
|
|
171
|
+
const overallEmoji = vm.trend.overall === "improving" ? "📈" : vm.trend.overall === "degrading" ? "📉" : "↔️";
|
|
172
|
+
const verdictSentence = `${vm.verdictWord} — ${esc(vm.verdictRest)}.`;
|
|
173
|
+
const verdictHtml = `<div class="verdict ${vm.status}"><b>Verdict:</b> ${verdictSentence}</div>`;
|
|
174
|
+
const latestRunHuman = vm.lastRun ? isoTimeTag(vm.lastRun.completedAt || vm.lastRun.startedAt) : "—";
|
|
175
|
+
const freshnessHtml = `<div class="freshness${vm.isStale ? " stale" : ""}">${vm.isStale ? "⚠️ Stale: " : ""}Latest run ${latestRunHuman} · generated ${isoTimeTag(vm.generatedAt)}${vm.isStale ? " — no recent activity (newest run older than the 6h freshness threshold)." : "."}</div>`;
|
|
176
|
+
return `
|
|
442
177
|
<h2>${overallEmoji} Executive Summary
|
|
443
178
|
<span class="badge-pill ${badge.badge}" style="font-size:11px;">
|
|
444
179
|
<span class="dot ${badge.dot}"></span>${badge.label}</span></h2>
|
|
@@ -450,7 +185,7 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
450
185
|
<ul>${quickNumbers}</ul>
|
|
451
186
|
</div>
|
|
452
187
|
<div>
|
|
453
|
-
<h4>Trend vs prior ${esc(
|
|
188
|
+
<h4>Trend vs prior ${esc(vm.compare)}</h4>
|
|
454
189
|
<ul>${trendRows}</ul>
|
|
455
190
|
<h4 style="margin-top:14px;">Period-over-period deltas</h4>
|
|
456
191
|
<ul>${deltaRows}</ul>
|
|
@@ -464,200 +199,55 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
464
199
|
<ul>${windowRows}</ul>
|
|
465
200
|
</div>
|
|
466
201
|
</div>
|
|
467
|
-
<div class="overall">Overall trend: <b>${esc(trend.overall)}</b> ${overallEmoji}
|
|
202
|
+
<div class="overall">Overall trend: <b>${esc(vm.trend.overall)}</b> ${overallEmoji}
|
|
468
203
|
· based on decision quality, output volume, failures, and latency vs the prior window.</div>`.trim();
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
const
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Color is a health SIGNAL, not decoration: green/yellow/red where a card has
|
|
207
|
+
* a meaningful threshold; "neutral" for purely-informational counts. Cards
|
|
208
|
+
* are ordered by operator priority (failures first, informational counts
|
|
209
|
+
* last).
|
|
210
|
+
*/
|
|
211
|
+
function renderKpiCards(vm) {
|
|
212
|
+
const semValue = vm.sem.blocked ? "BLOCKED" : "OK";
|
|
213
|
+
const semColor = vm.sem.blocked ? "red" : "green";
|
|
214
|
+
const semStyle = vm.sem.blocked ? "font-size:18px;" : "";
|
|
215
|
+
const completionColor = vm.completionPct >= 99 ? "green" : vm.completionPct >= 90 ? "yellow" : "red";
|
|
479
216
|
const kpiCard = (color, label, value, sub, valueStyle = "") => `<div class="kpi-card ${color}">
|
|
480
217
|
<div class="label">${label}</div>
|
|
481
218
|
<div class="value"${valueStyle ? ` style="${valueStyle}"` : ""}>${value}</div>
|
|
482
219
|
<div class="sub">${sub}</div>
|
|
483
220
|
</div>`;
|
|
484
|
-
|
|
485
|
-
kpiCard(failedRuns === 0 ? "green" : "red", "Failed Runs", String(failedRuns), `of ${num(totalRuns)} runs · ${taskFailRate} task fail`),
|
|
486
|
-
kpiCard(completionColor, "Completion Rate", completionRate, `${num(completed)} / ${num(invoked)} invoked`),
|
|
487
|
-
kpiCard("neutral", "Median Duration", `${medianDurMin}m`, `p95 = ${p95DurMin}m`),
|
|
488
|
-
kpiCard("blue", "Total Promoted", num(
|
|
489
|
-
kpiCard("blue", "MI Written", num(miWritten), `${miYieldRate} yield rate`),
|
|
490
|
-
kpiCard("purple", "Graph Entities", num(
|
|
491
|
-
kpiCard("neutral", "Stash Derived", num(
|
|
221
|
+
return [
|
|
222
|
+
kpiCard(vm.failedRuns === 0 ? "green" : "red", "Failed Runs", String(vm.failedRuns), `of ${num(vm.totalRuns)} runs · ${vm.taskFailRate} task fail`),
|
|
223
|
+
kpiCard(completionColor, "Completion Rate", vm.completionRate, `${num(vm.completed)} / ${num(vm.invoked)} invoked`),
|
|
224
|
+
kpiCard("neutral", "Median Duration", `${vm.medianDurMin}m`, `p95 = ${vm.p95DurMin}m`),
|
|
225
|
+
kpiCard("blue", "Total Promoted", num(vm.consolidation.promoted), `avg ${vm.avgPromoted} / run`),
|
|
226
|
+
kpiCard("blue", "MI Written", num(vm.miWritten), `${vm.miYieldRate} yield rate`),
|
|
227
|
+
kpiCard("purple", "Graph Entities", num(vm.graphExtraction.entities), `+${num(vm.graphExtraction.relations)} relations`),
|
|
228
|
+
kpiCard("neutral", "Stash Derived", num(vm.memorySummary.derived), `of ${num(vm.memorySummary.eligible)} eligible (whole-stash)`),
|
|
492
229
|
// #576: real LLM work — duration leads, tokens compact, not a GPU proxy.
|
|
493
|
-
kpiCard(llm.calls > 0 ? "purple" : "neutral", "🧠 LLM Work", `${llmTokensCompact} tok`, `${fmtMs(llm.totalDurationMs)} · ${num(llm.calls)} calls · ${compact(llm.reasoningTokens)} reasoning`),
|
|
494
|
-
kpiCard(semColor, "Semantic Search", semValue, esc(sem.detail), semStyle),
|
|
495
|
-
kpiCard(proposals.length > 0 ? "yellow" : "neutral", "Pending Proposals", String(proposals.length), `from ${esc(
|
|
230
|
+
kpiCard(vm.llm.calls > 0 ? "purple" : "neutral", "🧠 LLM Work", `${vm.llmTokensCompact} tok`, `${fmtMs(vm.llm.totalDurationMs)} · ${num(vm.llm.calls)} calls · ${compact(vm.llm.reasoningTokens)} reasoning`),
|
|
231
|
+
kpiCard(semColor, "Semantic Search", semValue, esc(vm.sem.detail), semStyle),
|
|
232
|
+
kpiCard(vm.proposals.length > 0 ? "yellow" : "neutral", "Pending Proposals", String(vm.proposals.length), `from ${esc(vm.window)} batch`),
|
|
496
233
|
].join("\n");
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
.sort();
|
|
501
|
-
const runsJsConst = `const RUNS = ${JSON.stringify(runs)};`;
|
|
502
|
-
// ── Summary table rows ─────────────────────────────────────────────────────
|
|
503
|
-
// Optional 4th element = a glossary tooltip rendered as <abbr title>.
|
|
504
|
-
const summaryRows = [
|
|
505
|
-
["Task fail rate", taskFailRate, "flat"],
|
|
506
|
-
["Agent fail rate", agentFailRate, "flat"],
|
|
507
|
-
["Improve completion", `${num(completed)} / ${num(invoked)}`, "flat"],
|
|
508
|
-
[
|
|
509
|
-
"MI yield rate",
|
|
510
|
-
miYieldRate,
|
|
511
|
-
trend.decisionQuality,
|
|
512
|
-
"Memory-inference yield: share of considered candidates that produced a written fact.",
|
|
513
|
-
],
|
|
514
|
-
["MI written", num(miWritten), trend.outputVolume, "Memory-inference: facts written this window."],
|
|
515
|
-
["Consolidation promoted", num(cons.promoted), trend.outputVolume],
|
|
516
|
-
["Consolidation merged", num(cons.merged), "flat"],
|
|
517
|
-
["Consolidation deleted", num(cons.deleted), "flat"],
|
|
518
|
-
["Consolidation contradicted", num(cons.contradicted), "flat"],
|
|
519
|
-
[
|
|
520
|
-
"Consolidation judged: no action",
|
|
521
|
-
num(cons.judgedNoAction),
|
|
522
|
-
"flat",
|
|
523
|
-
"Candidates reviewed but intentionally left unchanged (the 'judgedNoAction' field).",
|
|
524
|
-
],
|
|
525
|
-
["Chunk failure", chunkFail, "flat"],
|
|
526
|
-
["Graph entities", num(ge.entities), "up"],
|
|
527
|
-
["Graph relations", num(ge.relations), "up"],
|
|
528
|
-
[
|
|
529
|
-
"Stash derived",
|
|
530
|
-
num(improve.memorySummary.derived),
|
|
531
|
-
"up",
|
|
532
|
-
"Whole-stash recount of derived assets at report time — not a per-run sum.",
|
|
533
|
-
],
|
|
534
|
-
["Median wall time", fmtMs(wallTime.medianMs), trend.latency],
|
|
535
|
-
["P95 wall time", fmtMs(wallTime.p95Ms), trend.latency],
|
|
536
|
-
// #576: real LLM accounting (replaces the GPU-time proxy).
|
|
537
|
-
["LLM calls", num(llm.calls), "flat"],
|
|
538
|
-
["LLM total tokens", num(llm.totalTokens), "flat"],
|
|
539
|
-
["LLM prompt tokens", num(llm.promptTokens), "flat"],
|
|
540
|
-
["LLM completion tokens", num(llm.completionTokens), "flat"],
|
|
541
|
-
[
|
|
542
|
-
"LLM reasoning tokens",
|
|
543
|
-
num(llm.reasoningTokens),
|
|
544
|
-
"flat",
|
|
545
|
-
"Tokens spent on model reasoning/thinking, billed separately from prompt and completion.",
|
|
546
|
-
],
|
|
547
|
-
["LLM wall time", fmtMs(llm.totalDurationMs), trend.latency],
|
|
548
|
-
];
|
|
549
|
-
// #612 — auto-accept gate calibration. Only surface when the gate actually
|
|
550
|
-
// acted on proposals in the window (samples > 0); a default ungated install
|
|
551
|
-
// reports an empty summary and we omit the rows to keep the table parity-clean.
|
|
552
|
-
const calibration = improve.calibration;
|
|
553
|
-
if (calibration && calibration.samples > 0) {
|
|
554
|
-
summaryRows.push([
|
|
555
|
-
"Calibration samples",
|
|
556
|
-
num(calibration.samples),
|
|
557
|
-
"flat",
|
|
558
|
-
"Auto-accept gate decisions (auto-accepted + auto-rejected) the calibration join measured this window.",
|
|
559
|
-
], [
|
|
560
|
-
"Calibration accept rate",
|
|
561
|
-
String(calibration.overallAcceptRate),
|
|
562
|
-
"flat",
|
|
563
|
-
"Realized accept rate of acted-on gate decisions (auto-accepted / total acted-on).",
|
|
564
|
-
], [
|
|
565
|
-
"Calibration gap",
|
|
566
|
-
String(calibration.calibrationGap),
|
|
567
|
-
"flat",
|
|
568
|
-
"Mean predicted confidence minus realized accept rate. Positive = the gate is over-confident.",
|
|
569
|
-
]);
|
|
570
|
-
}
|
|
571
|
-
// WS-5: denominator-fixed coverage rows (only when we have real data).
|
|
572
|
-
if (coverage && !Number.isNaN(coverage.rate)) {
|
|
573
|
-
summaryRows.push([
|
|
574
|
-
"Coverage rate",
|
|
575
|
-
pct(coverage.rate, 1),
|
|
576
|
-
"flat",
|
|
577
|
-
"Distinct accepted refs / total stash assets (denominator-fixed). Shows what fraction of the corpus has been touched.",
|
|
578
|
-
], [
|
|
579
|
-
"Eligible fraction",
|
|
580
|
-
pct(coverage.eligibleFraction, 1),
|
|
581
|
-
"flat",
|
|
582
|
-
"Eligible assets / total stash assets. Fraction the improve pipeline actively considers.",
|
|
583
|
-
], [
|
|
584
|
-
"Coverage accepted",
|
|
585
|
-
num(coverage.acceptedProposals),
|
|
586
|
-
"flat",
|
|
587
|
-
"Total accepted proposals in the window (raw volume — includes repeated rewrites of the same asset).",
|
|
588
|
-
], [
|
|
589
|
-
"Churn ratio",
|
|
590
|
-
Number.isFinite(coverage.churnRatio) ? num(coverage.churnRatio) : "—",
|
|
591
|
-
Number.isFinite(coverage.churnRatio) && coverage.churnRatio > 1.5 ? "down" : "flat",
|
|
592
|
-
"Accepted proposals / distinct refs touched. >1.5 = the loop is repeatedly rewriting the same assets (churn, not coverage).",
|
|
593
|
-
]);
|
|
594
|
-
}
|
|
595
|
-
// Enrichment-vs-minting policy rollup (reporting-only).
|
|
596
|
-
if (minting && Number.isFinite(minting.share)) {
|
|
597
|
-
summaryRows.push([
|
|
598
|
-
"Enrichment-lane minted share",
|
|
599
|
-
pct(minting.share, 1),
|
|
600
|
-
minting.share > 0.05 ? "down" : "flat",
|
|
601
|
-
`New assets minted by enrichment lanes / their accepted total (${minting.minted} minted vs ${minting.updated} updated). Enrichment lanes are ratified to edit existing assets only; WARN >5%, FAIL >15%.`,
|
|
602
|
-
]);
|
|
603
|
-
}
|
|
604
|
-
// WS-5: perf telemetry rows (only when at least one run reported telemetry).
|
|
605
|
-
if (perf.runsWithTelemetry > 0) {
|
|
606
|
-
const embedCacheTotal = perf.embedCacheHits + perf.embedCacheMisses;
|
|
607
|
-
const embedCacheHitRate = embedCacheTotal > 0 ? pct(perf.embedCacheHits / embedCacheTotal, 1) : "—";
|
|
608
|
-
summaryRows.push([
|
|
609
|
-
"Embed cache hit rate",
|
|
610
|
-
embedCacheHitRate,
|
|
611
|
-
"flat",
|
|
612
|
-
"Fraction of embedding lookups served from cache (>95% is healthy). Aggregated across WS-5 runs.",
|
|
613
|
-
], [
|
|
614
|
-
"Embed wall time",
|
|
615
|
-
fmtMs(perf.embedMs),
|
|
616
|
-
"flat",
|
|
617
|
-
"Cumulative embedding wall-clock time across consolidation runs in the window.",
|
|
618
|
-
], [
|
|
619
|
-
"Judged-cache skipped",
|
|
620
|
-
num(perf.judgedCacheSkipped),
|
|
621
|
-
"flat",
|
|
622
|
-
"Candidates skipped by the judged-cache (not sent to LLM). Higher = more efficient reuse of prior judgments.",
|
|
623
|
-
], [
|
|
624
|
-
"Dedup pool size",
|
|
625
|
-
num(perf.dedupPoolSize),
|
|
626
|
-
"flat",
|
|
627
|
-
"Average memory pool size after deduplication (before judged-cache narrowing). WS-5 perf telemetry.",
|
|
628
|
-
], [
|
|
629
|
-
"Over-budget consolidation runs",
|
|
630
|
-
String(perf.overBudgetRuns),
|
|
631
|
-
perf.overBudgetRuns > 0 ? "down" : "flat",
|
|
632
|
-
"Runs where consolidation alone exceeded the total run budget (estimatedBudgetFractionUsed > 1.0).",
|
|
633
|
-
]);
|
|
634
|
-
}
|
|
635
|
-
// WS-5: degradation metrics rows.
|
|
636
|
-
if (degradation) {
|
|
637
|
-
summaryRows.push([
|
|
638
|
-
"Corpus diversity (Gini)",
|
|
639
|
-
num(degradation.corpusCentroidDistance),
|
|
640
|
-
degradation.entrenchmentFlagged || degradation.salienceUniformityFlagged ? "down" : "flat",
|
|
641
|
-
"Gini coefficient of retrieval_salience for top-100 ranked assets. Two-tailed: >0.35 = entrenchment risk; <0.08 = collapsed toward uniform (ranking no longer discriminates).",
|
|
642
|
-
], [
|
|
643
|
-
"Merge fidelity contradiction rate",
|
|
644
|
-
pct(degradation.mergeFidelityContradictionRate, 1),
|
|
645
|
-
"flat",
|
|
646
|
-
"Fraction of consolidated proposals that involved a contradiction, from consolidation result envelopes.",
|
|
647
|
-
]);
|
|
648
|
-
}
|
|
649
|
-
const summaryRowsHtml = summaryRows
|
|
234
|
+
}
|
|
235
|
+
function renderSummaryRowsHtml(vm) {
|
|
236
|
+
return vm.summaryRows
|
|
650
237
|
.map(([label, value, t, tip]) => {
|
|
651
238
|
const labelHtml = tip ? `<abbr title="${esc(tip)}">${esc(label)}</abbr>` : esc(label);
|
|
652
239
|
return (` <tr><td>${labelHtml}</td><td>${esc(value)}</td>` +
|
|
653
240
|
`<td class="trend ${trendClass(t)}">${trendLabel(t)}</td></tr>`);
|
|
654
241
|
})
|
|
655
242
|
.join("\n");
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Merge + de-duplicate advisories and the old "what to watch" cards (built
|
|
246
|
+
* from the same VM data — invalid rows, sem-blocked, proposals, tail-latency,
|
|
247
|
+
* failed-runs, …) into ONE prioritized list (P1/P2/P3 + a remediation command
|
|
248
|
+
* per item), keyed so each concern appears exactly once.
|
|
249
|
+
*/
|
|
250
|
+
function renderActionItems(vm) {
|
|
661
251
|
const items = [];
|
|
662
252
|
const seen = new Set();
|
|
663
253
|
const pushItem = (item) => {
|
|
@@ -666,18 +256,18 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
666
256
|
seen.add(item.key);
|
|
667
257
|
items.push(item);
|
|
668
258
|
};
|
|
669
|
-
if (invalidRuns.length > 0) {
|
|
259
|
+
if (vm.invalidRuns.length > 0) {
|
|
670
260
|
pushItem({
|
|
671
261
|
key: "invalid-improve-result-rows",
|
|
672
262
|
prio: "P2",
|
|
673
263
|
cls: "warn",
|
|
674
|
-
title: `${invalidRuns.length} invalid improve result row${invalidRuns.length === 1 ? "" : "s"} skipped`,
|
|
675
|
-
descHtml: `Excluded from result-derived metrics: ${invalidRuns.map((run) => `<code>${esc(run.id)}</code>`).join(", ")}.`,
|
|
676
|
-
remedy: `akm health --since=${
|
|
264
|
+
title: `${vm.invalidRuns.length} invalid improve result row${vm.invalidRuns.length === 1 ? "" : "s"} skipped`,
|
|
265
|
+
descHtml: `Excluded from result-derived metrics: ${vm.invalidRuns.map((run) => `<code>${esc(run.id)}</code>`).join(", ")}.`,
|
|
266
|
+
remedy: `akm health --since=${vm.window} --group-by run`,
|
|
677
267
|
});
|
|
678
268
|
}
|
|
679
269
|
// Hard advisories from the health check engine (own remediation in message).
|
|
680
|
-
for (const a of
|
|
270
|
+
for (const a of vm.advisories) {
|
|
681
271
|
if (a.status !== "warn" && a.status !== "fail")
|
|
682
272
|
continue;
|
|
683
273
|
pushItem({
|
|
@@ -689,58 +279,53 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
689
279
|
});
|
|
690
280
|
}
|
|
691
281
|
// Failed runs in window.
|
|
692
|
-
if (failedRuns > 0) {
|
|
282
|
+
if (vm.failedRuns > 0) {
|
|
693
283
|
pushItem({
|
|
694
284
|
key: "failed-runs",
|
|
695
285
|
prio: "P1",
|
|
696
286
|
cls: "fail",
|
|
697
|
-
title: `${failedRuns} failed run${failedRuns === 1 ? "" : "s"} in window`,
|
|
698
|
-
descHtml: `Task fail rate ${esc(taskFailRate)}. Inspect failed runs (ok=false) for early-exit or harness errors.`,
|
|
699
|
-
remedy: `akm health --since=${
|
|
287
|
+
title: `${vm.failedRuns} failed run${vm.failedRuns === 1 ? "" : "s"} in window`,
|
|
288
|
+
descHtml: `Task fail rate ${esc(vm.taskFailRate)}. Inspect failed runs (ok=false) for early-exit or harness errors.`,
|
|
289
|
+
remedy: `akm health --since=${vm.window} --group-by run`,
|
|
700
290
|
});
|
|
701
291
|
}
|
|
702
292
|
// Semantic search blocked.
|
|
703
|
-
if (sem.blocked) {
|
|
293
|
+
if (vm.sem.blocked) {
|
|
704
294
|
pushItem({
|
|
705
295
|
key: "semantic-search-blocked",
|
|
706
296
|
prio: "P2",
|
|
707
297
|
cls: "warn",
|
|
708
298
|
title: "Semantic search blocked",
|
|
709
|
-
descHtml: `Embedding provider unreachable. ${esc(sem.detail)}. Curate falls back to keyword search — relevance scoring degraded.`,
|
|
299
|
+
descHtml: `Embedding provider unreachable. ${esc(vm.sem.detail)}. Curate falls back to keyword search — relevance scoring degraded.`,
|
|
710
300
|
remedy: "akm config show",
|
|
711
301
|
});
|
|
712
302
|
}
|
|
713
303
|
// Pending proposals to drain.
|
|
714
|
-
if (proposals.length > 0) {
|
|
715
|
-
const
|
|
716
|
-
for (const p of proposals)
|
|
717
|
-
bySource.set(p.source, (bySource.get(p.source) ?? 0) + 1);
|
|
718
|
-
const srcSummary = [...bySource.entries()]
|
|
719
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
720
|
-
.map(([source, count]) => `${count} via ${esc(source)}`)
|
|
721
|
-
.join(", ");
|
|
304
|
+
if (vm.proposals.length > 0) {
|
|
305
|
+
const srcSummary = vm.proposalsBySource.map(([source, count]) => `${count} via ${esc(source)}`).join(", ");
|
|
722
306
|
pushItem({
|
|
723
307
|
key: "drain-proposals",
|
|
724
308
|
prio: "P2",
|
|
725
309
|
cls: "warn",
|
|
726
|
-
title: `Drain ${proposals.length} pending proposal${proposals.length === 1 ? "" : "s"}`,
|
|
310
|
+
title: `Drain ${vm.proposals.length} pending proposal${vm.proposals.length === 1 ? "" : "s"}`,
|
|
727
311
|
descHtml: `Proposals generated this batch (${srcSummary}). Review before the queue grows further.`,
|
|
728
312
|
remedy: "akm proposal list",
|
|
729
313
|
});
|
|
730
314
|
}
|
|
731
315
|
// High tail latency.
|
|
732
|
-
if (wallTime.p95Ms && wallTime.medianMs && wallTime.p95Ms / wallTime.medianMs > 2.5) {
|
|
316
|
+
if (vm.wallTime.p95Ms && vm.wallTime.medianMs && vm.wallTime.p95Ms / vm.wallTime.medianMs > 2.5) {
|
|
733
317
|
pushItem({
|
|
734
318
|
key: "tail-latency",
|
|
735
319
|
prio: "P3",
|
|
736
320
|
cls: "warn",
|
|
737
|
-
title: `High tail latency: p95=${fmtMs(wallTime.p95Ms)}, median=${fmtMs(wallTime.medianMs)}`,
|
|
321
|
+
title: `High tail latency: p95=${fmtMs(vm.wallTime.p95Ms)}, median=${fmtMs(vm.wallTime.medianMs)}`,
|
|
738
322
|
descHtml: "P95 is well above median. Consolidation/LLM phase dominates wall time on slow runs. " +
|
|
739
323
|
"Check for slow chunks or LLM rate limiting.",
|
|
740
324
|
});
|
|
741
325
|
}
|
|
742
326
|
// Stale freshness.
|
|
743
|
-
if (isStale && totalRuns > 0) {
|
|
327
|
+
if (vm.isStale && vm.totalRuns > 0) {
|
|
328
|
+
const latestRunHuman = vm.lastRun ? isoTimeTag(vm.lastRun.completedAt || vm.lastRun.startedAt) : "—";
|
|
744
329
|
pushItem({
|
|
745
330
|
key: "stale",
|
|
746
331
|
prio: "P3",
|
|
@@ -750,7 +335,7 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
750
335
|
});
|
|
751
336
|
}
|
|
752
337
|
// WS-5: corpus entrenchment flag.
|
|
753
|
-
if (degradation?.entrenchmentFlagged) {
|
|
338
|
+
if (vm.degradation?.entrenchmentFlagged) {
|
|
754
339
|
pushItem({
|
|
755
340
|
key: "corpus-entrenchment",
|
|
756
341
|
prio: "P2",
|
|
@@ -758,12 +343,12 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
758
343
|
title: "Corpus entrenchment risk: retrieval_salience Gini > 0.35",
|
|
759
344
|
descHtml: "A small set of assets dominates retrieval — retrieval diversity is low. " +
|
|
760
345
|
"Review top-ranked assets for stale or over-represented content. " +
|
|
761
|
-
`Corpus diversity proxy: ${esc(String(degradation.corpusCentroidDistance))}.`,
|
|
346
|
+
`Corpus diversity proxy: ${esc(String(vm.degradation.corpusCentroidDistance))}.`,
|
|
762
347
|
remedy: "akm health --format json | jq '.improve.degradation'",
|
|
763
348
|
});
|
|
764
349
|
}
|
|
765
350
|
// Low-tail companion: salience distribution collapsed toward uniform.
|
|
766
|
-
if (degradation?.salienceUniformityFlagged) {
|
|
351
|
+
if (vm.degradation?.salienceUniformityFlagged) {
|
|
767
352
|
pushItem({
|
|
768
353
|
key: "salience-uniformity-collapse",
|
|
769
354
|
prio: "P2",
|
|
@@ -771,25 +356,25 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
771
356
|
title: "Salience distribution collapsed: retrieval_salience Gini < 0.08",
|
|
772
357
|
descHtml: "The top-100 salience scores are near-uniform (uniform baseline ≈ 0.1) — " +
|
|
773
358
|
"ranking currently carries little to no discrimination between assets. " +
|
|
774
|
-
`Corpus diversity proxy: ${esc(String(degradation.corpusCentroidDistance))}.`,
|
|
359
|
+
`Corpus diversity proxy: ${esc(String(vm.degradation.corpusCentroidDistance))}.`,
|
|
775
360
|
remedy: "akm health --format json | jq '.improve.degradation'",
|
|
776
361
|
});
|
|
777
362
|
}
|
|
778
363
|
// WS-5: over-budget consolidation advisory.
|
|
779
|
-
if (perf.overBudgetRuns > 0) {
|
|
364
|
+
if (vm.perf.overBudgetRuns > 0) {
|
|
780
365
|
pushItem({
|
|
781
366
|
key: "over-budget-consolidation",
|
|
782
367
|
prio: "P2",
|
|
783
368
|
cls: "warn",
|
|
784
|
-
title: `${perf.overBudgetRuns} consolidation run${perf.overBudgetRuns === 1 ? "" : "s"} exceeded budget`,
|
|
369
|
+
title: `${vm.perf.overBudgetRuns} consolidation run${vm.perf.overBudgetRuns === 1 ? "" : "s"} exceeded budget`,
|
|
785
370
|
descHtml: "Consolidation phase wall time exceeded the total run budget on these runs. " +
|
|
786
371
|
"Consider increasing the timeout or reducing the consolidation pool via profile config.",
|
|
787
372
|
remedy: "akm config show",
|
|
788
373
|
});
|
|
789
374
|
}
|
|
790
375
|
// Session-log notes (informational, lowest priority).
|
|
791
|
-
if (
|
|
792
|
-
const patterns =
|
|
376
|
+
if (vm.sessionLogAdvisories.length > 0) {
|
|
377
|
+
const patterns = vm.sessionLogAdvisories
|
|
793
378
|
.slice(0, 6)
|
|
794
379
|
.map((p) => `<li>${esc(p.topic)}</li>`)
|
|
795
380
|
.join("");
|
|
@@ -797,17 +382,18 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
797
382
|
key: "session-log-notes",
|
|
798
383
|
prio: "P3",
|
|
799
384
|
cls: "warn",
|
|
800
|
-
title: `${
|
|
385
|
+
title: `${vm.sessionLogAdvisories.length} session-log note(s) (informational)`,
|
|
801
386
|
descHtml: `<ul style="margin:4px 0 0 16px;padding:0;">${patterns}</ul>`,
|
|
802
387
|
});
|
|
803
388
|
}
|
|
804
389
|
items.sort((a, b) => PRIO_RANK[a.prio] - PRIO_RANK[b.prio]);
|
|
805
|
-
|
|
390
|
+
return items.length > 0
|
|
806
391
|
? items.map(actionItemCard).join("\n")
|
|
807
392
|
: passCard("No action items", "All checks passed and nothing needs attention for this window.");
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
393
|
+
}
|
|
394
|
+
function renderProposalRowsHtml(vm) {
|
|
395
|
+
return vm.proposals.length > 0
|
|
396
|
+
? vm.proposals
|
|
811
397
|
.map((p, i) => {
|
|
812
398
|
const tagCls = p.source === "extract" ? "tag-extract" : "tag-consolidate";
|
|
813
399
|
return (`<tr><td>${i + 1}</td><td><code>${esc(p.ref)}</code></td>` +
|
|
@@ -816,24 +402,35 @@ export function buildHealthHtmlReplacements(result, opts) {
|
|
|
816
402
|
})
|
|
817
403
|
.join("\n")
|
|
818
404
|
: '<tr><td colspan="4" style="text-align:center;color:var(--muted);">No pending proposals</td></tr>';
|
|
405
|
+
}
|
|
406
|
+
// ── Main builder: extract-then-render glue ──────────────────────────────────
|
|
407
|
+
/**
|
|
408
|
+
* Compute all 17 `%%TOKEN%%` replacements for the health HTML template.
|
|
409
|
+
* There is deliberately NO standalone `%%OVERALL_STATUS%%` token — the
|
|
410
|
+
* overall status is embedded in the pre-rendered badge / exec-summary
|
|
411
|
+
* fragments, matching the skill template. Advisories and "what to watch" are
|
|
412
|
+
* merged + de-duplicated into a single prioritized `%%ACTION_ITEMS_HTML%%`.
|
|
413
|
+
*/
|
|
414
|
+
export function buildHealthHtmlReplacements(result, opts) {
|
|
415
|
+
const vm = buildHealthReportViewModel(result, opts);
|
|
819
416
|
return {
|
|
820
|
-
"%%ECHARTS_TAG%%": buildEchartsTag(
|
|
821
|
-
"%%REPORT_TITLE%%": esc(reportTitle),
|
|
822
|
-
"%%WINDOW%%": esc(
|
|
823
|
-
"%%SINCE_HUMAN%%":
|
|
824
|
-
"%%RUN_COUNT%%": num(totalRuns),
|
|
825
|
-
"%%STATUS_BADGE_HTML%%":
|
|
826
|
-
"%%EXEC_SUMMARY_HTML%%":
|
|
827
|
-
"%%KPI_CARDS_HTML%%":
|
|
828
|
-
"%%RUNS_JS_CONST%%":
|
|
829
|
-
"%%DISTILL_REASONS_JSON%%": JSON.stringify(distillReasons),
|
|
830
|
-
"%%SLICE_OPTIONS_HTML%%": buildSliceOptions(
|
|
831
|
-
"%%LLM_BY_STAGE_JSON%%": JSON.stringify(llm.byStage ?? {}),
|
|
832
|
-
"%%SUMMARY_ROWS_HTML%%":
|
|
833
|
-
"%%ACTION_ITEMS_HTML%%":
|
|
834
|
-
"%%PROPOSAL_ROWS_HTML%%":
|
|
835
|
-
"%%PROPOSAL_COUNT%%": String(proposals.length),
|
|
836
|
-
"%%GENERATED_AT%%": esc(generatedAt),
|
|
417
|
+
"%%ECHARTS_TAG%%": buildEchartsTag(),
|
|
418
|
+
"%%REPORT_TITLE%%": esc(vm.reportTitle),
|
|
419
|
+
"%%WINDOW%%": esc(vm.window),
|
|
420
|
+
"%%SINCE_HUMAN%%": renderSinceHuman(vm),
|
|
421
|
+
"%%RUN_COUNT%%": num(vm.totalRuns),
|
|
422
|
+
"%%STATUS_BADGE_HTML%%": renderStatusBadgeHtml(vm),
|
|
423
|
+
"%%EXEC_SUMMARY_HTML%%": renderExecSummary(vm),
|
|
424
|
+
"%%KPI_CARDS_HTML%%": renderKpiCards(vm),
|
|
425
|
+
"%%RUNS_JS_CONST%%": `const RUNS = ${JSON.stringify(vm.runs)};`,
|
|
426
|
+
"%%DISTILL_REASONS_JSON%%": JSON.stringify(vm.distillReasons),
|
|
427
|
+
"%%SLICE_OPTIONS_HTML%%": buildSliceOptions(vm),
|
|
428
|
+
"%%LLM_BY_STAGE_JSON%%": JSON.stringify(vm.llm.byStage ?? {}),
|
|
429
|
+
"%%SUMMARY_ROWS_HTML%%": renderSummaryRowsHtml(vm),
|
|
430
|
+
"%%ACTION_ITEMS_HTML%%": renderActionItems(vm),
|
|
431
|
+
"%%PROPOSAL_ROWS_HTML%%": renderProposalRowsHtml(vm),
|
|
432
|
+
"%%PROPOSAL_COUNT%%": String(vm.proposals.length),
|
|
433
|
+
"%%GENERATED_AT%%": esc(vm.generatedAt),
|
|
837
434
|
"%%AKM_VERSION%%": esc(pkgVersion),
|
|
838
435
|
};
|
|
839
436
|
}
|