akm-cli 0.8.6 → 0.8.14
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 +442 -0
- package/dist/assets/help/help-proposals.md +1 -2
- package/dist/assets/hints/cli-hints-full.md +34 -19
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/profiles/catchup.json +13 -0
- package/dist/assets/profiles/consolidate.json +13 -0
- package/dist/assets/profiles/frequent.json +13 -0
- package/dist/assets/tasks/core/backup.yml +4 -0
- package/dist/assets/tasks/core/extract.yml +4 -0
- package/dist/assets/tasks/core/improve.yml +4 -0
- package/dist/assets/tasks/core/index-refresh.yml +4 -0
- package/dist/assets/tasks/core/sync.yml +4 -0
- package/dist/assets/tasks/core/update-stashes.yml +4 -0
- package/dist/assets/tasks/core/version-check.yml +4 -0
- package/dist/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +560 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
- package/dist/cli/config-migrate.js +6 -6
- package/dist/cli/config-validate.js +4 -4
- package/dist/cli/confirm.js +3 -3
- package/dist/cli/parse-args.js +1 -1
- package/dist/cli/shared.js +72 -19
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +206 -3866
- package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
- package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
- package/dist/commands/agent/contribute-cli.js +200 -0
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +230 -3
- package/dist/commands/db-cli.js +2 -2
- package/dist/commands/env/env-cli.js +529 -0
- package/dist/commands/env/env.js +410 -0
- package/dist/commands/env/secret-cli.js +259 -0
- package/dist/commands/{secret.js → env/secret.js} +6 -47
- package/dist/commands/events.js +4 -4
- package/dist/commands/feedback-cli.js +18 -34
- package/dist/commands/graph/graph-cli.js +132 -0
- package/dist/commands/{graph.js → graph/graph.js} +22 -16
- package/dist/commands/health/checks.js +279 -0
- package/dist/commands/health/html-report.js +448 -0
- package/dist/commands/health.js +189 -266
- package/dist/commands/{consolidate.js → improve/consolidate.js} +63 -38
- package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
- package/dist/commands/{distill.js → improve/distill.js} +39 -18
- package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
- package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
- package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
- package/dist/commands/{extract.js → improve/extract.js} +221 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
- package/dist/commands/{improve.js → improve/improve.js} +672 -292
- package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
- package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
- package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
- package/dist/commands/improve/session-asset.js +248 -0
- package/dist/commands/lint/agent-linter.js +1 -1
- package/dist/commands/lint/base-linter.js +55 -37
- package/dist/commands/lint/command-linter.js +1 -1
- package/dist/commands/lint/default-linter.js +1 -1
- package/dist/commands/lint/env-key-rules.js +1 -1
- package/dist/commands/lint/index.js +19 -25
- package/dist/commands/lint/knowledge-linter.js +1 -1
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +8 -8
- package/dist/commands/lint/skill-linter.js +1 -1
- package/dist/commands/lint/task-linter.js +1 -1
- package/dist/commands/lint/workflow-linter.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/observability-cli.js +244 -0
- package/dist/commands/proposal/drain-policies.js +3 -3
- package/dist/commands/proposal/drain.js +87 -15
- package/dist/commands/proposal/proposal-cli.js +490 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
- package/dist/commands/{propose.js → proposal/propose.js} +11 -11
- package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
- package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
- package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
- package/dist/commands/{curate.js → read/curate.js} +7 -7
- package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
- package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
- package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
- package/dist/commands/read/search-cli.js +207 -0
- package/dist/commands/{search.js → read/search.js} +22 -27
- package/dist/commands/{show.js → read/show.js} +31 -45
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +14 -10
- package/dist/commands/sources/add-cli.js +293 -0
- package/dist/commands/{history.js → sources/history.js} +27 -25
- package/dist/commands/{info.js → sources/info.js} +6 -6
- package/dist/commands/{init.js → sources/init.js} +6 -6
- package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
- package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
- package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
- package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
- package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
- package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
- package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
- package/dist/commands/sources/sources-cli.js +305 -0
- package/dist/commands/sources/stash-cli.js +219 -0
- package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
- package/dist/commands/tasks/default-tasks.js +173 -0
- package/dist/commands/tasks/tasks-cli.js +210 -0
- package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
- package/dist/commands/wiki-cli.js +307 -0
- package/dist/commands/workflow-cli.js +329 -0
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/assert.js +40 -0
- package/dist/core/asset/asset-create.js +54 -0
- package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
- package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
- package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
- package/dist/core/{markdown.js → asset/markdown.js} +1 -1
- package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
- package/dist/core/best-effort.js +64 -0
- package/dist/core/common.js +32 -18
- package/dist/core/{config-io.js → config/config-io.js} +29 -19
- package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
- package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
- package/dist/core/config/config-types.js +16 -0
- package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
- package/dist/core/{config.js → config/config.js} +10 -8
- package/dist/core/env-secret-ref.js +90 -0
- package/dist/core/errors.js +13 -3
- package/dist/core/events.js +27 -4
- package/dist/core/file-lock.js +1 -1
- package/dist/core/improve-types.js +48 -0
- package/dist/core/lesson-lint.js +2 -2
- package/dist/core/logs-db.js +304 -0
- package/dist/core/paths.js +2 -2
- package/dist/core/ripgrep/install.js +2 -2
- package/dist/core/ripgrep/resolve.js +2 -2
- package/dist/core/state-db.js +195 -60
- package/dist/core/text-truncation.js +148 -0
- package/dist/core/time.js +1 -1
- package/dist/core/write-source.js +98 -85
- package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
- package/dist/indexer/{db.js → db/db.js} +128 -118
- package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
- package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
- package/dist/indexer/ensure-index.js +4 -4
- package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
- package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
- package/dist/indexer/indexer.js +37 -30
- package/dist/indexer/init.js +54 -0
- package/dist/indexer/manifest.js +10 -10
- package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
- package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
- package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
- package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
- package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
- package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
- package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
- package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
- package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
- package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
- package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
- package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
- package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
- package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
- package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
- package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
- package/dist/indexer/{walker.js → walk/walker.js} +4 -3
- package/dist/integrations/agent/builder-shared.js +39 -0
- package/dist/integrations/agent/builders.js +14 -81
- package/dist/integrations/agent/config.js +6 -4
- package/dist/integrations/agent/detect.js +1 -1
- package/dist/integrations/agent/index.js +23 -8
- package/dist/integrations/agent/prompts.js +2 -3
- package/dist/integrations/agent/runner.js +22 -3
- package/dist/integrations/agent/spawn.js +9 -10
- package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
- package/dist/integrations/harnesses/claude/config-import.js +70 -0
- package/dist/integrations/harnesses/claude/index.js +64 -0
- package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
- package/dist/integrations/harnesses/index.js +144 -0
- package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
- package/dist/integrations/harnesses/opencode/config-import.js +82 -0
- package/dist/integrations/harnesses/opencode/index.js +59 -0
- package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
- package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
- package/dist/integrations/harnesses/types.js +43 -0
- package/dist/integrations/lockfile.js +7 -16
- package/dist/integrations/session-logs/index.js +82 -9
- package/dist/llm/call-ai.js +4 -4
- package/dist/llm/client.js +146 -6
- package/dist/llm/embedder.js +6 -6
- package/dist/llm/embedders/local.js +9 -22
- package/dist/llm/embedders/remote.js +2 -2
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +31 -12
- package/dist/llm/index-passes.js +1 -1
- package/dist/llm/memory-infer.js +12 -5
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +9 -46
- package/dist/output/html-render.js +73 -0
- package/dist/output/renderers.js +88 -58
- package/dist/output/shapes/curate.js +7 -3
- package/dist/output/shapes/distill.js +7 -3
- package/dist/output/shapes/env-list.js +18 -16
- package/dist/output/shapes/events.js +5 -4
- package/dist/output/shapes/helpers.js +19 -5
- package/dist/output/shapes/history.js +7 -3
- package/dist/output/shapes/passthrough.js +8 -11
- package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
- package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
- package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
- package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
- package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
- package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
- package/dist/output/shapes/registry-search.js +7 -3
- package/dist/output/shapes/registry.js +12 -0
- package/dist/output/shapes/search.js +7 -3
- package/dist/output/shapes/secret-list.js +18 -16
- package/dist/output/shapes/show.js +7 -3
- package/dist/output/shapes.js +55 -30
- package/dist/output/text/add.js +2 -3
- package/dist/output/text/clone.js +2 -3
- package/dist/output/text/config.js +2 -3
- package/dist/output/text/curate.js +4 -3
- package/dist/output/text/distill.js +2 -3
- package/dist/output/text/enable-disable.js +5 -4
- package/dist/output/text/env.js +13 -0
- package/dist/output/text/events.js +5 -4
- package/dist/output/text/feedback.js +4 -3
- package/dist/output/text/helpers.js +123 -40
- package/dist/output/text/history.js +2 -3
- package/dist/output/text/import.js +2 -3
- package/dist/output/text/index.js +2 -3
- package/dist/output/text/info.js +2 -3
- package/dist/output/text/init.js +2 -3
- package/dist/output/text/list.js +2 -3
- package/dist/output/text/proposal/producer.js +9 -0
- package/dist/output/text/proposal/proposal.js +13 -0
- package/dist/output/text/registry-commands.js +8 -7
- package/dist/output/text/registry.js +12 -0
- package/dist/output/text/remember.js +4 -3
- package/dist/output/text/remove.js +2 -3
- package/dist/output/text/save.js +2 -3
- package/dist/output/text/search.js +4 -3
- package/dist/output/text/show.js +4 -3
- package/dist/output/text/update.js +2 -3
- package/dist/output/text/upgrade.js +2 -3
- package/dist/output/text/wiki.js +12 -11
- package/dist/output/text/workflow.js +12 -10
- package/dist/output/text.js +66 -32
- package/dist/registry/build-index.js +11 -10
- package/dist/registry/factory.js +1 -1
- package/dist/registry/origin-resolve.js +1 -1
- package/dist/registry/providers/index.js +2 -2
- package/dist/registry/providers/skills-sh.js +91 -72
- package/dist/registry/providers/static-index.js +75 -52
- package/dist/registry/resolve.js +3 -3
- package/dist/runtime.js +242 -0
- package/dist/scripts/migrate-storage.js +1654 -683
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
- package/dist/setup/detect.js +311 -9
- package/dist/setup/harness-config-import.js +6 -120
- package/dist/setup/setup.js +454 -43
- package/dist/sources/include.js +1 -1
- package/dist/sources/provider-factory.js +2 -2
- package/dist/sources/providers/filesystem.js +3 -3
- package/dist/sources/providers/git.js +9 -9
- package/dist/sources/providers/index.js +4 -4
- package/dist/sources/providers/npm.js +6 -6
- package/dist/sources/providers/provider-utils.js +13 -20
- package/dist/sources/providers/sync-from-ref.js +5 -5
- package/dist/sources/providers/tar-utils.js +2 -2
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/website-ingest.js +5 -5
- package/dist/storage/database.js +102 -0
- package/dist/storage/engines/sqlite-migrations.js +42 -0
- package/dist/storage/locations.js +25 -0
- package/dist/storage/repositories/index-db.js +43 -0
- package/dist/storage/repositories/workflow-runs-repository.js +141 -0
- package/dist/tasks/backends/cron.js +4 -4
- package/dist/tasks/backends/exec-utils.js +32 -0
- package/dist/tasks/backends/index.js +3 -3
- package/dist/tasks/backends/launchd.js +7 -14
- package/dist/tasks/backends/schtasks.js +7 -16
- package/dist/tasks/embedded.js +71 -0
- package/dist/tasks/parser.js +2 -2
- package/dist/tasks/resolveAkmBin.js +1 -1
- package/dist/tasks/runner.js +127 -31
- package/dist/tasks/schedule.js +1 -1
- package/dist/tasks/validator.js +7 -7
- package/dist/text-import-hook.mjs +51 -0
- package/dist/version.js +2 -1
- package/dist/wiki/wiki.js +7 -7
- package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
- package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
- package/dist/workflows/cli.js +1 -1
- package/dist/workflows/db.js +54 -32
- package/dist/workflows/parser.js +4 -4
- package/dist/workflows/renderer.js +5 -5
- package/dist/workflows/runtime/agent-identity.js +56 -0
- package/dist/workflows/runtime/checkin.js +57 -0
- package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
- package/dist/workflows/validate-summary.js +82 -0
- package/docs/README.md +1 -1
- package/docs/data-and-telemetry.md +6 -6
- package/package.json +17 -8
- package/dist/commands/add-cli.js +0 -279
- package/dist/commands/env.js +0 -213
- package/dist/integrations/agent/sdk-runner.js +0 -126
- package/dist/output/shapes/vault-list.js +0 -19
- package/dist/output/text/proposal-producer.js +0 -8
- package/dist/output/text/proposal.js +0 -12
- package/dist/output/text/vault.js +0 -16
- /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
- /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
- /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
- /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
- /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
- /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
- /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
- /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* `akm health --format html` — token builder for the full HTML health report
|
|
6
|
+
* (#582). Ports the external akm-health-report skill's collect.py + render.py
|
|
7
|
+
* to TypeScript so the report is generated in-process (no python, no
|
|
8
|
+
* shell-out). The template (`src/assets/templates/html/health.html`) is a
|
|
9
|
+
* verbatim copy of the skill's report.html; this module computes the 17
|
|
10
|
+
* `%%TOKEN%%` replacements it consumes.
|
|
11
|
+
*
|
|
12
|
+
* Determinism: nothing here depends on Date.now()/Math.random(). Runs are
|
|
13
|
+
* sorted by startedAt; `%%GENERATED_AT%%` derives from the latest run (or the
|
|
14
|
+
* window anchor), so output is byte-identical for identical inputs.
|
|
15
|
+
*/
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { escapeHtml } from "../../output/html-render.js";
|
|
19
|
+
import { getDirname } from "../../runtime.js";
|
|
20
|
+
const ECHARTS_CDN = "https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js";
|
|
21
|
+
const ECHARTS_VENDOR_PATH = path.join(getDirname(import.meta.url), "../../assets/templates/html/vendor/echarts.min.js");
|
|
22
|
+
// ── Small formatters (ports of render.py helpers) ───────────────────────────
|
|
23
|
+
const esc = escapeHtml;
|
|
24
|
+
function num(value) {
|
|
25
|
+
return Math.round(value).toLocaleString("en-US");
|
|
26
|
+
}
|
|
27
|
+
function fmtMs(ms) {
|
|
28
|
+
return ms ? `${(ms / 60000).toFixed(1)}m` : "—";
|
|
29
|
+
}
|
|
30
|
+
function pct(rate, digits) {
|
|
31
|
+
return `${(rate * 100).toFixed(digits)}%`;
|
|
32
|
+
}
|
|
33
|
+
function trendClass(direction) {
|
|
34
|
+
return direction === "up" ? "trend-up" : direction === "down" ? "trend-down" : "trend-flat";
|
|
35
|
+
}
|
|
36
|
+
function trendLabel(direction) {
|
|
37
|
+
return direction === "up" ? "▲ up" : direction === "down" ? "▼ watch" : "— flat";
|
|
38
|
+
}
|
|
39
|
+
/** pctChange may be a number or an "+inf"/"-inf" sentinel (prior window was 0). */
|
|
40
|
+
function coercePct(raw) {
|
|
41
|
+
if (typeof raw === "number")
|
|
42
|
+
return raw;
|
|
43
|
+
if (typeof raw !== "string")
|
|
44
|
+
return undefined;
|
|
45
|
+
const s = raw.trim().toLowerCase();
|
|
46
|
+
if (s === "+inf" || s === "inf" || s === "infinity" || s === "+infinity")
|
|
47
|
+
return 1e9;
|
|
48
|
+
if (s === "-inf" || s === "-infinity")
|
|
49
|
+
return -1e9;
|
|
50
|
+
const parsed = Number.parseFloat(s.replace(/%$/, ""));
|
|
51
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
52
|
+
}
|
|
53
|
+
function reshapeRun(r) {
|
|
54
|
+
const cons = r.consolidation;
|
|
55
|
+
const mi = r.memoryInference;
|
|
56
|
+
const ge = r.graphExtraction;
|
|
57
|
+
const wall = r.wallTimeMs || 0;
|
|
58
|
+
const consMs = cons.durationMs || 0;
|
|
59
|
+
const miMs = mi.durationMs || 0;
|
|
60
|
+
const geMs = ge.durationMs || 0;
|
|
61
|
+
return {
|
|
62
|
+
id: r.id,
|
|
63
|
+
startedAt: r.startedAt,
|
|
64
|
+
completedAt: r.completedAt,
|
|
65
|
+
wallTimeMs: wall,
|
|
66
|
+
ok: r.ok,
|
|
67
|
+
mode: r.scope.mode,
|
|
68
|
+
consDurationMs: consMs,
|
|
69
|
+
miDurationMs: miMs,
|
|
70
|
+
geDurationMs: geMs,
|
|
71
|
+
otherMs: Math.max(0, wall - consMs - miMs - geMs),
|
|
72
|
+
consRan: cons.ran,
|
|
73
|
+
promoted: cons.promoted,
|
|
74
|
+
merged: cons.merged,
|
|
75
|
+
deleted: cons.deleted,
|
|
76
|
+
contradicted: cons.contradicted,
|
|
77
|
+
judgedNoAction: cons.judgedNoAction,
|
|
78
|
+
processed: cons.processed,
|
|
79
|
+
failedChunks: cons.failedChunks,
|
|
80
|
+
totalChunks: cons.totalChunks,
|
|
81
|
+
miWritten: mi.written || mi.writes || 0,
|
|
82
|
+
miConsidered: mi.considered,
|
|
83
|
+
miYieldRate: mi.yieldRate,
|
|
84
|
+
miCacheHits: mi.cacheHits,
|
|
85
|
+
geEntities: ge.entities,
|
|
86
|
+
geRelations: ge.relations,
|
|
87
|
+
geCacheHitRate: ge.cacheHitRate,
|
|
88
|
+
geFailures: ge.failures,
|
|
89
|
+
distillSkipped: r.actions.distill.skipped,
|
|
90
|
+
distillQueued: r.actions.distill.queued,
|
|
91
|
+
distillLlmFailed: r.actions.distill.llmFailed,
|
|
92
|
+
distillByReason: r.actions.distill.skippedByReason,
|
|
93
|
+
reflectOk: r.actions.reflect.ok,
|
|
94
|
+
reflectFailed: r.actions.reflect.failed,
|
|
95
|
+
derived: r.memorySummary.derived,
|
|
96
|
+
eligible: r.memorySummary.eligible,
|
|
97
|
+
lintFlagged: r.lintFlagged,
|
|
98
|
+
lintFixed: r.lintFixed,
|
|
99
|
+
reflectsWithErrorContext: r.reflectsWithErrorContext,
|
|
100
|
+
orphansPurged: r.orphansPurged,
|
|
101
|
+
evalCasesWritten: r.evalCasesWritten,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// ── Trend classification (port of collect.py classify) ──────────────────────
|
|
105
|
+
function classify(deltas, metricKeys, lowerIsBetter = false) {
|
|
106
|
+
const changes = metricKeys
|
|
107
|
+
.map((key) => coercePct(deltas[key]?.pctChange))
|
|
108
|
+
.filter((c) => c !== undefined);
|
|
109
|
+
if (changes.length === 0)
|
|
110
|
+
return "flat";
|
|
111
|
+
const avg = changes.reduce((acc, c) => acc + c, 0) / changes.length;
|
|
112
|
+
if (Math.abs(avg) < 5)
|
|
113
|
+
return "flat";
|
|
114
|
+
const direction = avg > 0 ? "up" : "down";
|
|
115
|
+
if (lowerIsBetter)
|
|
116
|
+
return avg > 0 ? "down" : "up";
|
|
117
|
+
return direction;
|
|
118
|
+
}
|
|
119
|
+
function buildTrend(deltas) {
|
|
120
|
+
const decisionQuality = classify(deltas, ["improve.memoryInference.yieldRate", "improve.consolidation.promoted"]);
|
|
121
|
+
const outputVolume = classify(deltas, [
|
|
122
|
+
"improve.consolidation.promoted",
|
|
123
|
+
"improve.memoryInference.written",
|
|
124
|
+
"improve.sessionExtraction.proposalsCreated",
|
|
125
|
+
]);
|
|
126
|
+
const failures = classify(deltas, ["improve.graphExtraction.failures"], true);
|
|
127
|
+
const latency = classify(deltas, ["improve.wallTime.medianMs", "improve.wallTime.p95Ms"], true);
|
|
128
|
+
const score = [decisionQuality, outputVolume, failures, latency].reduce((acc, d) => acc + (d === "up" ? 1 : d === "down" ? -1 : 0), 0);
|
|
129
|
+
const overall = score >= 1 ? "improving" : score <= -1 ? "degrading" : "mixed";
|
|
130
|
+
return { decisionQuality, outputVolume, failures, latency, overall };
|
|
131
|
+
}
|
|
132
|
+
function deltaPill(deltas, key, lowerIsBetter = false) {
|
|
133
|
+
const raw = deltas[key]?.pctChange;
|
|
134
|
+
if (raw === undefined)
|
|
135
|
+
return '<span class="trend-pill flat">— n/a</span>';
|
|
136
|
+
if (typeof raw === "string") {
|
|
137
|
+
const sign = raw.trim().startsWith("-") ? -1 : 1;
|
|
138
|
+
const good = lowerIsBetter ? sign < 0 : sign > 0;
|
|
139
|
+
return `<span class="trend-pill ${good ? "up" : "down"}">${sign > 0 ? "▲ new" : "▼ gone"}</span>`;
|
|
140
|
+
}
|
|
141
|
+
const good = lowerIsBetter ? raw < 0 : raw > 0;
|
|
142
|
+
const cls = Math.abs(raw) < 1 ? "flat" : good ? "up" : "down";
|
|
143
|
+
const arrow = raw > 0 ? "▲" : raw < 0 ? "▼" : "—";
|
|
144
|
+
const signed = `${raw > 0 ? "+" : ""}${Math.round(raw)}%`;
|
|
145
|
+
return `<span class="trend-pill ${cls}">${arrow} ${signed}</span>`;
|
|
146
|
+
}
|
|
147
|
+
// ── Advisory / watch-item cards ──────────────────────────────────────────────
|
|
148
|
+
function advisoryCard(cls, icon, title, descHtml) {
|
|
149
|
+
return (`<div class="advisory ${cls}"><div class="advisory-icon">${icon}</div>` +
|
|
150
|
+
`<div class="advisory-body"><div class="title">${title}</div>` +
|
|
151
|
+
`<div class="desc">${descHtml}</div></div></div>`);
|
|
152
|
+
}
|
|
153
|
+
function passCard(title, desc) {
|
|
154
|
+
return ('<div class="advisory" style="border-left:3px solid var(--green);">' +
|
|
155
|
+
'<div class="advisory-icon">✅</div><div class="advisory-body">' +
|
|
156
|
+
`<div class="title">${esc(title)}</div>` +
|
|
157
|
+
`<div class="desc">${esc(desc)}</div></div></div>`);
|
|
158
|
+
}
|
|
159
|
+
function readSemSearch(advisories) {
|
|
160
|
+
const check = advisories.find((a) => a.name === "semantic-search-runtime");
|
|
161
|
+
if (!check)
|
|
162
|
+
return { blocked: false, detail: "" };
|
|
163
|
+
const evidence = check.evidence ?? {};
|
|
164
|
+
const status = String(evidence.status ?? "unknown");
|
|
165
|
+
const blocked = check.status !== "pass" || status.toLowerCase().includes("block");
|
|
166
|
+
const entries = typeof evidence.entryCount === "number" ? evidence.entryCount : 0;
|
|
167
|
+
const embeddings = typeof evidence.embeddingCount === "number" ? evidence.embeddingCount : 0;
|
|
168
|
+
return { blocked, detail: `${num(entries)} entries, ${num(embeddings)} embeddings` };
|
|
169
|
+
}
|
|
170
|
+
// ── ECharts delivery ─────────────────────────────────────────────────────────
|
|
171
|
+
function buildEchartsTag(opts) {
|
|
172
|
+
const mode = opts.echarts ?? (process.env.AKM_ECHARTS === "cdn" ? "cdn" : "inline");
|
|
173
|
+
if (mode === "cdn")
|
|
174
|
+
return `<script src="${ECHARTS_CDN}"></script>`;
|
|
175
|
+
const libPath = opts.echartsLibPath ?? ECHARTS_VENDOR_PATH;
|
|
176
|
+
// Guard against an accidental </script> in the minified payload.
|
|
177
|
+
const lib = fs.readFileSync(libPath, "utf8").replaceAll("</script>", "<\\/script>");
|
|
178
|
+
return `<script>\n${lib}\n</script>`;
|
|
179
|
+
}
|
|
180
|
+
// ── Main builder ─────────────────────────────────────────────────────────────
|
|
181
|
+
/**
|
|
182
|
+
* Compute all 17 `%%TOKEN%%` replacements for the health HTML template.
|
|
183
|
+
* There is deliberately NO standalone `%%OVERALL_STATUS%%` token — the
|
|
184
|
+
* overall status is embedded in the pre-rendered badge / exec-summary
|
|
185
|
+
* fragments, matching the skill template.
|
|
186
|
+
*/
|
|
187
|
+
export function buildHealthHtmlReplacements(result, opts) {
|
|
188
|
+
const deltas = opts.deltas ?? {};
|
|
189
|
+
const runs = (result.runs ?? []).map(reshapeRun).sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
190
|
+
const improve = result.improve;
|
|
191
|
+
const proposals = opts.proposals;
|
|
192
|
+
const sem = readSemSearch(result.advisories);
|
|
193
|
+
const trend = buildTrend(deltas);
|
|
194
|
+
// ── Aggregates (collect.py step 6) ─────────────────────────────────────────
|
|
195
|
+
const cons = improve.consolidation;
|
|
196
|
+
const mi = improve.memoryInference;
|
|
197
|
+
const ge = improve.graphExtraction;
|
|
198
|
+
const wallTime = improve.wallTime;
|
|
199
|
+
const totalRuns = runs.length;
|
|
200
|
+
const failedRuns = runs.filter((r) => !r.ok).length;
|
|
201
|
+
const invoked = improve.invoked || totalRuns;
|
|
202
|
+
const completed = improve.completed || totalRuns - failedRuns;
|
|
203
|
+
const miWritten = mi.written || mi.writes || 0;
|
|
204
|
+
const completionRate = invoked ? `${Math.round((100 * completed) / invoked)}%` : "0%";
|
|
205
|
+
const taskFailRate = pct(result.metrics.taskFailRate, 1);
|
|
206
|
+
const agentFailRate = pct(result.metrics.agentFailureRate, 2);
|
|
207
|
+
const miYieldRate = pct(mi.yieldRate, 1);
|
|
208
|
+
const medianDurMin = (wallTime.medianMs / 60000).toFixed(1);
|
|
209
|
+
const p95DurMin = (wallTime.p95Ms / 60000).toFixed(1);
|
|
210
|
+
const avgPromoted = totalRuns ? String(Math.round(cons.promoted / totalRuns)) : "0";
|
|
211
|
+
const chunkFail = `${num(cons.failedChunks)} / ${num(cons.totalChunks)}`;
|
|
212
|
+
// ── Meta (collect.py steps 10-11) ──────────────────────────────────────────
|
|
213
|
+
const sinceIso = result.since;
|
|
214
|
+
const reportDate = sinceIso.slice(0, 10);
|
|
215
|
+
const sinceHuman = sinceIso ? `${sinceIso.slice(0, 16).replace("T", " ")} UTC → now` : `last ${opts.window}`;
|
|
216
|
+
const reportTitle = reportDate ? `AKM Health Report — ${reportDate}` : "AKM Health Report";
|
|
217
|
+
const lastRun = runs[runs.length - 1];
|
|
218
|
+
const generatedAt = lastRun ? lastRun.completedAt || lastRun.startedAt || sinceIso : sinceIso;
|
|
219
|
+
const latest = [...runs].reverse().find((r) => r.ok) ?? lastRun;
|
|
220
|
+
// ── Status badges ──────────────────────────────────────────────────────────
|
|
221
|
+
const badgeByStatus = {
|
|
222
|
+
pass: { badge: "badge-pass", dot: "dot-pass", label: "PASS" },
|
|
223
|
+
warn: { badge: "badge-warn", dot: "dot-warn", label: "WARN" },
|
|
224
|
+
fail: { badge: "badge-fail", dot: "dot-fail", label: "FAIL" },
|
|
225
|
+
};
|
|
226
|
+
const badge = badgeByStatus[result.status];
|
|
227
|
+
const statusBadge = `<span class="badge-pill ${badge.badge}"><span class="dot ${badge.dot}"></span>${badge.label}</span>`;
|
|
228
|
+
const failOk = result.metrics.taskFailRate < 0.05;
|
|
229
|
+
const failBadge = `<span class="badge-pill ${failOk ? "badge-pass" : "badge-warn"}">` +
|
|
230
|
+
`<span class="dot ${failOk ? "dot-pass" : "dot-warn"}"></span>${taskFailRate} Fail Rate</span>`;
|
|
231
|
+
// ── Executive summary ──────────────────────────────────────────────────────
|
|
232
|
+
const li = (k, vHtml) => `<li><span class="k">${esc(k)}</span><span class="v">${vHtml}</span></li>`;
|
|
233
|
+
const trendLi = (k, d) => li(k, `<span class="trend-pill ${d === "flat" ? "flat" : d}">${d}</span>`);
|
|
234
|
+
const quickNumbers = [
|
|
235
|
+
li("Task fail rate", taskFailRate),
|
|
236
|
+
li("Agent fail rate", agentFailRate),
|
|
237
|
+
li("Improve completion", `${num(completed)} / ${num(invoked)} (${completionRate})`),
|
|
238
|
+
li("MI yield rate", miYieldRate),
|
|
239
|
+
li("MI written", num(miWritten)),
|
|
240
|
+
li("Consolidation promoted", num(cons.promoted)),
|
|
241
|
+
li("Consolidation judgedNoAction", num(cons.judgedNoAction)),
|
|
242
|
+
li("Chunk failure", chunkFail),
|
|
243
|
+
li("Median wall time", fmtMs(wallTime.medianMs)),
|
|
244
|
+
li("P95 wall time", fmtMs(wallTime.p95Ms)),
|
|
245
|
+
].join("");
|
|
246
|
+
const trendRows = [
|
|
247
|
+
trendLi("Decision quality", trend.decisionQuality),
|
|
248
|
+
trendLi("Output volume", trend.outputVolume),
|
|
249
|
+
trendLi("Failures", trend.failures),
|
|
250
|
+
trendLi("Latency", trend.latency),
|
|
251
|
+
].join("");
|
|
252
|
+
const deltaRows = [
|
|
253
|
+
li("Promoted", deltaPill(deltas, "improve.consolidation.promoted")),
|
|
254
|
+
li("MI written", deltaPill(deltas, "improve.memoryInference.written")),
|
|
255
|
+
li("MI yield", deltaPill(deltas, "improve.memoryInference.yieldRate")),
|
|
256
|
+
li("Median wall", deltaPill(deltas, "improve.wallTime.medianMs", true)),
|
|
257
|
+
li("P95 wall", deltaPill(deltas, "improve.wallTime.p95Ms", true)),
|
|
258
|
+
].join("");
|
|
259
|
+
const snapRows = latest
|
|
260
|
+
? [
|
|
261
|
+
li("Run id", `<code>${esc(latest.id.slice(0, 28))}</code>`),
|
|
262
|
+
li("Completed", `${esc((latest.completedAt || latest.startedAt).slice(0, 16).replace("T", " "))} UTC`),
|
|
263
|
+
li("Status", latest.ok ? "✅ ok" : "❌ failed"),
|
|
264
|
+
li("Wall time", fmtMs(latest.wallTimeMs)),
|
|
265
|
+
li("Reflect ok/fail", `${latest.reflectOk} / ${latest.reflectFailed}`),
|
|
266
|
+
li("Promoted", String(latest.promoted)),
|
|
267
|
+
li("judgedNoAction", String(latest.judgedNoAction)),
|
|
268
|
+
li("MI written", String(latest.miWritten)),
|
|
269
|
+
li("Graph entities/relations", `${latest.geEntities} / ${latest.geRelations}`),
|
|
270
|
+
].join("")
|
|
271
|
+
: '<li><span class="k">No runs in window</span><span class="v">—</span></li>';
|
|
272
|
+
const windowRows = [
|
|
273
|
+
li("Report window", esc(opts.window)),
|
|
274
|
+
li("Compare window", esc(opts.compare)),
|
|
275
|
+
li("Runs", `${num(totalRuns)} (${failedRuns} failed)`),
|
|
276
|
+
li("Stash derived", num(improve.memorySummary.derived)),
|
|
277
|
+
li("Stash eligible", num(improve.memorySummary.eligible)),
|
|
278
|
+
li("Pending proposals", String(proposals.length)),
|
|
279
|
+
li("Semantic search", sem.blocked ? "BLOCKED" : "OK"),
|
|
280
|
+
].join("");
|
|
281
|
+
const overallEmoji = trend.overall === "improving" ? "📈" : trend.overall === "degrading" ? "📉" : "↔️";
|
|
282
|
+
const execSummary = `
|
|
283
|
+
<h2>${overallEmoji} Executive Summary
|
|
284
|
+
<span class="badge-pill ${badge.badge}" style="font-size:11px;">
|
|
285
|
+
<span class="dot ${badge.dot}"></span>${badge.label}</span></h2>
|
|
286
|
+
<div class="exec-grid">
|
|
287
|
+
<div>
|
|
288
|
+
<h4>Quick Numbers</h4>
|
|
289
|
+
<ul>${quickNumbers}</ul>
|
|
290
|
+
</div>
|
|
291
|
+
<div>
|
|
292
|
+
<h4>Trend vs prior ${esc(opts.compare)}</h4>
|
|
293
|
+
<ul>${trendRows}</ul>
|
|
294
|
+
<h4 style="margin-top:14px;">Period-over-period deltas</h4>
|
|
295
|
+
<ul>${deltaRows}</ul>
|
|
296
|
+
</div>
|
|
297
|
+
<div>
|
|
298
|
+
<h4>Current Run Snapshot</h4>
|
|
299
|
+
<ul>${snapRows}</ul>
|
|
300
|
+
</div>
|
|
301
|
+
<div>
|
|
302
|
+
<h4>Window</h4>
|
|
303
|
+
<ul>${windowRows}</ul>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="overall">Overall trend: <b>${esc(trend.overall)}</b> ${overallEmoji}
|
|
307
|
+
· based on decision quality, output volume, failures, and latency vs the prior window.</div>`.trim();
|
|
308
|
+
// ── KPI cards ──────────────────────────────────────────────────────────────
|
|
309
|
+
const semValue = sem.blocked ? "BLOCKED" : "OK";
|
|
310
|
+
const semColor = sem.blocked ? "yellow" : "green";
|
|
311
|
+
const semStyle = sem.blocked ? "font-size:18px;" : "";
|
|
312
|
+
const kpiCard = (color, label, value, sub, valueStyle = "") => `<div class="kpi-card ${color}">
|
|
313
|
+
<div class="label">${label}</div>
|
|
314
|
+
<div class="value"${valueStyle ? ` style="${valueStyle}"` : ""}>${value}</div>
|
|
315
|
+
<div class="sub">${sub}</div>
|
|
316
|
+
</div>`;
|
|
317
|
+
const kpiCards = [
|
|
318
|
+
kpiCard(failedRuns === 0 ? "green" : "yellow", "Completion Rate", completionRate, `${num(completed)} / ${num(invoked)} invoked`),
|
|
319
|
+
kpiCard(failedRuns === 0 ? "green" : "red", "Failed Runs", String(failedRuns), `of ${num(totalRuns)} runs · ${taskFailRate} task fail`),
|
|
320
|
+
kpiCard("blue", "Total Promoted", num(cons.promoted), `avg ${avgPromoted} / run`),
|
|
321
|
+
kpiCard("blue", "MI Written", num(miWritten), `${miYieldRate} yield rate`),
|
|
322
|
+
kpiCard("purple", "Graph Entities", num(ge.entities), `+${num(ge.relations)} relations`),
|
|
323
|
+
kpiCard("green", "Stash Derived", num(improve.memorySummary.derived), `of ${num(improve.memorySummary.eligible)} eligible`),
|
|
324
|
+
kpiCard("yellow", "Median Duration", `${medianDurMin}m`, `p95 = ${p95DurMin}m`),
|
|
325
|
+
kpiCard(semColor, "Semantic Search", semValue, esc(sem.detail), semStyle),
|
|
326
|
+
kpiCard("yellow", "Pending Proposals", String(proposals.length), `from ${esc(opts.window)} batch`),
|
|
327
|
+
].join("\n");
|
|
328
|
+
// ── Chart payload ──────────────────────────────────────────────────────────
|
|
329
|
+
const distillReasons = [...new Set(runs.flatMap((r) => Object.keys(r.distillByReason)))].sort();
|
|
330
|
+
const runsJsConst = `const RUNS = ${JSON.stringify(runs)};`;
|
|
331
|
+
// ── Summary table rows ─────────────────────────────────────────────────────
|
|
332
|
+
const summaryRows = [
|
|
333
|
+
["Task fail rate", taskFailRate, "flat"],
|
|
334
|
+
["Agent fail rate", agentFailRate, "flat"],
|
|
335
|
+
["Improve completion", `${num(completed)} / ${num(invoked)}`, "flat"],
|
|
336
|
+
["MI yield rate", miYieldRate, trend.decisionQuality],
|
|
337
|
+
["MI written", num(miWritten), trend.outputVolume],
|
|
338
|
+
["Consolidation promoted", num(cons.promoted), trend.outputVolume],
|
|
339
|
+
["Consolidation merged", num(cons.merged), "flat"],
|
|
340
|
+
["Consolidation deleted", num(cons.deleted), "flat"],
|
|
341
|
+
["Consolidation contradicted", num(cons.contradicted), "flat"],
|
|
342
|
+
["Consolidation judgedNoAction", num(cons.judgedNoAction), "flat"],
|
|
343
|
+
["Chunk failure", chunkFail, "flat"],
|
|
344
|
+
["Graph entities", num(ge.entities), "up"],
|
|
345
|
+
["Graph relations", num(ge.relations), "up"],
|
|
346
|
+
["Stash derived", num(improve.memorySummary.derived), "up"],
|
|
347
|
+
["Median wall time", fmtMs(wallTime.medianMs), trend.latency],
|
|
348
|
+
["P95 wall time", fmtMs(wallTime.p95Ms), trend.latency],
|
|
349
|
+
];
|
|
350
|
+
const summaryRowsHtml = summaryRows
|
|
351
|
+
.map(([label, value, t]) => ` <tr><td>${esc(label)}</td><td>${esc(value)}</td>` +
|
|
352
|
+
`<td class="trend ${trendClass(t)}">${trendLabel(t)}</td></tr>`)
|
|
353
|
+
.join("\n");
|
|
354
|
+
// ── Advisory cards ─────────────────────────────────────────────────────────
|
|
355
|
+
const advisoryParts = [];
|
|
356
|
+
for (const a of result.advisories) {
|
|
357
|
+
if (a.status !== "warn" && a.status !== "fail")
|
|
358
|
+
continue;
|
|
359
|
+
advisoryParts.push(advisoryCard(a.status === "fail" ? "fail" : "warn", a.status === "fail" ? "🔴" : "⚠️", esc(a.name), esc(a.message)));
|
|
360
|
+
}
|
|
361
|
+
if (sem.blocked) {
|
|
362
|
+
advisoryParts.push(advisoryCard("warn", "⚠️", "Semantic search blocked", `Embedding provider unreachable. ${esc(sem.detail)}. Curate falls back to keyword search — relevance scoring degraded.`));
|
|
363
|
+
}
|
|
364
|
+
if (proposals.length > 0) {
|
|
365
|
+
advisoryParts.push(advisoryCard("warn", "⚠️", `${proposals.length} proposals pending (drain needed)`, "Run <code>akm proposal list</code> to review and drain."));
|
|
366
|
+
}
|
|
367
|
+
if (result.sessionLogAdvisories.length > 0) {
|
|
368
|
+
const patterns = result.sessionLogAdvisories
|
|
369
|
+
.slice(0, 6)
|
|
370
|
+
.map((p) => `<li>${esc(p.topic)}</li>`)
|
|
371
|
+
.join("");
|
|
372
|
+
advisoryParts.push('<div class="advisory" style="border-left:3px solid var(--accent);">' +
|
|
373
|
+
'<div class="advisory-icon">ℹ️</div><div class="advisory-body">' +
|
|
374
|
+
`<div class="title">${result.sessionLogAdvisories.length} session-log note(s) (informational)</div>` +
|
|
375
|
+
`<div class="desc"><ul style="margin:4px 0 0 16px;padding:0;">${patterns}</ul></div></div></div>`);
|
|
376
|
+
}
|
|
377
|
+
const advisoryCardsHtml = advisoryParts.length > 0
|
|
378
|
+
? advisoryParts.join("\n")
|
|
379
|
+
: passCard("No active advisories", "All checks passed for this window.");
|
|
380
|
+
// ── Proposal rows ──────────────────────────────────────────────────────────
|
|
381
|
+
const proposalRowsHtml = proposals.length > 0
|
|
382
|
+
? proposals
|
|
383
|
+
.map((p, i) => {
|
|
384
|
+
const tagCls = p.source === "extract" ? "tag-extract" : "tag-consolidate";
|
|
385
|
+
const ts = p.createdAt.slice(0, 16).replace("T", " ");
|
|
386
|
+
return (`<tr><td>${i + 1}</td><td><code>${esc(p.ref)}</code></td>` +
|
|
387
|
+
`<td><span class="tag ${tagCls}">${esc(p.source)}</span></td>` +
|
|
388
|
+
`<td>${esc(ts)}</td></tr>`);
|
|
389
|
+
})
|
|
390
|
+
.join("\n")
|
|
391
|
+
: '<tr><td colspan="4" style="text-align:center;color:var(--muted);">No pending proposals</td></tr>';
|
|
392
|
+
// ── What to watch ──────────────────────────────────────────────────────────
|
|
393
|
+
const watchParts = [];
|
|
394
|
+
for (const a of result.advisories) {
|
|
395
|
+
if (a.status !== "warn" && a.status !== "fail")
|
|
396
|
+
continue;
|
|
397
|
+
const prio = a.status === "fail" ? "P1" : "P2";
|
|
398
|
+
watchParts.push(advisoryCard(a.status === "fail" ? "fail" : "warn", a.status === "fail" ? "🔴" : "🟡", `${esc(a.name)} (${prio})`, esc(a.message)));
|
|
399
|
+
}
|
|
400
|
+
if (sem.blocked) {
|
|
401
|
+
watchParts.push(advisoryCard("warn", "🟡", "Embedding server unreachable (P2)", "Curate quality and semantic ranking are degraded. Check the embedding endpoint configured in config.json."));
|
|
402
|
+
}
|
|
403
|
+
if (proposals.length > 0) {
|
|
404
|
+
const bySource = new Map();
|
|
405
|
+
for (const p of proposals)
|
|
406
|
+
bySource.set(p.source, (bySource.get(p.source) ?? 0) + 1);
|
|
407
|
+
const srcSummary = [...bySource.entries()]
|
|
408
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
409
|
+
.map(([source, count]) => `${count} via ${esc(source)}`)
|
|
410
|
+
.join(", ");
|
|
411
|
+
watchParts.push(advisoryCard("warn", "🟡", `Drain ${proposals.length} pending proposals (P2)`, `Proposals generated this batch (${srcSummary}). Run <code>akm proposal list</code> before the queue grows further.`));
|
|
412
|
+
}
|
|
413
|
+
if (wallTime.p95Ms && wallTime.medianMs && wallTime.p95Ms / wallTime.medianMs > 2.5) {
|
|
414
|
+
watchParts.push(advisoryCard("warn", "🟡", `High tail latency (P3): p95=${fmtMs(wallTime.p95Ms)}, median=${fmtMs(wallTime.medianMs)}`, "P95 is well above median. Consolidation/LLM phase dominates wall time on slow runs. " +
|
|
415
|
+
"Check for slow chunks or LLM rate limiting."));
|
|
416
|
+
}
|
|
417
|
+
if (failedRuns > 0) {
|
|
418
|
+
watchParts.push(advisoryCard("warn", "🟡", `${failedRuns} failed run(s) in window (P2)`, `Task fail rate ${taskFailRate}. Inspect failed runs (ok=false) for early-exit or harness errors.`));
|
|
419
|
+
}
|
|
420
|
+
const watchItemsHtml = watchParts.length > 0
|
|
421
|
+
? watchParts.join("\n")
|
|
422
|
+
: passCard("Nothing critical to watch", "All indicators are within normal range.");
|
|
423
|
+
// ── Commands used ──────────────────────────────────────────────────────────
|
|
424
|
+
const commandsHtml = [
|
|
425
|
+
` <div><span>akm health --since=${esc(opts.window)} --group-by run --format json</span></div>`,
|
|
426
|
+
` <div><span>akm health --since=${esc(opts.window)} --window-compare=${esc(opts.compare)} --format json</span></div>`,
|
|
427
|
+
" <div><span>akm proposal list</span></div>",
|
|
428
|
+
].join("\n");
|
|
429
|
+
return {
|
|
430
|
+
"%%ECHARTS_TAG%%": buildEchartsTag(opts),
|
|
431
|
+
"%%REPORT_TITLE%%": esc(reportTitle),
|
|
432
|
+
"%%WINDOW%%": esc(opts.window),
|
|
433
|
+
"%%SINCE_HUMAN%%": esc(sinceHuman),
|
|
434
|
+
"%%RUN_COUNT%%": num(totalRuns),
|
|
435
|
+
"%%STATUS_BADGE_HTML%%": `${statusBadge}\n ${failBadge}`,
|
|
436
|
+
"%%EXEC_SUMMARY_HTML%%": execSummary,
|
|
437
|
+
"%%KPI_CARDS_HTML%%": kpiCards,
|
|
438
|
+
"%%RUNS_JS_CONST%%": runsJsConst,
|
|
439
|
+
"%%DISTILL_REASONS_JSON%%": JSON.stringify(distillReasons),
|
|
440
|
+
"%%SUMMARY_ROWS_HTML%%": summaryRowsHtml,
|
|
441
|
+
"%%ADVISORY_CARDS_HTML%%": advisoryCardsHtml,
|
|
442
|
+
"%%PROPOSAL_ROWS_HTML%%": proposalRowsHtml,
|
|
443
|
+
"%%PROPOSAL_COUNT%%": String(proposals.length),
|
|
444
|
+
"%%WATCH_ITEMS_HTML%%": watchItemsHtml,
|
|
445
|
+
"%%COMMANDS_HTML%%": commandsHtml,
|
|
446
|
+
"%%GENERATED_AT%%": esc(generatedAt),
|
|
447
|
+
};
|
|
448
|
+
}
|