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
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
* - MemOS (arXiv:2507.03724) — formal archive/merge/transition with shared state model
|
|
29
29
|
*/
|
|
30
30
|
import fs from "node:fs";
|
|
31
|
-
import { assembleAsset } from "
|
|
32
|
-
import { parseFrontmatter } from "
|
|
31
|
+
import { assembleAsset } from "../../../core/asset/asset-serialize.js";
|
|
32
|
+
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
33
33
|
// ── Contradiction edge writer ─────────────────────────────────────────────────
|
|
34
34
|
/**
|
|
35
35
|
* Write `contradictedBy` and `beliefState: contradicted` edges to a memory
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import fs from "node:fs";
|
|
37
37
|
import path from "node:path";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
38
|
+
import { assembleAsset } from "../../../core/asset/asset-serialize.js";
|
|
39
|
+
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
40
|
+
import { getDefaultLlmConfig } from "../../../core/config/config.js";
|
|
41
|
+
import { chatCompletion, parseEmbeddedJsonResponse } from "../../../llm/client.js";
|
|
42
|
+
import { tryLlmFeature } from "../../../llm/feature-gate.js";
|
|
43
43
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
44
44
|
/**
|
|
45
45
|
* Maximum family size for pairwise contradiction checking. Families larger
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { makeAssetRef, parseAssetRef } from "
|
|
7
|
-
import { assembleAsset } from "
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { makeAssetRef, parseAssetRef } from "../../../core/asset/asset-ref.js";
|
|
7
|
+
import { assembleAsset } from "../../../core/asset/asset-serialize.js";
|
|
8
|
+
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
9
|
+
import { firstString, groupBy, stringArray } from "../../../core/common.js";
|
|
10
10
|
const DERIVED_SUFFIX = ".derived";
|
|
11
11
|
export function analyzeMemoryCleanup(stashDir, options = {}) {
|
|
12
12
|
const records = collectDerivedMemories(stashDir, options.parentRef);
|
|
Binary file
|
|
@@ -24,27 +24,29 @@
|
|
|
24
24
|
import fs from "node:fs";
|
|
25
25
|
import os from "node:os";
|
|
26
26
|
import path from "node:path";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
27
|
+
import { assertNever } from "../../core/assert.js";
|
|
28
|
+
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
29
|
+
import { assembleAssetFromString, serializeFrontmatter } from "../../core/asset/asset-serialize.js";
|
|
30
|
+
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
31
|
+
import { stripMarkdownFences } from "../../core/asset/markdown.js";
|
|
32
|
+
import { resolveStashDir } from "../../core/common.js";
|
|
33
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
34
|
+
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
35
|
+
import { appendEvent, readEvents } from "../../core/events.js";
|
|
36
|
+
import { lintLessonContent } from "../../core/lesson-lint.js";
|
|
37
|
+
import { lookup } from "../../indexer/indexer.js";
|
|
38
|
+
import { runAgent, } from "../../integrations/agent/index.js";
|
|
39
|
+
import { resolveProcessAgentProfile } from "../../integrations/agent/config.js";
|
|
40
|
+
import { buildReflectPrompt, extractDraftConfidence, parseAgentProposalPayload, } from "../../integrations/agent/prompts.js";
|
|
41
|
+
import { resolveImproveProcessRunnerFromProfile, runnerIsLlm, runnerSupportsFileWrite, } from "../../integrations/agent/runner.js";
|
|
42
|
+
import { runOpencodeSdk } from "../../integrations/harnesses/opencode-sdk/index.js";
|
|
43
|
+
import { chatCompletion } from "../../llm/client.js";
|
|
44
|
+
import { isLlmFeatureEnabled } from "../../llm/feature-gate.js";
|
|
45
|
+
import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "../agent/agent-support.js";
|
|
46
|
+
import { checkReflectSize } from "../proposal/validators/proposal-quality-validators.js";
|
|
47
|
+
import { createProposal, isProposalSkipped, listProposals, } from "../proposal/validators/proposals.js";
|
|
48
|
+
import { deriveLessonRef, runLessonQualityJudge } from "./distill.js";
|
|
49
|
+
import { classifyReflectChange } from "./reflect-noise.js";
|
|
48
50
|
const MAX_FEEDBACK_LINES = 10;
|
|
49
51
|
const MAX_GLOBAL_FEEDBACK_LINES = 20;
|
|
50
52
|
/**
|
|
@@ -76,7 +78,7 @@ const MAX_REJECTED_PROPOSALS = 3;
|
|
|
76
78
|
* Asset types that reflect is allowed to operate on.
|
|
77
79
|
*
|
|
78
80
|
* Reflect's canonical output shape is `frontmatter + markdown body`. Running it
|
|
79
|
-
* against types whose on-disk form is NOT markdown (executable scripts,
|
|
81
|
+
* against types whose on-disk form is NOT markdown (executable scripts, env files
|
|
80
82
|
* env files, YAML tasks) blindly prepends `---\n…\n---\n` to the asset and
|
|
81
83
|
* breaks the runtime contract — for example a `.ts` script with a YAML preamble
|
|
82
84
|
* is a TypeScript syntax error.
|
|
@@ -614,7 +616,7 @@ export async function akmReflect(options = {}) {
|
|
|
614
616
|
parsedRef = parseAssetRef(options.ref);
|
|
615
617
|
// 2a. Type guard — reflect only operates on asset types whose canonical
|
|
616
618
|
// shape is `frontmatter + markdown body`. Refuse non-markdown types
|
|
617
|
-
// (script /
|
|
619
|
+
// (script / env / task) up-front so reflect never prepends YAML to a
|
|
618
620
|
// `.ts` file or rewrites a `.env` blob as prose. See REFLECT_ALLOWED_TYPES.
|
|
619
621
|
if (!REFLECT_ALLOWED_TYPES.has(parsedRef.type)) {
|
|
620
622
|
// Deterministic type-guard rejection — the LLM is never invoked. Emit
|
|
@@ -707,10 +709,10 @@ export async function akmReflect(options = {}) {
|
|
|
707
709
|
}
|
|
708
710
|
// Derive a display name for logging — either from the resolved profile or the runnerSpec.
|
|
709
711
|
const resolvedProfileName = profile?.name ??
|
|
710
|
-
(runnerSpec
|
|
712
|
+
(runnerSpec && runnerIsLlm(runnerSpec)
|
|
711
713
|
? `llm:${runnerSpec.connection.model}`
|
|
712
|
-
: runnerSpec
|
|
713
|
-
? `${runnerSpec.kind}:${runnerSpec.profile
|
|
714
|
+
: runnerSpec
|
|
715
|
+
? `${runnerSpec.kind}:${runnerSpec.profile.name ?? "unknown"}`
|
|
714
716
|
: "unknown");
|
|
715
717
|
// 4. Build the shared prompt inputs — feedback, hints, lessons, rejected
|
|
716
718
|
// proposals. These are stable across refinement iterations; only the
|
|
@@ -739,7 +741,7 @@ export async function akmReflect(options = {}) {
|
|
|
739
741
|
// `profile.sdkMode` fallback also runs the SDK so it counts as file-writable.
|
|
740
742
|
// Test seams (`options.runAgentOptions.spawn`) emulate agent CLI behaviour so
|
|
741
743
|
// they participate as well — tests opt out by simply not writing the file.
|
|
742
|
-
const
|
|
744
|
+
const canRunnerWriteFile = runnerSpec ? runnerSupportsFileWrite(runnerSpec) : true;
|
|
743
745
|
// Initialized to a sentinel; always overwritten in the first loop iteration
|
|
744
746
|
// (maxRefineIters is clamped to >= 1 above). TypeScript cannot prove a
|
|
745
747
|
// for-loop always runs at least once, so we use a type assertion here.
|
|
@@ -775,7 +777,7 @@ export async function akmReflect(options = {}) {
|
|
|
775
777
|
for (let iter = 0; iter < maxRefineIters; iter++) {
|
|
776
778
|
// Synthesize a fresh tmp path per iteration so refinement passes never
|
|
777
779
|
// clobber an earlier draft (and so reading back is unambiguous).
|
|
778
|
-
const iterDraftPath =
|
|
780
|
+
const iterDraftPath = canRunnerWriteFile ? synthesizeReflectDraftPath(options.ref) : undefined;
|
|
779
781
|
if (iterDraftPath) {
|
|
780
782
|
draftPathsToCleanup.push(iterDraftPath);
|
|
781
783
|
lastDraftPath = iterDraftPath;
|
|
@@ -853,6 +855,10 @@ export async function akmReflect(options = {}) {
|
|
|
853
855
|
...(runnerSpec.timeoutMs !== undefined ? { timeoutMs: runnerSpec.timeoutMs } : {}),
|
|
854
856
|
});
|
|
855
857
|
break;
|
|
858
|
+
default:
|
|
859
|
+
// Exhaustiveness arm (H1): a 4th RunnerSpec kind becomes a compile
|
|
860
|
+
// error here instead of leaving `iterResult` unassigned at runtime.
|
|
861
|
+
assertNever(runnerSpec);
|
|
856
862
|
}
|
|
857
863
|
}
|
|
858
864
|
else {
|
|
@@ -1133,6 +1139,30 @@ export async function akmReflect(options = {}) {
|
|
|
1133
1139
|
content: sanitizeOutcome.content,
|
|
1134
1140
|
...(sanitizeOutcome.frontmatter ? { frontmatter: sanitizeOutcome.frontmatter } : {}),
|
|
1135
1141
|
};
|
|
1142
|
+
// 7c. Noise gate (#580): never queue a proposal whose sanitized content is
|
|
1143
|
+
// identical to the current asset (empty diff) or differs only cosmetically
|
|
1144
|
+
// (whitespace reflow, code-fence language hints, YAML scalar re-folding).
|
|
1145
|
+
// Pure deterministic text comparison — see `reflect-noise.ts`. Runs before
|
|
1146
|
+
// the draftMode branch so self-consistency sampling never votes a no-op
|
|
1147
|
+
// candidate into the queue either. Skipped when there is no source asset
|
|
1148
|
+
// (new-asset proposals have nothing to diff against).
|
|
1149
|
+
if (assetContent !== undefined) {
|
|
1150
|
+
const changeKind = classifyReflectChange(assetContent, payload.content);
|
|
1151
|
+
if (changeKind !== "substantive") {
|
|
1152
|
+
const subreason = changeKind === "noop" ? "reflect_skipped_noop" : "reflect_skipped_cosmetic";
|
|
1153
|
+
emitReflectFailed("no_change", subreason, options.ref, { changeKind });
|
|
1154
|
+
return {
|
|
1155
|
+
schemaVersion: 1,
|
|
1156
|
+
ok: false,
|
|
1157
|
+
reason: "no_change",
|
|
1158
|
+
error: changeKind === "noop"
|
|
1159
|
+
? `Reflect skipped: proposed content for ${payload.ref} is identical to the current asset (empty diff); no proposal created.`
|
|
1160
|
+
: `Reflect skipped: proposed content for ${payload.ref} is a cosmetic-only reformat of the current asset (whitespace/fence/YAML-folding changes); no proposal created.`,
|
|
1161
|
+
...(options.ref ? { ref: options.ref } : {}),
|
|
1162
|
+
exitCode: result.exitCode,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1136
1166
|
// 8. Create the proposal. The proposal queue is the ONLY thing reflect
|
|
1137
1167
|
// writes — promotion to a real asset is gated by `akm proposal accept`.
|
|
1138
1168
|
//
|
|
@@ -0,0 +1,248 @@
|
|
|
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
|
+
* Session asset generation for the `extract` pass (#561).
|
|
6
|
+
*
|
|
7
|
+
* After the extractor distills memory proposals from a session, it ALSO writes
|
|
8
|
+
* the session itself to the stash as a first-class `session` asset so any agent
|
|
9
|
+
* — on any harness — can discover prior work via `akm search` / `akm curate`.
|
|
10
|
+
*
|
|
11
|
+
* Design constraints (see #561):
|
|
12
|
+
* - ADDITIVE + FAIL-OPEN + CONFIG-GATED. Disabled (or no LLM provider) →
|
|
13
|
+
* extract behaves EXACTLY as before. Nothing is written.
|
|
14
|
+
* - The LLM summary call routes through the injectable {@link SessionSummaryGenerator}
|
|
15
|
+
* seam so tests never touch a real provider, and so production wraps the
|
|
16
|
+
* call in the existing `tryLlmFeature` fail-open pattern.
|
|
17
|
+
* - The `log_path` + `access` frontmatter fields are the durable correlation
|
|
18
|
+
* key — they survive index rebuilds (the body is re-derived from disk).
|
|
19
|
+
*
|
|
20
|
+
* The asset is written to `sessions/<harness>/<session-id>.md`; the registered
|
|
21
|
+
* `session` asset type (see `asset-spec.ts`) makes the normal index pass pick it
|
|
22
|
+
* up for FTS + vector search with no special-casing.
|
|
23
|
+
*/
|
|
24
|
+
import fs from "node:fs";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
27
|
+
import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
|
|
28
|
+
import { normalizeHarnessId } from "../../integrations/harnesses/index.js";
|
|
29
|
+
/**
|
|
30
|
+
* JSON Schema for the session-summary LLM call. Strict so providers that
|
|
31
|
+
* support schema enforcement constrain the output upstream; the parser only
|
|
32
|
+
* has to handle the happy path. `additionalProperties: false` drops any
|
|
33
|
+
* hallucinated keys before parsing.
|
|
34
|
+
*/
|
|
35
|
+
export const SESSION_SUMMARY_JSON_SCHEMA = {
|
|
36
|
+
type: "object",
|
|
37
|
+
required: ["summary", "key_topics"],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
properties: {
|
|
40
|
+
summary: { type: "string" },
|
|
41
|
+
key_topics: { type: "array", items: { type: "string" } },
|
|
42
|
+
tags: { type: "array", items: { type: "string" } },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Render a compact transcript snippet from session events for the summary
|
|
47
|
+
* prompt. Mirrors the extract transcript format but caps total length so the
|
|
48
|
+
* summary prompt stays bounded regardless of session size.
|
|
49
|
+
*/
|
|
50
|
+
function renderTranscriptForSummary(events, maxChars = 12_000) {
|
|
51
|
+
if (events.length === 0)
|
|
52
|
+
return "(empty — no events)";
|
|
53
|
+
const lines = [];
|
|
54
|
+
let total = 0;
|
|
55
|
+
for (const e of events) {
|
|
56
|
+
const role = e.role ?? "unknown";
|
|
57
|
+
const text = e.text.trim();
|
|
58
|
+
if (!text)
|
|
59
|
+
continue;
|
|
60
|
+
const line = `[${role}] ${text}`;
|
|
61
|
+
if (total + line.length > maxChars)
|
|
62
|
+
break;
|
|
63
|
+
lines.push(line);
|
|
64
|
+
total += line.length + 2;
|
|
65
|
+
}
|
|
66
|
+
return lines.join("\n\n") || "(empty — no textual events)";
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the user prompt for the session-summary LLM call. Pure — no IO. The
|
|
70
|
+
* model is asked for a dense 2–4 sentence summary plus key topics, optimised
|
|
71
|
+
* for semantic search recall.
|
|
72
|
+
*/
|
|
73
|
+
export function buildSessionSummaryPrompt(data) {
|
|
74
|
+
const ref = data.ref;
|
|
75
|
+
const startedAt = isoOrUndefined(ref.startedAt) ?? "unknown";
|
|
76
|
+
const endedAt = isoOrUndefined(ref.endedAt) ?? "unknown";
|
|
77
|
+
return [
|
|
78
|
+
"You are summarizing an agent coding session so it can be found later via semantic search.",
|
|
79
|
+
"Write a DENSE 2–4 sentence summary of what was worked on, the key decisions made, and the outcomes.",
|
|
80
|
+
"Then list the concrete entities touched: files, GitHub issues/PRs, commands, concepts, and people.",
|
|
81
|
+
"Optimise the summary for recall — include the specific nouns an agent would search for.",
|
|
82
|
+
"",
|
|
83
|
+
`Harness: ${ref.harness}`,
|
|
84
|
+
`Project: ${ref.projectHint ?? "(unknown)"}`,
|
|
85
|
+
`Started: ${startedAt} Ended: ${endedAt}`,
|
|
86
|
+
`Title: ${ref.title ?? "(none)"}`,
|
|
87
|
+
"",
|
|
88
|
+
"Transcript:",
|
|
89
|
+
renderTranscriptForSummary(data.events),
|
|
90
|
+
"",
|
|
91
|
+
'Respond as JSON: {"summary": string, "key_topics": string[], "tags"?: string[]}.',
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse the session-summary LLM response into a {@link SessionSummaryResult}.
|
|
96
|
+
* Defensive: tolerates prose preamble/postamble around the JSON, and returns
|
|
97
|
+
* `undefined` when nothing usable parses (fail-open: no asset is written).
|
|
98
|
+
*/
|
|
99
|
+
export function parseSessionSummary(raw) {
|
|
100
|
+
if (!raw || raw.trim().length === 0)
|
|
101
|
+
return undefined;
|
|
102
|
+
let parsed;
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(raw);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
const start = raw.indexOf("{");
|
|
108
|
+
const end = raw.lastIndexOf("}");
|
|
109
|
+
if (start === -1 || end <= start)
|
|
110
|
+
return undefined;
|
|
111
|
+
try {
|
|
112
|
+
parsed = JSON.parse(raw.slice(start, end + 1));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!parsed || typeof parsed !== "object")
|
|
119
|
+
return undefined;
|
|
120
|
+
const obj = parsed;
|
|
121
|
+
const summary = typeof obj.summary === "string" ? obj.summary.trim() : "";
|
|
122
|
+
if (summary.length === 0)
|
|
123
|
+
return undefined;
|
|
124
|
+
const keyTopics = Array.isArray(obj.key_topics)
|
|
125
|
+
? obj.key_topics.filter((t) => typeof t === "string" && t.trim().length > 0)
|
|
126
|
+
: [];
|
|
127
|
+
const tags = Array.isArray(obj.tags)
|
|
128
|
+
? obj.tags.filter((t) => typeof t === "string" && t.trim().length > 0)
|
|
129
|
+
: undefined;
|
|
130
|
+
return { summary, keyTopics, ...(tags && tags.length > 0 ? { tags } : {}) };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Decide whether a session is long enough to index. `minDurationMinutes <= 0`
|
|
134
|
+
* disables the gate. When either timestamp is missing we DON'T gate it out —
|
|
135
|
+
* fail-open toward indexing, since a missing timestamp is not evidence of a
|
|
136
|
+
* trivial session.
|
|
137
|
+
*/
|
|
138
|
+
export function sessionMeetsDurationGate(data, minDurationMinutes) {
|
|
139
|
+
if (!Number.isFinite(minDurationMinutes) || minDurationMinutes <= 0)
|
|
140
|
+
return true;
|
|
141
|
+
const { startedAt, endedAt } = data.ref;
|
|
142
|
+
if (typeof startedAt !== "number" || typeof endedAt !== "number")
|
|
143
|
+
return true;
|
|
144
|
+
const durationMinutes = (endedAt - startedAt) / 60_000;
|
|
145
|
+
return durationMinutes >= minDurationMinutes;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build per-harness `access` instructions for reading the raw session log.
|
|
149
|
+
*
|
|
150
|
+
* Documented convention (#561, checklist item "Document `access` field
|
|
151
|
+
* convention per harness"): the string tells a downstream agent exactly how to
|
|
152
|
+
* read and parse the log at `log_path`. New harnesses fall back to a generic
|
|
153
|
+
* `cat <log_path>` hint, which is always correct for a file-backed log.
|
|
154
|
+
*/
|
|
155
|
+
export function buildSessionAccessInstructions(harness, logPath) {
|
|
156
|
+
const canonical = normalizeHarnessId(harness);
|
|
157
|
+
if (canonical === "claude") {
|
|
158
|
+
return [
|
|
159
|
+
`Read with: cat ${logPath}`,
|
|
160
|
+
`Parse messages: jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' ${logPath}`,
|
|
161
|
+
].join("\n");
|
|
162
|
+
}
|
|
163
|
+
if (canonical === "opencode") {
|
|
164
|
+
return [
|
|
165
|
+
`Read with: cat ${logPath}`,
|
|
166
|
+
`The log is opencode session storage (JSON). Inspect with: jq '.' ${logPath}`,
|
|
167
|
+
].join("\n");
|
|
168
|
+
}
|
|
169
|
+
// Generic fallback — file-backed logs are always readable with cat.
|
|
170
|
+
return `Read with: cat ${logPath}`;
|
|
171
|
+
}
|
|
172
|
+
/** ISO-8601 (UTC) from a ms-epoch, or undefined when absent/non-finite. */
|
|
173
|
+
function isoOrUndefined(ms) {
|
|
174
|
+
return typeof ms === "number" && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
|
|
175
|
+
}
|
|
176
|
+
/** Default session-name slug: `<harness>-session-<yyyy-mm-dd>-<shortId>`. */
|
|
177
|
+
export function buildSessionAssetName(harness, sessionId, startedAtMs) {
|
|
178
|
+
const canonical = normalizeHarnessId(harness);
|
|
179
|
+
const datePart = isoOrUndefined(startedAtMs)?.slice(0, 10) ?? "unknown-date";
|
|
180
|
+
const shortId = sessionId.slice(0, 8);
|
|
181
|
+
return `${canonical}-session-${datePart}-${shortId}`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Assemble the full session asset (frontmatter + `## Summary` / `## Key topics`).
|
|
185
|
+
* Pure — no IO. Returns the serialized markdown string.
|
|
186
|
+
*/
|
|
187
|
+
export function buildSessionAssetContent(data, summary) {
|
|
188
|
+
const ref = data.ref;
|
|
189
|
+
const harness = ref.harness;
|
|
190
|
+
const startedAt = isoOrUndefined(ref.startedAt);
|
|
191
|
+
const endedAt = isoOrUndefined(ref.endedAt);
|
|
192
|
+
const name = buildSessionAssetName(harness, ref.sessionId, ref.startedAt);
|
|
193
|
+
const logPath = ref.filePath;
|
|
194
|
+
const baseTags = ["session", normalizeHarnessId(harness)];
|
|
195
|
+
const extraTags = (summary.tags ?? []).filter((t) => typeof t === "string" && t.trim().length > 0);
|
|
196
|
+
const tags = Array.from(new Set([...baseTags, ...extraTags]));
|
|
197
|
+
const frontmatter = {
|
|
198
|
+
name,
|
|
199
|
+
type: "session",
|
|
200
|
+
harness,
|
|
201
|
+
session_id: ref.sessionId,
|
|
202
|
+
...(startedAt ? { started_at: startedAt } : {}),
|
|
203
|
+
...(endedAt ? { ended_at: endedAt } : {}),
|
|
204
|
+
...(ref.projectHint ? { project: ref.projectHint } : {}),
|
|
205
|
+
log_path: logPath,
|
|
206
|
+
access: buildSessionAccessInstructions(harness, logPath),
|
|
207
|
+
tags,
|
|
208
|
+
};
|
|
209
|
+
const topics = summary.keyTopics
|
|
210
|
+
.filter((t) => typeof t === "string" && t.trim().length > 0)
|
|
211
|
+
.map((t) => `- ${t.trim()}`)
|
|
212
|
+
.join("\n");
|
|
213
|
+
const body = `## Summary\n\n${summary.summary.trim()}\n\n## Key topics\n\n${topics || "- (none extracted)"}\n`;
|
|
214
|
+
// `description` is duplicated into frontmatter so the metadata pass surfaces
|
|
215
|
+
// it without re-reading the body — matches how other content types behave.
|
|
216
|
+
const content = assembleAsset({ ...frontmatter, description: summary.summary.trim() }, body);
|
|
217
|
+
return { name, frontmatter, content };
|
|
218
|
+
}
|
|
219
|
+
/** Resolve `<stash>/sessions/<harness>/<session-id>.md`. */
|
|
220
|
+
export function resolveSessionAssetPath(stashDir, harness, sessionId) {
|
|
221
|
+
const dir = TYPE_DIRS.session ?? "sessions";
|
|
222
|
+
return path.join(stashDir, dir, normalizeHarnessId(harness), `${sessionId}.md`);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Generate (via the injected summarizer) and write a session asset to the stash.
|
|
226
|
+
*
|
|
227
|
+
* FAIL-OPEN: when the summarizer returns `undefined` (disabled / no LLM /
|
|
228
|
+
* error), NOTHING is written and `{ written: false }` is returned. Any write
|
|
229
|
+
* error is swallowed by the caller — session indexing must NEVER break extract.
|
|
230
|
+
*/
|
|
231
|
+
export async function writeSessionAsset(data, stashDir, generate) {
|
|
232
|
+
const summary = await generate(data);
|
|
233
|
+
if (!summary?.summary || summary.summary.trim().length === 0) {
|
|
234
|
+
return { written: false };
|
|
235
|
+
}
|
|
236
|
+
const { content } = buildSessionAssetContent(data, summary);
|
|
237
|
+
const harness = data.ref.harness;
|
|
238
|
+
const sessionId = data.ref.sessionId;
|
|
239
|
+
const filePath = resolveSessionAssetPath(stashDir, harness, sessionId);
|
|
240
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
241
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
242
|
+
return {
|
|
243
|
+
written: true,
|
|
244
|
+
filePath,
|
|
245
|
+
ref: `session:${normalizeHarnessId(harness)}/${sessionId}`,
|
|
246
|
+
logPath: data.ref.filePath,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { BaseLinter } from "./base-linter";
|
|
5
|
+
import { BaseLinter } from "./base-linter.js";
|
|
6
6
|
/**
|
|
7
7
|
* Linter for `agents/` assets.
|
|
8
8
|
*
|
|
@@ -18,18 +18,28 @@
|
|
|
18
18
|
// will fail.
|
|
19
19
|
//
|
|
20
20
|
// Cases the contract covers (see fixture in the contract test):
|
|
21
|
-
// - existing memory / knowledge / agent / workflow / skill
|
|
21
|
+
// - existing memory / knowledge / agent / workflow / skill refs
|
|
22
22
|
// - knowledge subdirectory layout (knowledge/<category>/<slug>.md)
|
|
23
23
|
// - skill multi-file layout (skills/<slug>/SKILL.md)
|
|
24
24
|
// - memory `.derived.md` sibling
|
|
25
|
-
// - vault default vs named (.env vs <name>.env)
|
|
26
25
|
// - namespaced slugs containing `/`
|
|
26
|
+
// - env (`env/.env`, `env/<name>.env`) and secret (`secrets/<name>`) refs
|
|
27
27
|
// - non-existent refs
|
|
28
28
|
// - script type (unresolvable by design — both must return false)
|
|
29
|
+
//
|
|
30
|
+
// As of 0.9 the type alternation in `REF_RE` and the path mapping in
|
|
31
|
+
// `refToRelPath` are DERIVED FROM THE ASSET REGISTRY (`getAssetTypes()` /
|
|
32
|
+
// `resolveAssetPathFromName` in `src/core/asset/asset-spec.ts`) rather than
|
|
33
|
+
// hand-encoded, so they can no longer drift from the registry. The previously
|
|
34
|
+
// hand-listed `vault` type was removed from the registry in 0.9 (replaced by
|
|
35
|
+
// `env`); `vault:` refs are therefore no longer matched here. `env:`/`secret:`
|
|
36
|
+
// refs are now matched and path-resolved. `script` stays unresolvable and
|
|
37
|
+
// `task` keeps its legacy `.md` resolution (see refToRelPath for both).
|
|
29
38
|
// ----------------------------------------------------------------------------
|
|
30
39
|
import fs from "node:fs";
|
|
31
40
|
import path from "node:path";
|
|
32
|
-
import {
|
|
41
|
+
import { getAssetTypes, resolveAssetPathFromName, TYPE_DIRS } from "../../core/asset/asset-spec.js";
|
|
42
|
+
import { findSafeInsertionPoint } from "./markdown-insertion.js";
|
|
33
43
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
34
44
|
function formatDate(d) {
|
|
35
45
|
const y = d.getFullYear();
|
|
@@ -85,45 +95,53 @@ function checkStalePath(body) {
|
|
|
85
95
|
return null;
|
|
86
96
|
}
|
|
87
97
|
// ── missing-ref helpers ───────────────────────────────────────────────────────
|
|
88
|
-
const REF_RE = /(?:^|[\s`"'(])((agent|command|knowledge|memory|script|skill|workflow|lesson|task|wiki|vault):[^\s"'`)\]>,\n]+)/gm;
|
|
89
98
|
/**
|
|
90
|
-
*
|
|
99
|
+
* Type alternation for {@link REF_RE}, derived from the asset registry at
|
|
100
|
+
* module load so it can never drift from `ASSET_SPECS`. Longest-first ordering
|
|
101
|
+
* is defensive (no built-in type is a prefix of another, but a future custom
|
|
102
|
+
* `registerAssetType` one might be) so the alternation prefers the longest
|
|
103
|
+
* match. Regex metacharacters are escaped in case a custom type introduces one.
|
|
104
|
+
*/
|
|
105
|
+
function buildRefTypeAlternation() {
|
|
106
|
+
const types = [...getAssetTypes()].sort((a, b) => b.length - a.length);
|
|
107
|
+
return types.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
108
|
+
}
|
|
109
|
+
// Only the TYPE alternation is registry-derived; the surrounding grammar
|
|
110
|
+
// (boundary prefix, capture group, slug charset) is byte-identical to the
|
|
111
|
+
// legacy hand-written pattern. Deriving the types from `getAssetTypes()` means
|
|
112
|
+
// `env`/`secret` (added in 0.9) are now matched, and the removed `vault` type
|
|
113
|
+
// is not — both follow the registry automatically.
|
|
114
|
+
const REF_RE = new RegExp(`(?:^|[\\s\`"'(])((${buildRefTypeAlternation()}):[^\\s"'\`)\\]>,\\n]+)`, "gm");
|
|
115
|
+
/**
|
|
116
|
+
* Map from ref type to relative path pattern within stashRoot. Returns null to
|
|
117
|
+
* skip (type is unresolvable by the slug walker).
|
|
118
|
+
*
|
|
119
|
+
* Path layout is owned by the asset registry: we resolve through
|
|
120
|
+
* `resolveAssetPathFromName(type, TYPE_DIRS[type], name)` so the linter and the
|
|
121
|
+
* rest of the CLI agree on where an asset lives. Two legacy carve-outs are
|
|
122
|
+
* preserved to keep pre-0.9 behaviour byte-identical:
|
|
123
|
+
* - `script`: returns null (scripts live in nested dirs with arbitrary
|
|
124
|
+
* extensions — unresolvable by the slug-based walker, as the contract pins).
|
|
125
|
+
* - `task`: the registry stores tasks as `<id>.yml`, but the missing-ref
|
|
126
|
+
* linter has always resolved `task:` refs against `tasks/<id>.md`; that
|
|
127
|
+
* behaviour is held constant here (non-env/secret behaviour is unchanged).
|
|
91
128
|
*
|
|
92
129
|
* Exported for contract testing — see header CONTRACT block.
|
|
93
130
|
*/
|
|
94
131
|
export function refToRelPath(refType, refName) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
case "workflow":
|
|
109
|
-
return path.join("workflows", `${refName}.md`);
|
|
110
|
-
case "lesson":
|
|
111
|
-
return path.join("lessons", `${refName}.md`);
|
|
112
|
-
case "task":
|
|
113
|
-
return path.join("tasks", `${refName}.md`);
|
|
114
|
-
case "wiki":
|
|
115
|
-
return path.join("wikis", `${refName}.md`);
|
|
116
|
-
case "vault":
|
|
117
|
-
// Vaults are .env files. The canonical name "default" (or empty) maps to
|
|
118
|
-
// ".env"; any other name maps to "<name>.env". This mirrors the vault
|
|
119
|
-
// asset-spec toAssetPath logic in src/core/asset-spec.ts.
|
|
120
|
-
if (!refName || refName === "default") {
|
|
121
|
-
return path.join("vaults", ".env");
|
|
122
|
-
}
|
|
123
|
-
return path.join("vaults", `${refName}.env`);
|
|
124
|
-
default:
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
132
|
+
// script is intentionally unresolvable (contract-pinned).
|
|
133
|
+
if (refType === "script")
|
|
134
|
+
return null;
|
|
135
|
+
// Preserve the legacy `.md` resolution for tasks.
|
|
136
|
+
if (refType === "task")
|
|
137
|
+
return path.join(TYPE_DIRS.task ?? "tasks", `${refName}.md`);
|
|
138
|
+
const typeDir = TYPE_DIRS[refType];
|
|
139
|
+
if (!typeDir)
|
|
140
|
+
return null; // unknown type — skip
|
|
141
|
+
// resolveAssetPathFromName returns a path rooted at the type dir we pass in,
|
|
142
|
+
// i.e. "<typeDir>/<...>" — exactly the stash-relative path this helper has
|
|
143
|
+
// always returned.
|
|
144
|
+
return resolveAssetPathFromName(refType, typeDir, refName);
|
|
127
145
|
}
|
|
128
146
|
/**
|
|
129
147
|
* Returns true if `relPath` resolves to a real file (or multi-file directory
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { BaseLinter } from "./base-linter";
|
|
5
|
+
import { BaseLinter } from "./base-linter.js";
|
|
6
6
|
/**
|
|
7
7
|
* Linter for `commands/` assets.
|
|
8
8
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import { BaseLinter } from "./base-linter";
|
|
4
|
+
import { BaseLinter } from "./base-linter.js";
|
|
5
5
|
/**
|
|
6
6
|
* Default linter for asset types that have no type-specific rules beyond the
|
|
7
7
|
* base checks (`unquoted-colon`, `missing-updated`).
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* where the operator legitimately wants to set their editor — accept the
|
|
25
25
|
* FP and bypass with `--allow-insecure` after review.
|
|
26
26
|
*/
|
|
27
|
-
import { listKeys } from "../env";
|
|
27
|
+
import { listKeys } from "../env/env.js";
|
|
28
28
|
// ── Dangerous key set ─────────────────────────────────────────────────────────
|
|
29
29
|
export const DANGEROUS_VAULT_KEYS = new Set([
|
|
30
30
|
// Dynamic linker hijacking (Linux glibc ld.so)
|