akm-cli 0.9.0-beta.6 → 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 +663 -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 -14
- 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 +153 -147
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +76 -72
- 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 -1083
- 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 +1313 -1278
- 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 +243 -599
- 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 +55 -4
- 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 +160 -7
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +32 -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 +510 -2537
- 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 +115 -0
- 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 +212 -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 +7 -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 -882
- package/dist/commands/read/curate.js +419 -103
- 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 +29 -41
- 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 +389 -58
- 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 +21 -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 +142 -1091
- 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 +356 -780
- 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 +103 -24
- 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 +106 -0
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +291 -310
- 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 +11 -1
- 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 +57 -15
- 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 +2718 -2354
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +891 -597
- 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/tar-utils.js +16 -8
- 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
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
* LLM helper for the `akm index` memory-inference pass (#201).
|
|
6
|
+
*
|
|
7
|
+
* Compresses a single memory body into one higher-signal derived memory. The
|
|
8
|
+
* pass itself (in `src/indexer/memory-inference.ts`) is responsible for
|
|
9
|
+
* deciding which memories are pending, persisting the derived memory with the
|
|
10
|
+
* correct frontmatter (`inferred: true`, `source: <parent-ref>`), and marking
|
|
11
|
+
* the parent as processed for idempotency.
|
|
12
|
+
*
|
|
13
|
+
* This module is intentionally tiny and stateless so tests can stub it via
|
|
14
|
+
* `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
|
|
15
|
+
*
|
|
16
|
+
* Locked v1 contract (#208): the LLM connection always comes from the
|
|
17
|
+
* shared `akm.llm` block — never from a per-pass override. Callers obtain
|
|
18
|
+
* the connection via `resolveIndexPassLLM("memory", config)` and pass it
|
|
19
|
+
* straight through.
|
|
20
|
+
*/
|
|
21
|
+
import memoryInferSystemPrompt from "../assets/prompts/memory-infer-system.md" with { type: "text" };
|
|
22
|
+
import memoryInferUserPrompt from "../assets/prompts/memory-infer-user.md" with { type: "text" };
|
|
23
|
+
import { toErrorMessage } from "../core/common.js";
|
|
24
|
+
import { warn } from "../core/warn.js";
|
|
25
|
+
import { parseEmbeddedJsonResponse } from "./client.js";
|
|
26
|
+
import { callStructured } from "./structured-call.js";
|
|
27
|
+
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
28
|
+
const MAX_BODY_CHARS = 4000;
|
|
29
|
+
const SYSTEM_PROMPT = memoryInferSystemPrompt;
|
|
30
|
+
const USER_PROMPT_PREFIX = memoryInferUserPrompt;
|
|
31
|
+
/**
|
|
32
|
+
* Strict JSON Schema for the derived-memory payload. Sent to providers that
|
|
33
|
+
* opt in via `LlmConnectionConfig.supportsJsonSchema = true`; the client
|
|
34
|
+
* silently drops the schema for providers that don't.
|
|
35
|
+
*
|
|
36
|
+
* Extends the responseSchema lift (PR 1, asset-writers-investigation §5) to
|
|
37
|
+
* the memory-inference path. Mirrors the validation gate below
|
|
38
|
+
* (title/description/content + non-empty tags/searchHints) so a
|
|
39
|
+
* schema-compliant response is guaranteed to pass the downstream check
|
|
40
|
+
* — no more "incomplete derived memory payload from LLM; skipping memory"
|
|
41
|
+
* for shape-only failures.
|
|
42
|
+
*/
|
|
43
|
+
const DERIVED_MEMORY_JSON_SCHEMA = {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
title: { type: "string", minLength: 1 },
|
|
47
|
+
description: { type: "string", minLength: 1 },
|
|
48
|
+
content: { type: "string", minLength: 1 },
|
|
49
|
+
tags: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 8 },
|
|
50
|
+
searchHints: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 6 },
|
|
51
|
+
},
|
|
52
|
+
required: ["title", "description", "content", "tags", "searchHints"],
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Compress a single memory body into one derived memory via the configured LLM.
|
|
57
|
+
*
|
|
58
|
+
* Returns `undefined` on any failure (timeout, invalid JSON, empty response).
|
|
59
|
+
* Errors are logged via `warn()` but never thrown — a failed split for one memory
|
|
60
|
+
* must not abort the rest of the index pass.
|
|
61
|
+
*
|
|
62
|
+
* Routes through `callStructured({ feature: "memory_inference", ... })` so the
|
|
63
|
+
* feature gate, error classification, and onFallback hook are honoured uniformly
|
|
64
|
+
* (Fix C5).
|
|
65
|
+
*/
|
|
66
|
+
export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt) {
|
|
67
|
+
const trimmedBody = body.trim();
|
|
68
|
+
if (!trimmedBody)
|
|
69
|
+
return undefined;
|
|
70
|
+
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
71
|
+
// Memory-inference is ALWAYS gated: no `akmConfig` ⇒ gate closed (no chat,
|
|
72
|
+
// `disabled` fallback), never the seam's ungated/propagate path (which is for
|
|
73
|
+
// direct callers like `enhanceMetadata`). This is the gate-closed branch
|
|
74
|
+
// `tryLlmFeature(_, undefined, _)` took before the migration.
|
|
75
|
+
if (!akmConfig) {
|
|
76
|
+
onFallback?.({ feature: "memory_inference", reason: "disabled" });
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
return callStructured({
|
|
80
|
+
feature: "memory_inference",
|
|
81
|
+
akmConfig,
|
|
82
|
+
config: llmConfig,
|
|
83
|
+
messages: [
|
|
84
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
85
|
+
{ role: "user", content: userPrompt },
|
|
86
|
+
],
|
|
87
|
+
request: {
|
|
88
|
+
temperature: 0.1,
|
|
89
|
+
timeoutMs: llmConfig.timeoutMs,
|
|
90
|
+
signal,
|
|
91
|
+
responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
|
|
92
|
+
onRetryAttempt,
|
|
93
|
+
},
|
|
94
|
+
parse: (raw) => {
|
|
95
|
+
if (!raw)
|
|
96
|
+
return undefined;
|
|
97
|
+
const parsed = parseEmbeddedJsonResponse(raw);
|
|
98
|
+
if (!parsed) {
|
|
99
|
+
warn("memory inference: invalid JSON response from LLM; skipping memory.");
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
|
|
103
|
+
const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
|
|
104
|
+
const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
|
|
105
|
+
const tags = Array.isArray(parsed.tags)
|
|
106
|
+
? parsed.tags
|
|
107
|
+
.filter((t) => typeof t === "string")
|
|
108
|
+
.map((t) => t.trim())
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.slice(0, 8)
|
|
111
|
+
: [];
|
|
112
|
+
const searchHints = Array.isArray(parsed.searchHints)
|
|
113
|
+
? parsed.searchHints
|
|
114
|
+
.filter((h) => typeof h === "string")
|
|
115
|
+
.map((h) => h.trim())
|
|
116
|
+
.filter(Boolean)
|
|
117
|
+
.slice(0, 6)
|
|
118
|
+
: [];
|
|
119
|
+
if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
|
|
120
|
+
warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
return { title, description, tags, searchHints, content };
|
|
124
|
+
},
|
|
125
|
+
onError: (cls, err) => {
|
|
126
|
+
if (cls === "html") {
|
|
127
|
+
if (telemetry)
|
|
128
|
+
telemetry.htmlErrorCount = (telemetry.htmlErrorCount ?? 0) + 1;
|
|
129
|
+
warn(`memory inference: provider returned HTML instead of JSON; skipping memory: ${toErrorMessage(err)}`);
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
133
|
+
return undefined;
|
|
134
|
+
},
|
|
135
|
+
fallback: undefined,
|
|
136
|
+
onFallback,
|
|
137
|
+
});
|
|
138
|
+
}
|
package/dist/llm/memory-infer.js
CHANGED
|
@@ -1,130 +1,4 @@
|
|
|
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
|
-
|
|
5
|
-
* LLM helper for the `akm index` memory-inference pass (#201).
|
|
6
|
-
*
|
|
7
|
-
* Compresses a single memory body into one higher-signal derived memory. The
|
|
8
|
-
* pass itself (in `src/indexer/memory-inference.ts`) is responsible for
|
|
9
|
-
* deciding which memories are pending, persisting the derived memory with the
|
|
10
|
-
* correct frontmatter (`inferred: true`, `source: <parent-ref>`), and marking
|
|
11
|
-
* the parent as processed for idempotency.
|
|
12
|
-
*
|
|
13
|
-
* This module is intentionally tiny and stateless so tests can stub it via
|
|
14
|
-
* `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
|
|
15
|
-
*
|
|
16
|
-
* Locked v1 contract (#208): the LLM connection always comes from the
|
|
17
|
-
* shared `akm.llm` block — never from a per-pass override. Callers obtain
|
|
18
|
-
* the connection via `resolveIndexPassLLM("memory", config)` and pass it
|
|
19
|
-
* straight through.
|
|
20
|
-
*/
|
|
21
|
-
import { toErrorMessage } from "../core/common.js";
|
|
22
|
-
import { warn } from "../core/warn.js";
|
|
23
|
-
import { chatCompletion, LlmCallError, parseEmbeddedJsonResponse } from "./client.js";
|
|
24
|
-
import { tryLlmFeature } from "./feature-gate.js";
|
|
25
|
-
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
26
|
-
const MAX_BODY_CHARS = 4000;
|
|
27
|
-
const SYSTEM_PROMPT = "You compress a developer memory into one high-signal derived memory for later retrieval. " +
|
|
28
|
-
"Return only valid JSON. No prose outside the JSON object. No markdown fences.";
|
|
29
|
-
const USER_PROMPT_PREFIX = `Compress the memory below into one derived memory. Output ONLY JSON:
|
|
30
|
-
{"title":"short title string","description":"one sentence summary string","tags":["tag1","tag2"],"searchHints":["search phrase 1","search phrase 2"],"content":"2-3 sentence compressed body preserving key facts verbatim"}
|
|
31
|
-
Rules: be specific, no vague generalizations, preserve key facts (names/versions/paths/config keys verbatim), merge related points, 3-8 tags, 3-6 searchHints. The content field must be a plain string with 2-3 sentences.
|
|
32
|
-
|
|
33
|
-
Memory:
|
|
34
|
-
`;
|
|
35
|
-
/**
|
|
36
|
-
* Strict JSON Schema for the derived-memory payload. Sent to providers that
|
|
37
|
-
* opt in via `LlmConnectionConfig.supportsJsonSchema = true`; the client
|
|
38
|
-
* silently drops the schema for providers that don't.
|
|
39
|
-
*
|
|
40
|
-
* Extends the responseSchema lift (PR 1, asset-writers-investigation §5) to
|
|
41
|
-
* the memory-inference path. Mirrors the validation gate below
|
|
42
|
-
* (title/description/content + non-empty tags/searchHints) so a
|
|
43
|
-
* schema-compliant response is guaranteed to pass the downstream check
|
|
44
|
-
* — no more "incomplete derived memory payload from LLM; skipping memory"
|
|
45
|
-
* for shape-only failures.
|
|
46
|
-
*/
|
|
47
|
-
const DERIVED_MEMORY_JSON_SCHEMA = {
|
|
48
|
-
type: "object",
|
|
49
|
-
properties: {
|
|
50
|
-
title: { type: "string", minLength: 1 },
|
|
51
|
-
description: { type: "string", minLength: 1 },
|
|
52
|
-
content: { type: "string", minLength: 1 },
|
|
53
|
-
tags: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 8 },
|
|
54
|
-
searchHints: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 6 },
|
|
55
|
-
},
|
|
56
|
-
required: ["title", "description", "content", "tags", "searchHints"],
|
|
57
|
-
additionalProperties: false,
|
|
58
|
-
};
|
|
59
|
-
/**
|
|
60
|
-
* Compress a single memory body into one derived memory via the configured LLM.
|
|
61
|
-
*
|
|
62
|
-
* Returns `undefined` on any failure (timeout, invalid JSON, empty response).
|
|
63
|
-
* Errors are logged via `warn()` but never thrown — a failed split for one memory
|
|
64
|
-
* must not abort the rest of the index pass.
|
|
65
|
-
*
|
|
66
|
-
* Routes through `tryLlmFeature("memory_inference", ...)` so the feature gate
|
|
67
|
-
* and onFallback hook are honoured uniformly (Fix C5).
|
|
68
|
-
*/
|
|
69
|
-
export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt) {
|
|
70
|
-
const trimmedBody = body.trim();
|
|
71
|
-
if (!trimmedBody)
|
|
72
|
-
return undefined;
|
|
73
|
-
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
74
|
-
return tryLlmFeature("memory_inference", akmConfig, async () => {
|
|
75
|
-
try {
|
|
76
|
-
const raw = await chatCompletion(llmConfig, [
|
|
77
|
-
{ role: "system", content: SYSTEM_PROMPT },
|
|
78
|
-
{ role: "user", content: userPrompt },
|
|
79
|
-
], {
|
|
80
|
-
temperature: 0.1,
|
|
81
|
-
timeoutMs: llmConfig.timeoutMs,
|
|
82
|
-
signal,
|
|
83
|
-
responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
|
|
84
|
-
onRetryAttempt,
|
|
85
|
-
});
|
|
86
|
-
if (!raw)
|
|
87
|
-
return undefined;
|
|
88
|
-
const parsed = parseEmbeddedJsonResponse(raw);
|
|
89
|
-
if (!parsed) {
|
|
90
|
-
warn("memory inference: invalid JSON response from LLM; skipping memory.");
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
|
|
94
|
-
const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
|
|
95
|
-
const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
|
|
96
|
-
const tags = Array.isArray(parsed.tags)
|
|
97
|
-
? parsed.tags
|
|
98
|
-
.filter((t) => typeof t === "string")
|
|
99
|
-
.map((t) => t.trim())
|
|
100
|
-
.filter(Boolean)
|
|
101
|
-
.slice(0, 8)
|
|
102
|
-
: [];
|
|
103
|
-
const searchHints = Array.isArray(parsed.searchHints)
|
|
104
|
-
? parsed.searchHints
|
|
105
|
-
.filter((h) => typeof h === "string")
|
|
106
|
-
.map((h) => h.trim())
|
|
107
|
-
.filter(Boolean)
|
|
108
|
-
.slice(0, 6)
|
|
109
|
-
: [];
|
|
110
|
-
if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
|
|
111
|
-
warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
return { title, description, tags, searchHints, content };
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
if (err instanceof LlmCallError && err.code === "provider_html_error") {
|
|
118
|
-
if (telemetry)
|
|
119
|
-
telemetry.htmlErrorCount = (telemetry.htmlErrorCount ?? 0) + 1;
|
|
120
|
-
warn(`memory inference: provider returned HTML instead of JSON; skipping memory: ${toErrorMessage(err)}`);
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
}, undefined, {
|
|
127
|
-
timeoutMs: llmConfig.timeoutMs,
|
|
128
|
-
onFallback,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
4
|
+
export { compressMemoryToDerivedMemory, } from "./memory-infer-impl.js";
|
|
@@ -1,9 +1,17 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* LLM-driven metadata enhancement for stash entries.
|
|
6
|
+
*
|
|
7
|
+
* Split out of `llm.ts` so the higher-level workflow (prompting the LLM to
|
|
8
|
+
* improve descriptions/tags/searchHints) lives separately from the low-level
|
|
9
|
+
* transport client in `client.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import metadataEnhanceSystemPrompt from "../assets/prompts/metadata-enhance-system.md" with { type: "text" };
|
|
12
|
+
import { parseJsonResponse } from "./client.js";
|
|
13
|
+
import { callStructured } from "./structured-call.js";
|
|
14
|
+
const SYSTEM_PROMPT = metadataEnhanceSystemPrompt;
|
|
7
15
|
/**
|
|
8
16
|
* Use an LLM to enhance a stash entry's metadata: improve description,
|
|
9
17
|
* generate searchHints, and suggest tags.
|
|
@@ -33,34 +41,39 @@ Generate improved metadata for this ${entry.type}. Return JSON with these fields
|
|
|
33
41
|
- "tags": an array of 3-8 relevant keyword tags
|
|
34
42
|
|
|
35
43
|
Return ONLY the JSON object, no explanation.`;
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
// `parse` owns the raw response: the `!raw`/unparseable case ⇒ `{}`, plus the
|
|
45
|
+
// description/searchHints/tags shaping. `enhanceMetadata` never warns and
|
|
46
|
+
// never bumps telemetry, so `onError` (gated path only) just swallows to `{}`
|
|
47
|
+
// — identical to the surrounding control flow's pre-migration behaviour. The
|
|
48
|
+
// ungated path (akmConfig === undefined) propagates errors via callStructured.
|
|
49
|
+
return callStructured({
|
|
50
|
+
feature: "metadata_enhance",
|
|
51
|
+
akmConfig,
|
|
52
|
+
config,
|
|
53
|
+
messages: [
|
|
38
54
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
39
55
|
{ role: "user", content: userPrompt },
|
|
40
|
-
],
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
result
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return runLlm();
|
|
64
|
-
}
|
|
65
|
-
return tryLlmFeature("metadata_enhance", akmConfig, runLlm, {}, { timeoutMs: config.timeoutMs });
|
|
56
|
+
],
|
|
57
|
+
request: { signal, timeoutMs: config.timeoutMs },
|
|
58
|
+
parse: (raw) => {
|
|
59
|
+
const parsed = raw ? parseJsonResponse(raw) : undefined;
|
|
60
|
+
if (!parsed)
|
|
61
|
+
return {};
|
|
62
|
+
const result = {};
|
|
63
|
+
if (typeof parsed.description === "string" && parsed.description) {
|
|
64
|
+
result.description = parsed.description;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(parsed.searchHints)) {
|
|
67
|
+
result.searchHints = parsed.searchHints
|
|
68
|
+
.filter((s) => typeof s === "string" && s.trim().length > 0)
|
|
69
|
+
.slice(0, 8);
|
|
70
|
+
}
|
|
71
|
+
if (Array.isArray(parsed.tags)) {
|
|
72
|
+
result.tags = parsed.tags.filter((s) => typeof s === "string" && s.trim().length > 0).slice(0, 10);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
},
|
|
76
|
+
onError: () => ({}),
|
|
77
|
+
fallback: {},
|
|
78
|
+
});
|
|
66
79
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
import { chatCompletion, isContextSizeError, LlmCallError } from "./client.js";
|
|
5
|
+
import { tryLlmFeature } from "./feature-gate.js";
|
|
6
|
+
/**
|
|
7
|
+
* Classify a thrown LLM error into one of the three buckets. This is the single
|
|
8
|
+
* home for the `isContextSizeError -> html -> other` ladder that was previously
|
|
9
|
+
* inlined at every call site.
|
|
10
|
+
*/
|
|
11
|
+
export function classifyLlmError(err) {
|
|
12
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13
|
+
if (isContextSizeError(message))
|
|
14
|
+
return "context_limit";
|
|
15
|
+
if (err instanceof LlmCallError && err.code === "provider_html_error")
|
|
16
|
+
return "html";
|
|
17
|
+
return "other";
|
|
18
|
+
}
|
|
19
|
+
export async function callStructured(opts) {
|
|
20
|
+
const { feature, akmConfig, config, messages, request, parse, onError, fallback, onFallback } = opts;
|
|
21
|
+
const chat = request?.chat ?? chatCompletion;
|
|
22
|
+
const chatOptions = {
|
|
23
|
+
temperature: request?.temperature,
|
|
24
|
+
timeoutMs: request?.timeoutMs,
|
|
25
|
+
signal: request?.signal,
|
|
26
|
+
responseSchema: request?.responseSchema,
|
|
27
|
+
onRetryAttempt: request?.onRetryAttempt,
|
|
28
|
+
};
|
|
29
|
+
// UNGATED: run the chat+parse directly. Errors propagate — no `onError`
|
|
30
|
+
// funnel — matching the pre-gate behaviour of direct callers.
|
|
31
|
+
if (akmConfig === undefined) {
|
|
32
|
+
const raw = await chat(config, messages, chatOptions);
|
|
33
|
+
return parse(raw);
|
|
34
|
+
}
|
|
35
|
+
// GATED: run through `tryLlmFeature`. A throw inside is classified ONCE and
|
|
36
|
+
// routed to `onError`; `tryLlmFeature` returns `fallback` on disablement/timeout.
|
|
37
|
+
return tryLlmFeature(feature, akmConfig, async () => {
|
|
38
|
+
try {
|
|
39
|
+
const raw = await chat(config, messages, chatOptions);
|
|
40
|
+
return parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return onError(classifyLlmError(err), err);
|
|
44
|
+
}
|
|
45
|
+
}, fallback, {
|
|
46
|
+
timeoutMs: request?.timeoutMs,
|
|
47
|
+
onFallback,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
|
|
6
|
+
const { main } = await import("./scripts/migrate-storage.js");
|
|
7
|
+
|
|
8
|
+
await main();
|
package/dist/output/context.js
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* Initialized from `cli.ts` before `runMain`.
|
|
13
13
|
*/
|
|
14
14
|
import { UsageError } from "../core/errors.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl", "md", "html"];
|
|
16
|
+
const DETAIL_LEVELS = ["brief", "normal", "full"];
|
|
17
|
+
const SHAPE_MODES = ["human", "agent", "summary"];
|
|
18
|
+
function parseOutputFormat(value) {
|
|
19
19
|
if (!value)
|
|
20
20
|
return undefined;
|
|
21
21
|
if (OUTPUT_FORMATS.includes(value))
|
|
@@ -29,7 +29,7 @@ export function parseDetailLevel(value) {
|
|
|
29
29
|
return value;
|
|
30
30
|
throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}`, "INVALID_DETAIL_VALUE");
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
function parseShapeMode(value) {
|
|
33
33
|
if (!value)
|
|
34
34
|
return undefined;
|
|
35
35
|
if (SHAPE_MODES.includes(value))
|
package/dist/output/renderers.js
CHANGED
|
@@ -210,6 +210,10 @@ const agentMdRenderer = {
|
|
|
210
210
|
action: "Dispatch using the prompt below verbatim. Use modelHint and toolPolicy if present.",
|
|
211
211
|
description: asNonEmptyString(parsedMd.data.description),
|
|
212
212
|
prompt: parsedMd.content,
|
|
213
|
+
// `tools` is self-declared frontmatter. The provenance CEILING that decides
|
|
214
|
+
// whether this self-declared policy is honoured is applied at the show
|
|
215
|
+
// layer (`akmShowUnified`), which knows whether the source is the operator's
|
|
216
|
+
// own writable stash vs a read-only third-party source (07 P1-D).
|
|
213
217
|
toolPolicy: parsedMd.data.tools,
|
|
214
218
|
modelHint: typeof parsedMd.data.model === "string" ? parsedMd.data.model : undefined,
|
|
215
219
|
};
|
|
@@ -364,23 +368,21 @@ const scriptSourceRenderer = {
|
|
|
364
368
|
};
|
|
365
369
|
// ── 8. env-file ───────────────────────────────────────────────────────────────
|
|
366
370
|
/**
|
|
367
|
-
* Env renderer. Returns ONLY key names
|
|
368
|
-
*
|
|
369
|
-
* through `akm show`.
|
|
371
|
+
* Env renderer. Returns ONLY key names — never values, and never comment
|
|
372
|
+
* text (comments routinely contain commented-out credentials). Deliberately
|
|
373
|
+
* omits content/template/prompt so env values cannot leak through `akm show`.
|
|
370
374
|
*/
|
|
371
375
|
const envFileRenderer = {
|
|
372
376
|
name: "env-file",
|
|
373
377
|
buildShowResponse(ctx) {
|
|
374
378
|
const name = deriveName(ctx);
|
|
375
|
-
const { keys
|
|
379
|
+
const { keys } = listVaultKeys(ctx.absPath);
|
|
376
380
|
return {
|
|
377
381
|
type: "env",
|
|
378
382
|
name,
|
|
379
383
|
path: ctx.absPath,
|
|
380
|
-
action: "Environment —
|
|
381
|
-
description: comments.length > 0 ? comments.join("\n") : undefined,
|
|
384
|
+
action: "Environment — key names only. Use `akm env run <ref> -- <command>` to run with the whole .env injected; prefer `--clean` to minimize inherited parent env. AKM itself does not print values, but child stdout/stderr is not redacted. `akm env export <ref> --out <file>` writes a sourceable script to a file. Never `source` the raw file. Values stay on disk and are never written to akm's stdout.",
|
|
382
385
|
keys,
|
|
383
|
-
comments,
|
|
384
386
|
};
|
|
385
387
|
},
|
|
386
388
|
enrichSearchHit(hit, _stashDir) {
|
|
@@ -468,6 +470,44 @@ const sessionMdRenderer = {
|
|
|
468
470
|
};
|
|
469
471
|
},
|
|
470
472
|
};
|
|
473
|
+
// ── 9. fact-md ───────────────────────────────────────────────────────────────
|
|
474
|
+
/**
|
|
475
|
+
* Renderer for the `fact` asset type. A fact is durable stash-level semantic
|
|
476
|
+
* knowledge (personal/team/project details, coding conventions, stash-meta).
|
|
477
|
+
* It carries `category` (personal|team|project|convention|meta) and an
|
|
478
|
+
* optional `pinned` flag marking it as part of the always-injected core. The
|
|
479
|
+
* renderer surfaces a one-liner (category + pinned marker) so an agent can tell
|
|
480
|
+
* at a glance what kind of fact it is and whether it is core context.
|
|
481
|
+
*/
|
|
482
|
+
const factMdRenderer = {
|
|
483
|
+
name: "fact-md",
|
|
484
|
+
buildShowResponse(ctx) {
|
|
485
|
+
const name = deriveName(ctx);
|
|
486
|
+
const parsed = parseFrontmatter(ctx.content());
|
|
487
|
+
const fm = parsed.data;
|
|
488
|
+
const category = asNonEmptyString(fm.category);
|
|
489
|
+
const description = asNonEmptyString(fm.description);
|
|
490
|
+
const pinned = fm.pinned === true;
|
|
491
|
+
const headerParts = [
|
|
492
|
+
category ? `category: ${category}` : undefined,
|
|
493
|
+
pinned ? "pinned (core context)" : undefined,
|
|
494
|
+
].filter((p) => !!p);
|
|
495
|
+
const action = [
|
|
496
|
+
"Durable stash fact — apply it as background context.",
|
|
497
|
+
headerParts.length > 0 ? headerParts.join(" ") : undefined,
|
|
498
|
+
]
|
|
499
|
+
.filter((p) => !!p)
|
|
500
|
+
.join("\n");
|
|
501
|
+
return {
|
|
502
|
+
type: "fact",
|
|
503
|
+
name,
|
|
504
|
+
path: ctx.absPath,
|
|
505
|
+
action,
|
|
506
|
+
description,
|
|
507
|
+
content: parsed.content,
|
|
508
|
+
};
|
|
509
|
+
},
|
|
510
|
+
};
|
|
471
511
|
function applySessionMetadata(entry, ctx) {
|
|
472
512
|
try {
|
|
473
513
|
const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
|
|
@@ -501,6 +541,34 @@ function applyTocMetadata(entry, ctx) {
|
|
|
501
541
|
// Non-fatal: skip TOC if file can't be read
|
|
502
542
|
}
|
|
503
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Fact metadata: surface `category` and the `pinned` core marker as tags +
|
|
546
|
+
* search hints (no dedicated DB columns — same encoding pattern as session /
|
|
547
|
+
* task). `pinned` is mirrored to both a `pinned` tag and a `pinned` search
|
|
548
|
+
* hint so the ranking contributor can detect it and queries can target it.
|
|
549
|
+
*/
|
|
550
|
+
function applyFactMetadata(entry, ctx) {
|
|
551
|
+
try {
|
|
552
|
+
const fm = applyFrontmatterDescriptionAndTags(entry, ctx);
|
|
553
|
+
const tags = new Set([...(entry.tags ?? []), "fact"]);
|
|
554
|
+
const hints = new Set(entry.searchHints ?? []);
|
|
555
|
+
const category = asNonEmptyString(fm.category);
|
|
556
|
+
if (category) {
|
|
557
|
+
tags.add(category);
|
|
558
|
+
hints.add(`category:${category}`);
|
|
559
|
+
}
|
|
560
|
+
if (fm.pinned === true) {
|
|
561
|
+
tags.add("pinned");
|
|
562
|
+
hints.add("pinned");
|
|
563
|
+
}
|
|
564
|
+
entry.tags = Array.from(tags).filter(Boolean);
|
|
565
|
+
if (hints.size > 0)
|
|
566
|
+
entry.searchHints = Array.from(hints).filter(Boolean);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// Non-fatal: skip metadata extraction on parse error
|
|
570
|
+
}
|
|
571
|
+
}
|
|
504
572
|
/**
|
|
505
573
|
* Parse frontmatter, apply description (if not already set) and merge tags
|
|
506
574
|
* into `entry`. Returns the raw frontmatter data object so callers can access
|
|
@@ -581,12 +649,9 @@ function applyScriptMetadata(entry, ctx) {
|
|
|
581
649
|
}
|
|
582
650
|
}
|
|
583
651
|
function applyEnvMetadata(entry, ctx) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
entry.source = "comments";
|
|
588
|
-
entry.confidence = 0.7;
|
|
589
|
-
}
|
|
652
|
+
// Key names only — comment text must never reach description/search_text
|
|
653
|
+
// (comments routinely contain commented-out credentials).
|
|
654
|
+
const { keys } = listVaultKeys(ctx.absPath);
|
|
590
655
|
if (keys.length > 0) {
|
|
591
656
|
entry.searchHints = keys;
|
|
592
657
|
}
|
|
@@ -660,6 +725,11 @@ registerMetadataContributor({
|
|
|
660
725
|
appliesTo: ({ rendererName }) => rendererName === "session-md",
|
|
661
726
|
contribute: (entry, ctx) => applySessionMetadata(entry, ctx.renderContext),
|
|
662
727
|
});
|
|
728
|
+
registerMetadataContributor({
|
|
729
|
+
name: "fact-md-metadata",
|
|
730
|
+
appliesTo: ({ rendererName }) => rendererName === "fact-md",
|
|
731
|
+
contribute: (entry, ctx) => applyFactMetadata(entry, ctx.renderContext),
|
|
732
|
+
});
|
|
663
733
|
// ── Registration ─────────────────────────────────────────────────────────────
|
|
664
734
|
/** All built-in renderers. */
|
|
665
735
|
const builtinRenderers = [
|
|
@@ -676,6 +746,7 @@ const builtinRenderers = [
|
|
|
676
746
|
secretFileRenderer,
|
|
677
747
|
taskMdRenderer,
|
|
678
748
|
sessionMdRenderer,
|
|
749
|
+
factMdRenderer,
|
|
679
750
|
];
|
|
680
751
|
/**
|
|
681
752
|
* Register all built-in renderers with the file-context registry.
|
|
@@ -687,4 +758,4 @@ export function registerBuiltinRenderers() {
|
|
|
687
758
|
}
|
|
688
759
|
}
|
|
689
760
|
// ── Named exports for testing ────────────────────────────────────────────────
|
|
690
|
-
export { agentMdRenderer, commandMdRenderer, envFileRenderer, INTERPRETER_MAP, knowledgeMdRenderer, lessonMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, secretFileRenderer, skillMdRenderer, wikiMdRenderer, workflowMdRenderer, };
|
|
761
|
+
export { agentMdRenderer, commandMdRenderer, envFileRenderer, factMdRenderer, INTERPRETER_MAP, knowledgeMdRenderer, lessonMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, secretFileRenderer, skillMdRenderer, wikiMdRenderer, workflowMdRenderer, };
|
|
@@ -5,7 +5,7 @@ import { capDescription, NORMAL_DESCRIPTION_LIMIT, pickFields } from "./helpers.
|
|
|
5
5
|
// Curation is a small, high-signal top-N. Even at `brief` we keep `followUp`
|
|
6
6
|
// (the actionable `akm show <ref>` command) and `reason` (why this asset was
|
|
7
7
|
// selected) — these are the point of curate, unlike a bulk search listing.
|
|
8
|
-
const BRIEF_FIELDS = ["source", "type", "name", "ref", "id", "followUp", "reason"];
|
|
8
|
+
const BRIEF_FIELDS = ["source", "type", "name", "ref", "id", "supportRefs", "followUp", "reason"];
|
|
9
9
|
const NORMAL_FIELDS = [
|
|
10
10
|
"source",
|
|
11
11
|
"type",
|
|
@@ -17,12 +17,24 @@ const NORMAL_FIELDS = [
|
|
|
17
17
|
"keys",
|
|
18
18
|
"parameters",
|
|
19
19
|
"run",
|
|
20
|
+
"supportRefs",
|
|
20
21
|
"followUp",
|
|
21
22
|
"reason",
|
|
22
23
|
"score",
|
|
23
24
|
];
|
|
24
25
|
// Agent shape: the minimal field set an LLM needs to decide and act.
|
|
25
|
-
const AGENT_FIELDS = [
|
|
26
|
+
const AGENT_FIELDS = [
|
|
27
|
+
"source",
|
|
28
|
+
"type",
|
|
29
|
+
"name",
|
|
30
|
+
"ref",
|
|
31
|
+
"id",
|
|
32
|
+
"description",
|
|
33
|
+
"supportRefs",
|
|
34
|
+
"followUp",
|
|
35
|
+
"reason",
|
|
36
|
+
"score",
|
|
37
|
+
];
|
|
26
38
|
function shapeCurateItem(item, detail, shape) {
|
|
27
39
|
if (shape === "agent") {
|
|
28
40
|
return capDescription(pickFields(item, AGENT_FIELDS), NORMAL_DESCRIPTION_LIMIT);
|
|
@@ -369,7 +369,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
369
369
|
"workflowParameters",
|
|
370
370
|
"steps",
|
|
371
371
|
"keys",
|
|
372
|
-
"comments",
|
|
373
372
|
"related",
|
|
374
373
|
]);
|
|
375
374
|
}
|
|
@@ -385,7 +384,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
385
384
|
"run",
|
|
386
385
|
"origin",
|
|
387
386
|
"keys",
|
|
388
|
-
"comments",
|
|
389
387
|
"related",
|
|
390
388
|
]);
|
|
391
389
|
}
|
|
@@ -411,7 +409,6 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
411
409
|
"cwd",
|
|
412
410
|
"activeRun",
|
|
413
411
|
"keys",
|
|
414
|
-
"comments",
|
|
415
412
|
"related",
|
|
416
413
|
// path and editable are always projected so JSON consumers can locate and
|
|
417
414
|
// edit the asset without needing --detail full (QA #7).
|