akm-cli 0.9.0-beta.9 → 0.9.0-rc.0
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 +592 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-cli.js +220 -227
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +15 -1
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +21 -4
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +382 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +259 -769
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +9 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +39 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +7 -1
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +59 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +69 -67
- package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
- package/dist/integrations/harnesses/claude/index.js +2 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/index.js +2 -3
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +2 -0
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
- package/dist/integrations/harnesses/types.js +1 -0
- package/dist/integrations/session-logs/index.js +16 -0
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +85 -14
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +2 -1
- package/dist/output/text/helpers.js +29 -1
- package/dist/output/text/workflow.js +1 -0
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/scripts/migrate-storage.js +2661 -2369
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +883 -596
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +0 -0
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/cli.js +1 -0
- package/dist/workflows/db.js +3 -4
- package/dist/workflows/runtime/runs.js +43 -118
- package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +5 -4
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
Binary file
|
|
@@ -24,27 +24,30 @@
|
|
|
24
24
|
import fs from "node:fs";
|
|
25
25
|
import os from "node:os";
|
|
26
26
|
import path from "node:path";
|
|
27
|
-
import { assertNever } from "../../core/assert.js";
|
|
28
27
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
29
28
|
import { assembleAssetFromString, serializeFrontmatter } from "../../core/asset/asset-serialize.js";
|
|
30
29
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
31
30
|
import { stripMarkdownFences } from "../../core/asset/markdown.js";
|
|
31
|
+
import { DESCRIPTION_MAX_CHARS, requiresDescription } from "../../core/authoring-rules.js";
|
|
32
32
|
import { resolveStashDir } from "../../core/common.js";
|
|
33
|
-
import { loadConfig } from "../../core/config/config.js";
|
|
33
|
+
import { getImproveProcessConfig, loadConfig } from "../../core/config/config.js";
|
|
34
34
|
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
35
35
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
36
36
|
import { lintLessonContent } from "../../core/lesson-lint.js";
|
|
37
|
+
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
38
|
+
import { warn } from "../../core/warn.js";
|
|
37
39
|
import { lookup } from "../../indexer/indexer.js";
|
|
38
40
|
import { runAgent, } from "../../integrations/agent/index.js";
|
|
39
41
|
import { resolveProcessAgentProfile } from "../../integrations/agent/config.js";
|
|
40
42
|
import { buildReflectPrompt, extractDraftConfidence, parseAgentProposalPayload, } from "../../integrations/agent/prompts.js";
|
|
41
|
-
import { resolveImproveProcessRunnerFromProfile, runnerIsLlm, runnerSupportsFileWrite, } from "../../integrations/agent/runner.js";
|
|
43
|
+
import { resolveDefaultLlmRunner, resolveImproveProcessRunnerFromProfile, runnerIsLlm, runnerSupportsFileWrite, } from "../../integrations/agent/runner.js";
|
|
44
|
+
import { executeRunner } from "../../integrations/agent/runner-dispatch.js";
|
|
42
45
|
import { runOpencodeSdk } from "../../integrations/harnesses/opencode-sdk/index.js";
|
|
43
46
|
import { chatCompletion } from "../../llm/client.js";
|
|
44
47
|
import { isLlmFeatureEnabled } from "../../llm/feature-gate.js";
|
|
45
48
|
import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "../agent/agent-support.js";
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
49
|
+
import { createProposal, isProposalSkipped, listProposals, } from "../proposal/repository.js";
|
|
50
|
+
import { checkReflectSize, isValidDescription } from "../proposal/validators/proposal-quality-validators.js";
|
|
48
51
|
import { deriveLessonRef, runLessonQualityJudge } from "./distill.js";
|
|
49
52
|
import { classifyReflectChange } from "./reflect-noise.js";
|
|
50
53
|
const MAX_FEEDBACK_LINES = 10;
|
|
@@ -383,6 +386,101 @@ function splitFrontmatter(raw) {
|
|
|
383
386
|
return { fmText: null, body: raw };
|
|
384
387
|
return { fmText: m[1], body: m[2] };
|
|
385
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Strip an LLM-appended duplicate frontmatter block from a body string.
|
|
391
|
+
*
|
|
392
|
+
* When the LLM echoes the original source file verbatim after its rewrite,
|
|
393
|
+
* the resulting body contains a second `---...---` YAML block. We detect it
|
|
394
|
+
* by requiring BOTH a balanced fence (opening + closing `---`) AND YAML-like
|
|
395
|
+
* `key: value` content inside, so legitimate Markdown thematic breaks and
|
|
396
|
+
* code-fence examples are never truncated.
|
|
397
|
+
*/
|
|
398
|
+
function stripAppendedFrontmatter(body) {
|
|
399
|
+
const fencePattern = /\n---\r?\n([\s\S]*?)\n---\r?\n/;
|
|
400
|
+
const match = body.match(fencePattern);
|
|
401
|
+
if (!match)
|
|
402
|
+
return body;
|
|
403
|
+
// Only strip when the captured block looks like YAML frontmatter.
|
|
404
|
+
if (!/^\w[\w-]*:/m.test(match[1]))
|
|
405
|
+
return body;
|
|
406
|
+
return body.slice(0, body.indexOf(match[0])).replace(/\s+$/, "");
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* #636 — deterministically derive a valid `description` from an asset's existing
|
|
410
|
+
* metadata when one is missing. Sources, in priority order: the `title:`
|
|
411
|
+
* frontmatter field, the first `# Heading` in the (proposed or source) body, and
|
|
412
|
+
* the first sentence of the opening body paragraph. The candidate is normalized
|
|
413
|
+
* (whitespace collapsed, trailing punctuation/markdown stripped, clamped to the
|
|
414
|
+
* description max) and only returned if it PASSES `isValidDescription` — so this
|
|
415
|
+
* never produces a heading-fragment, truncated, or otherwise gate-failing value.
|
|
416
|
+
* Returns `undefined` when nothing usable can be derived (caller leaves the
|
|
417
|
+
* proposal as-is rather than fabricating prose).
|
|
418
|
+
*
|
|
419
|
+
* This is intentionally deterministic and lives in the reflect proposal-build
|
|
420
|
+
* path — it does NOT touch the validators or the promote-time repair.
|
|
421
|
+
*/
|
|
422
|
+
function deriveDescriptionFromAsset(title, proposedBody, sourceBody, targetRef) {
|
|
423
|
+
// Each candidate is tagged with its kind. A title or `# Heading` is a bare
|
|
424
|
+
// fragment ("Paged.js — Named Page") that reads poorly as a description even
|
|
425
|
+
// when it is long enough to pass the length gate, so for those we prefer the
|
|
426
|
+
// padded sentence form. A prose sentence is already a sentence, so it is used
|
|
427
|
+
// as-is (padding it would double-wrap an already-complete sentence).
|
|
428
|
+
const candidates = [];
|
|
429
|
+
// 1. title: frontmatter
|
|
430
|
+
if (typeof title === "string" && title.trim())
|
|
431
|
+
candidates.push({ text: title.trim(), kind: "fragment" });
|
|
432
|
+
// 2. first `# Heading` (proposed body first, then source body)
|
|
433
|
+
for (const body of [proposedBody, sourceBody]) {
|
|
434
|
+
const headingMatch = body.match(/^#{1,6}\s+(.+?)\s*$/m);
|
|
435
|
+
if (headingMatch?.[1])
|
|
436
|
+
candidates.push({ text: headingMatch[1].trim(), kind: "fragment" });
|
|
437
|
+
}
|
|
438
|
+
// 3. first sentence of the opening prose paragraph (skip headings, fences,
|
|
439
|
+
// list markers, blockquotes — those are not prose).
|
|
440
|
+
for (const body of [proposedBody, sourceBody]) {
|
|
441
|
+
const firstSentence = firstProseSentence(body);
|
|
442
|
+
if (firstSentence)
|
|
443
|
+
candidates.push({ text: firstSentence, kind: "prose" });
|
|
444
|
+
}
|
|
445
|
+
for (const { text, kind } of candidates) {
|
|
446
|
+
const normalized = normalizeDescriptionCandidate(text);
|
|
447
|
+
if (!normalized)
|
|
448
|
+
continue;
|
|
449
|
+
// For a title/heading fragment, try the padded sentence form FIRST so the
|
|
450
|
+
// result reads as a sentence rather than a bare fragment — a short but valid
|
|
451
|
+
// title like "Paged.js — Named Page" (21 chars) would otherwise be returned
|
|
452
|
+
// verbatim. Fall back to the bare form only if the padded form fails the
|
|
453
|
+
// gate. A prose candidate is already a sentence, so it is used as-is.
|
|
454
|
+
const variants = kind === "fragment" ? [`Reference notes on ${normalized}.`, normalized] : [normalized];
|
|
455
|
+
for (const v of variants) {
|
|
456
|
+
const clamped = v.length > DESCRIPTION_MAX_CHARS ? v.slice(0, DESCRIPTION_MAX_CHARS).trimEnd() : v;
|
|
457
|
+
if (isValidDescription(clamped, targetRef, { skipRefTailCheck: true }).ok)
|
|
458
|
+
return clamped;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
/** Extract the first prose sentence from a markdown body, or `""` if none. */
|
|
464
|
+
function firstProseSentence(body) {
|
|
465
|
+
for (const rawLine of body.split(/\r?\n/)) {
|
|
466
|
+
const line = rawLine.trim();
|
|
467
|
+
if (!line)
|
|
468
|
+
continue;
|
|
469
|
+
if (/^(#{1,6}\s|```|~~~|[-*+]\s|\d+\.\s|>|\||<!--)/.test(line))
|
|
470
|
+
continue;
|
|
471
|
+
const sentenceMatch = line.match(/^(.+?[.!?])(\s|$)/);
|
|
472
|
+
return (sentenceMatch?.[1] ?? line).trim();
|
|
473
|
+
}
|
|
474
|
+
return "";
|
|
475
|
+
}
|
|
476
|
+
/** Normalize a description candidate: strip markdown markers, collapse space. */
|
|
477
|
+
function normalizeDescriptionCandidate(raw) {
|
|
478
|
+
return raw
|
|
479
|
+
.replace(/`/g, "")
|
|
480
|
+
.replace(/^[#>*\-\s]+/, "")
|
|
481
|
+
.replace(/\s+/g, " ")
|
|
482
|
+
.trim();
|
|
483
|
+
}
|
|
386
484
|
/**
|
|
387
485
|
* Reflect post-processor — enforces the safety rails described at the top of
|
|
388
486
|
* this file:
|
|
@@ -408,7 +506,7 @@ function splitFrontmatter(raw) {
|
|
|
408
506
|
* from `payload.frontmatter` so identity fields can be enforced. Size guard
|
|
409
507
|
* is skipped because there is no source to compare against.
|
|
410
508
|
*/
|
|
411
|
-
function sanitizeReflectPayload(payload, sourceContent, targetRef) {
|
|
509
|
+
export function sanitizeReflectPayload(payload, sourceContent, targetRef) {
|
|
412
510
|
const warnings = [];
|
|
413
511
|
const { fmText: sourceFmText, body: sourceBody } = sourceContent
|
|
414
512
|
? splitFrontmatter(sourceContent)
|
|
@@ -449,7 +547,35 @@ function sanitizeReflectPayload(payload, sourceContent, targetRef) {
|
|
|
449
547
|
mergedFm[field] = sourceFm[field];
|
|
450
548
|
}
|
|
451
549
|
}
|
|
452
|
-
const cleanedBody = rawLlmBody.replace(/^\s+/, "");
|
|
550
|
+
const cleanedBody = stripAppendedFrontmatter(rawLlmBody.replace(/^\s+/, ""));
|
|
551
|
+
// #636 — deterministic description fallback (reflect-side belt-and-suspenders).
|
|
552
|
+
// If the type requires a `description` and the merged frontmatter is still
|
|
553
|
+
// MISSING one (source had none AND the model didn't author one), derive a
|
|
554
|
+
// description DETERMINISTICALLY from the existing `title:` frontmatter or the
|
|
555
|
+
// first `# Heading` / opening body sentence — never free-form invention. This
|
|
556
|
+
// runs in the reflect proposal-build path, BEFORE the proposal is created, so
|
|
557
|
+
// the validator/promote path is left untouched (no gate fabricates content).
|
|
558
|
+
//
|
|
559
|
+
// Scope is the issue's target: a source asset that ALREADY carries frontmatter
|
|
560
|
+
// (e.g. scraped docs: `source`/`title`/`scraped`) but has a MISSING/empty
|
|
561
|
+
// `description`. We deliberately do NOT fire when:
|
|
562
|
+
// - the source has no frontmatter block at all (injecting one would be a
|
|
563
|
+
// structural change and would defeat the #580 no-op/cosmetic noise gate
|
|
564
|
+
// for a pure body echo), or
|
|
565
|
+
// - a present-but-otherwise-invalid description exists (too short, a heading
|
|
566
|
+
// fragment) — overwriting authored content is out of scope; the prompt
|
|
567
|
+
// instruction handles improving it instead.
|
|
568
|
+
const refType = targetRef.includes(":") ? (targetRef.split(":")[0] ?? "") : "";
|
|
569
|
+
const mergedDesc = mergedFm.description;
|
|
570
|
+
const descIsMissing = typeof mergedDesc !== "string" || mergedDesc.trim().length === 0;
|
|
571
|
+
const sourceHadFrontmatter = sourceFmText !== null && Object.keys(sourceFm).length > 0;
|
|
572
|
+
if (refType && requiresDescription(refType) && descIsMissing && sourceHadFrontmatter) {
|
|
573
|
+
const derived = deriveDescriptionFromAsset(mergedFm.title, cleanedBody, sourceBody, targetRef);
|
|
574
|
+
if (derived) {
|
|
575
|
+
mergedFm.description = derived;
|
|
576
|
+
warnings.push("Synthesized a deterministic `description` from title/heading (#636) — source and proposal lacked one.");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
453
579
|
// Size guard — only when source body is meaningfully large. The pure
|
|
454
580
|
// predicate lives in `core/proposal-quality-validators` so the same check
|
|
455
581
|
// also runs inside `runProposalValidators` on `proposal accept`.
|
|
@@ -673,7 +799,7 @@ export async function akmReflect(options = {}) {
|
|
|
673
799
|
}
|
|
674
800
|
else {
|
|
675
801
|
const cfg = options.config ?? loadConfig();
|
|
676
|
-
const reflectProcess = cfg.
|
|
802
|
+
const reflectProcess = getImproveProcessConfig(cfg, "reflect", options.improveProfile);
|
|
677
803
|
// Resolve the runner from the improve profile's reflect entry when present.
|
|
678
804
|
runnerSpec = resolveImproveProcessRunnerFromProfile(reflectProcess, cfg) ?? undefined;
|
|
679
805
|
if (runnerSpec) {
|
|
@@ -705,6 +831,28 @@ export async function akmReflect(options = {}) {
|
|
|
705
831
|
throw err;
|
|
706
832
|
throw err;
|
|
707
833
|
}
|
|
834
|
+
// P1.3 (meta-review 07, Chain G): unattended `akm improve` must never hand
|
|
835
|
+
// reflect a tool-capable runner. Agent-CLI/SDK runners have filesystem
|
|
836
|
+
// access, so a poisoned asset body flowing through the scheduled reflect
|
|
837
|
+
// prompt could steer an agent with tools; the tool-less LLM HTTP runner
|
|
838
|
+
// corrupts output text at worst (src/llm/call-ai.ts). An llm RunnerSpec
|
|
839
|
+
// resolved from the process block is honored; anything else — an agent/sdk
|
|
840
|
+
// spec, a resolved agent profile, or the default-agent fallback below — is
|
|
841
|
+
// pinned to `defaults.llm`, failing CLOSED when that is unset.
|
|
842
|
+
// `options.agentProfile` stays exempt: it is a direct-injection test seam
|
|
843
|
+
// the cron path (loop-stages) never sets.
|
|
844
|
+
if (options.eventSource === "improve" && !options.agentProfile && (!runnerSpec || !runnerIsLlm(runnerSpec))) {
|
|
845
|
+
const cfg = options.config ?? loadConfig();
|
|
846
|
+
const pinned = resolveDefaultLlmRunner(cfg, resolvedTimeoutMs);
|
|
847
|
+
if (!pinned) {
|
|
848
|
+
throw new ConfigError("Unattended improve pins reflect to the tool-less LLM runner, but the config resolves a tool-capable runner and no defaults.llm profile exists to pin to.", "LLM_NOT_CONFIGURED", 'Set processes.reflect.mode to "llm" on the active improve profile, or configure defaults.llm.');
|
|
849
|
+
}
|
|
850
|
+
warn(runnerSpec
|
|
851
|
+
? `[akm] reflect: unattended improve resolved a tool-capable "${runnerSpec.kind}" runner — pinned to the tool-less LLM runner (07 Chain-G).`
|
|
852
|
+
: "[akm] reflect: unattended improve would have fallen back to an agent profile — pinned to the tool-less LLM runner (07 Chain-G).");
|
|
853
|
+
runnerSpec = pinned;
|
|
854
|
+
profile = undefined;
|
|
855
|
+
}
|
|
708
856
|
// Ensure profile is set for agent/sdk runners that don't use runnerSpec
|
|
709
857
|
if (!runnerSpec && !profile) {
|
|
710
858
|
const agent = options.agentConfig ?? loadAgentConfigFromDisk();
|
|
@@ -726,6 +874,9 @@ export async function akmReflect(options = {}) {
|
|
|
726
874
|
// Reflexion-style verbal-RL: inject rejected proposals so the agent avoids
|
|
727
875
|
// reproducing proposals that have already been reviewed and refused.
|
|
728
876
|
const rejectedProposals = readRejectedProposals(stash, options.ref);
|
|
877
|
+
// Standards "rulebook" for this target — wiki schema (wiki page) or stash
|
|
878
|
+
// convention/meta facts (non-wiki asset); empty when neither fires.
|
|
879
|
+
const standardsContext = resolveStandardsContext(options.ref, stash);
|
|
729
880
|
// 5. Spawn the agent — with optional Self-Refine loop (R-1 / #372).
|
|
730
881
|
//
|
|
731
882
|
// maxRefineIters controls how many agent invocations are made:
|
|
@@ -794,6 +945,7 @@ export async function akmReflect(options = {}) {
|
|
|
794
945
|
...(schemaHints.length > 0 ? { schemaHints } : {}),
|
|
795
946
|
...(relatedLessons.length > 0 ? { relatedLessons } : {}),
|
|
796
947
|
...(options.task ? { task: options.task } : {}),
|
|
948
|
+
...(standardsContext.trim() ? { standardsContext } : {}),
|
|
797
949
|
...(options.avoidPatterns && options.avoidPatterns.length > 0 ? { avoidPatterns: options.avoidPatterns } : {}),
|
|
798
950
|
...(rejectedProposals.length > 0 ? { rejectedProposals } : {}),
|
|
799
951
|
// R-1: inject prior draft as self-critique target on iterations > 0
|
|
@@ -825,44 +977,39 @@ export async function akmReflect(options = {}) {
|
|
|
825
977
|
iterResult = await runAgent(resolvedProfile, prompt, runOptions);
|
|
826
978
|
}
|
|
827
979
|
else if (runnerSpec) {
|
|
828
|
-
// v2: dispatch through unified RunnerSpec
|
|
980
|
+
// v2: dispatch through the unified RunnerSpec seam (X3). The `agent` /
|
|
981
|
+
// `sdk` arms route to the default profile runners; the `llm` arm is
|
|
982
|
+
// reflect-specific (wraps `runReflectViaLlm` — its bespoke iteration
|
|
983
|
+
// shape) so it is supplied as the `llm` handler.
|
|
829
984
|
const runOptions = {
|
|
830
985
|
stdio: "captured",
|
|
831
986
|
parseOutput: "text",
|
|
832
987
|
...(Object.keys(agentEnv).length > 0 ? { env: agentEnv } : {}),
|
|
833
988
|
};
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
...(runnerSpec.timeoutMs !== undefined ? { timeoutMs: runnerSpec.timeoutMs } : {}),
|
|
859
|
-
});
|
|
860
|
-
break;
|
|
861
|
-
default:
|
|
862
|
-
// Exhaustiveness arm (H1): a 4th RunnerSpec kind becomes a compile
|
|
863
|
-
// error here instead of leaving `iterResult` unassigned at runtime.
|
|
864
|
-
assertNever(runnerSpec);
|
|
865
|
-
}
|
|
989
|
+
iterResult = await executeRunner(runnerSpec, prompt ?? "", runOptions, {
|
|
990
|
+
llm: async (spec) =>
|
|
991
|
+
// LLM HTTP path — `draftFilePath` is accepted for type symmetry
|
|
992
|
+
// (see `RunReflectViaLlmOptions.draftFilePath` docstring) but is
|
|
993
|
+
// intentionally a no-op. The prompt builder above also did not
|
|
994
|
+
// include the file-write contract for this kind, so the LLM is
|
|
995
|
+
// still asked for JSON via stdout.
|
|
996
|
+
runReflectViaLlm({
|
|
997
|
+
prompt,
|
|
998
|
+
connection: spec.connection,
|
|
999
|
+
timeoutMs: spec.timeoutMs ?? (typeof resolvedTimeoutMs === "number" ? resolvedTimeoutMs : undefined),
|
|
1000
|
+
priorDraft,
|
|
1001
|
+
iteration: iter,
|
|
1002
|
+
responseSchema: REFLECT_JSON_SCHEMA,
|
|
1003
|
+
chat: options.chat,
|
|
1004
|
+
...(maxTokensForLlm !== undefined ? { maxTokens: maxTokensForLlm } : {}),
|
|
1005
|
+
}),
|
|
1006
|
+
// The `agent` arm (and only the agent arm — preserving prior behavior)
|
|
1007
|
+
// overlays `spec.timeoutMs` onto the base run options.
|
|
1008
|
+
runAgent: (profile, p, opts) => runAgent(profile, p, {
|
|
1009
|
+
...opts,
|
|
1010
|
+
...(runnerSpec.timeoutMs !== undefined ? { timeoutMs: runnerSpec.timeoutMs } : {}),
|
|
1011
|
+
}),
|
|
1012
|
+
});
|
|
866
1013
|
}
|
|
867
1014
|
else {
|
|
868
1015
|
// Production path (v1): dispatch directly to the appropriate runner.
|
|
@@ -1047,7 +1194,8 @@ export async function akmReflect(options = {}) {
|
|
|
1047
1194
|
// `profiles.improve.default.processes.reflect.qualityGate.enabled` or
|
|
1048
1195
|
// `profiles.improve.default.processes.distill.qualityGate.enabled` (the
|
|
1049
1196
|
// `lesson_quality_gate` flag name is the legacy alias still accepted by
|
|
1050
|
-
// `isLlmFeatureEnabled`). Fail-
|
|
1197
|
+
// `isLlmFeatureEnabled`). Fail-CLOSED (07 P0-2): a judge error / no-LLM /
|
|
1198
|
+
// parse failure rejects the proposal rather than passing it through.
|
|
1051
1199
|
// G-Eval (arXiv:2303.16634) — quality judgment before admission.
|
|
1052
1200
|
const runtimeConfig = options.config ??
|
|
1053
1201
|
(() => {
|
|
@@ -1151,8 +1299,19 @@ export async function akmReflect(options = {}) {
|
|
|
1151
1299
|
// (new-asset proposals have nothing to diff against).
|
|
1152
1300
|
if (assetContent !== undefined) {
|
|
1153
1301
|
const changeKind = classifyReflectChange(assetContent, payload.content);
|
|
1154
|
-
|
|
1155
|
-
|
|
1302
|
+
// 'low-value' is config-gated (#639). DEFAULT OFF — absent = byte-identical
|
|
1303
|
+
// pre-#639 behaviour (low-value treated the same as substantive). Resolved
|
|
1304
|
+
// by the caller from the ACTIVE improve profile's
|
|
1305
|
+
// `processes.reflect.lowValueFilter.enabled` and passed via options, so the
|
|
1306
|
+
// running profile (not a hardcoded `profiles.improve.default`) decides.
|
|
1307
|
+
const lowValueFilterEnabled = options.lowValueFilter === true;
|
|
1308
|
+
const isDeferred = changeKind === "noop" || changeKind === "cosmetic" || (changeKind === "low-value" && lowValueFilterEnabled);
|
|
1309
|
+
if (isDeferred) {
|
|
1310
|
+
const subreason = changeKind === "noop"
|
|
1311
|
+
? "reflect_skipped_noop"
|
|
1312
|
+
: changeKind === "low-value"
|
|
1313
|
+
? "reflect_skipped_low_value"
|
|
1314
|
+
: "reflect_skipped_cosmetic";
|
|
1156
1315
|
emitReflectFailed("no_change", subreason, options.ref, { changeKind });
|
|
1157
1316
|
return {
|
|
1158
1317
|
schemaVersion: 1,
|
|
@@ -1160,7 +1319,9 @@ export async function akmReflect(options = {}) {
|
|
|
1160
1319
|
reason: "no_change",
|
|
1161
1320
|
error: changeKind === "noop"
|
|
1162
1321
|
? `Reflect skipped: proposed content for ${payload.ref} is identical to the current asset (empty diff); no proposal created.`
|
|
1163
|
-
:
|
|
1322
|
+
: changeKind === "low-value"
|
|
1323
|
+
? `Reflect skipped: proposed content for ${payload.ref} is a low-value prose micro-rewrite (few changed tokens, no structural changes); no proposal created.`
|
|
1324
|
+
: `Reflect skipped: proposed content for ${payload.ref} is a cosmetic-only reformat of the current asset (whitespace/fence/YAML-folding changes); no proposal created.`,
|
|
1164
1325
|
...(options.ref ? { ref: options.ref } : {}),
|
|
1165
1326
|
exitCode: result.exitCode,
|
|
1166
1327
|
};
|