akm-cli 0.7.4 → 0.8.0-rc.10
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 +224 -1
- package/README.md +22 -6
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2631 -1440
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +2122 -0
- package/dist/commands/curate.js +45 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1081 -73
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +15 -24
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +3 -0
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +120 -45
- package/dist/commands/reflect.js +1104 -60
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +70 -7
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +158 -60
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +364 -968
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +105 -135
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +198 -489
- package/dist/indexer/db.js +990 -108
- package/dist/indexer/ensure-index.js +136 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -114
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +547 -309
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +6 -17
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +250 -36
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +183 -35
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +79 -88
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -72
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +80 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -311
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +306 -258
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -511
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1093
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +179 -20
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +227 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +141 -2
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +91 -89
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +79 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +20 -0
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +29 -11
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -333
- package/dist/templates/wiki-templates.js +0 -100
package/dist/llm/index-passes.js
CHANGED
|
@@ -1,35 +1,50 @@
|
|
|
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 { getDefaultLlmConfig, getIndexPassConfig } from "../core/config";
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Every LLM-using pass inside `akm index` defaults to that block.
|
|
7
|
-
* - A pass can be opted out individually with `index.<passName>.llm = false`.
|
|
8
|
-
* - Any attempt to supply provider/model fields under `index.<passName>` is
|
|
9
|
-
* rejected at config-load time by `parseIndexConfig` in
|
|
10
|
-
* {@link ../core/config.ts} (`ConfigError("INVALID_CONFIG_FILE")`).
|
|
11
|
-
*
|
|
12
|
-
* Passes plug in by calling {@link resolveIndexPassLLM} with their pass
|
|
13
|
-
* name (e.g. `"memory"` for #201's memory-inference pass, `"graph"` for
|
|
14
|
-
* #207's graph-extraction pass). They do not read `config.llm` directly.
|
|
15
|
-
* This keeps the config surface small and the wiring uniform.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* Resolve the {@link LlmConnectionConfig} a single index pass should use, or
|
|
19
|
-
* `undefined` when the pass should run without an LLM.
|
|
20
|
-
*
|
|
21
|
-
* Returns `undefined` if any of:
|
|
22
|
-
* - No top-level `akm.llm` block is configured.
|
|
23
|
-
* - The pass is explicitly opted out (`index.<passName>.llm === false`).
|
|
24
|
-
*
|
|
25
|
-
* Otherwise returns the shared `akm.llm` config. There is no per-pass
|
|
26
|
-
* provider override; that decision is locked by §9 of the v1 spec.
|
|
6
|
+
* Map a pass name (as used by callers — "memory", "graph", etc.) to the
|
|
7
|
+
* matching key under `profiles.improve.default.processes`. Pass names with
|
|
8
|
+
* no improve-profile counterpart return undefined and resolve via the
|
|
9
|
+
* default LLM only.
|
|
27
10
|
*/
|
|
11
|
+
function improveProcessKeyForPass(passName) {
|
|
12
|
+
switch (passName) {
|
|
13
|
+
case "memory":
|
|
14
|
+
return "memoryInference";
|
|
15
|
+
case "graph":
|
|
16
|
+
return "graphExtraction";
|
|
17
|
+
default:
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
28
21
|
export function resolveIndexPassLLM(passName, config) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const passConfig = config.index?.[passName];
|
|
22
|
+
// Gate 1 — explicit opt-out via the index-config block stays authoritative.
|
|
23
|
+
const passConfig = getIndexPassConfig(config.index, passName);
|
|
32
24
|
if (passConfig?.llm === false)
|
|
33
25
|
return undefined;
|
|
34
|
-
|
|
26
|
+
// Gate 2 — per-process profile from the improve profile, when present.
|
|
27
|
+
// This is the path that lets
|
|
28
|
+
// profiles.improve.default.processes.graphExtraction.profile = "ministral-3b"
|
|
29
|
+
// actually take effect on the graph pass instead of being silently
|
|
30
|
+
// ignored.
|
|
31
|
+
const processKey = improveProcessKeyForPass(passName);
|
|
32
|
+
if (processKey) {
|
|
33
|
+
const processConfig = config.profiles?.improve?.default?.processes?.[processKey];
|
|
34
|
+
// Honor enabled === false here too — an explicit disable wins.
|
|
35
|
+
if (processConfig?.enabled === false)
|
|
36
|
+
return undefined;
|
|
37
|
+
const profileName = processConfig?.profile;
|
|
38
|
+
if (profileName) {
|
|
39
|
+
const profile = config.profiles?.llm?.[profileName];
|
|
40
|
+
if (profile)
|
|
41
|
+
return profile;
|
|
42
|
+
// A named-but-missing profile is a configuration error in spirit, but
|
|
43
|
+
// we fall through to default rather than throwing — callers gracefully
|
|
44
|
+
// treat `undefined` as "pass disabled" and emitting a hard throw here
|
|
45
|
+
// would take the whole improve run down on a typo.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Gate 3 — fall back to the default LLM profile.
|
|
49
|
+
return getDefaultLlmConfig(config);
|
|
35
50
|
}
|
package/dist/llm/memory-infer.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* LLM helper for the `akm index` memory-inference pass (#201).
|
|
3
6
|
*
|
|
@@ -18,97 +21,103 @@
|
|
|
18
21
|
import { toErrorMessage } from "../core/common";
|
|
19
22
|
import { warn } from "../core/warn";
|
|
20
23
|
import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
|
|
24
|
+
import { tryLlmFeature } from "./feature-gate";
|
|
21
25
|
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
22
26
|
const MAX_BODY_CHARS = 4000;
|
|
23
|
-
/** Hard timeout for the LLM call. The index run must not hang on a misbehaving endpoint. */
|
|
24
|
-
const LLM_TIMEOUT_MS = 30_000;
|
|
25
27
|
const SYSTEM_PROMPT = "You compress a developer memory into one high-signal derived memory for later retrieval. " +
|
|
26
28
|
"Return only valid JSON. No prose outside the JSON object. No markdown fences.";
|
|
27
|
-
const USER_PROMPT_PREFIX = `Compress the memory below into one
|
|
28
|
-
|
|
29
|
-
Rules:
|
|
30
|
-
- Output ONLY a JSON object with exactly these keys: {"title": string, "description": string, "tags": string[], "searchHints": string[], "content": string}.
|
|
31
|
-
- ` +
|
|
32
|
-
'"title"' +
|
|
33
|
-
` is a short, descriptive title for the derived memory.
|
|
34
|
-
- ` +
|
|
35
|
-
'"description"' +
|
|
36
|
-
` is one sentence explaining why this derived memory matters.
|
|
37
|
-
- ` +
|
|
38
|
-
'"tags"' +
|
|
39
|
-
` contains 3-8 specific keywords.
|
|
40
|
-
- ` +
|
|
41
|
-
'"searchHints"' +
|
|
42
|
-
` contains 3-6 natural-language retrieval phrases.
|
|
43
|
-
- ` +
|
|
44
|
-
'"content"' +
|
|
45
|
-
` must be compact markdown that preserves the reusable insight, root cause, fix, constraints, and applicability conditions when present.
|
|
46
|
-
- Prefer 2-4 short sections with informative headings over long prose.
|
|
47
|
-
- Omit timestamps, verification-only metrics, pleasantries, and session-specific chatter unless they are essential to applying the insight later.
|
|
48
|
-
- Preserve technical specifics (names, versions, identifiers, selectors, file paths, config keys) verbatim.
|
|
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.
|
|
49
32
|
|
|
50
33
|
Memory:
|
|
51
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
|
+
};
|
|
52
59
|
/**
|
|
53
60
|
* Compress a single memory body into one derived memory via the configured LLM.
|
|
54
61
|
*
|
|
55
62
|
* Returns `undefined` on any failure (timeout, invalid JSON, empty response).
|
|
56
|
-
* Errors
|
|
57
|
-
* are logged via `warn()` but never thrown — a failed split for one memory
|
|
63
|
+
* Errors are logged via `warn()` but never thrown — a failed split for one memory
|
|
58
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).
|
|
59
68
|
*/
|
|
60
|
-
export async function compressMemoryToDerivedMemory(llmConfig, body, signal) {
|
|
69
|
+
export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback) {
|
|
61
70
|
const trimmedBody = body.trim();
|
|
62
71
|
if (!trimmedBody)
|
|
63
72
|
return undefined;
|
|
64
73
|
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
chatCompletion(llmConfig, [
|
|
74
|
+
return tryLlmFeature("memory_inference", akmConfig, async () => {
|
|
75
|
+
try {
|
|
76
|
+
const raw = await chatCompletion(llmConfig, [
|
|
69
77
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
70
78
|
{ role: "user", content: userPrompt },
|
|
71
|
-
], {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
], {
|
|
80
|
+
temperature: 0.1,
|
|
81
|
+
timeoutMs: llmConfig.timeoutMs,
|
|
82
|
+
signal,
|
|
83
|
+
responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
|
|
84
|
+
});
|
|
85
|
+
if (!raw)
|
|
86
|
+
return undefined;
|
|
87
|
+
const parsed = parseEmbeddedJsonResponse(raw);
|
|
88
|
+
if (!parsed) {
|
|
89
|
+
warn("memory inference: invalid JSON response from LLM; skipping memory.");
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
|
|
93
|
+
const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
|
|
94
|
+
const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
|
|
95
|
+
const tags = Array.isArray(parsed.tags)
|
|
96
|
+
? parsed.tags
|
|
97
|
+
.filter((t) => typeof t === "string")
|
|
98
|
+
.map((t) => t.trim())
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.slice(0, 8)
|
|
101
|
+
: [];
|
|
102
|
+
const searchHints = Array.isArray(parsed.searchHints)
|
|
103
|
+
? parsed.searchHints
|
|
104
|
+
.filter((h) => typeof h === "string")
|
|
105
|
+
.map((h) => h.trim())
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.slice(0, 6)
|
|
108
|
+
: [];
|
|
109
|
+
if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
|
|
110
|
+
warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return { title, description, tags, searchHints, content };
|
|
82
114
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
|
|
86
|
-
const tags = Array.isArray(parsed.tags)
|
|
87
|
-
? parsed.tags
|
|
88
|
-
.filter((t) => typeof t === "string")
|
|
89
|
-
.map((t) => t.trim())
|
|
90
|
-
.filter(Boolean)
|
|
91
|
-
.slice(0, 8)
|
|
92
|
-
: [];
|
|
93
|
-
const searchHints = Array.isArray(parsed.searchHints)
|
|
94
|
-
? parsed.searchHints
|
|
95
|
-
.filter((h) => typeof h === "string")
|
|
96
|
-
.map((h) => h.trim())
|
|
97
|
-
.filter(Boolean)
|
|
98
|
-
.slice(0, 6)
|
|
99
|
-
: [];
|
|
100
|
-
if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
|
|
101
|
-
warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
|
|
115
|
+
catch (err) {
|
|
116
|
+
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
102
117
|
return undefined;
|
|
103
118
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
if (timeoutHandle !== undefined)
|
|
112
|
-
clearTimeout(timeoutHandle);
|
|
113
|
-
}
|
|
119
|
+
}, undefined, {
|
|
120
|
+
timeoutMs: llmConfig.timeoutMs,
|
|
121
|
+
onFallback,
|
|
122
|
+
});
|
|
114
123
|
}
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Split out of `llm.ts` so the higher-level workflow (prompting the LLM to
|
|
5
|
-
* improve descriptions/tags/searchHints) lives separately from the low-level
|
|
6
|
-
* transport client in `client.ts`.
|
|
7
|
-
*/
|
|
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/.
|
|
8
4
|
import { chatCompletion, parseJsonResponse } from "./client";
|
|
5
|
+
import { tryLlmFeature } from "./feature-gate";
|
|
9
6
|
const SYSTEM_PROMPT = `You are a metadata generator for a developer asset registry. Given a script/skill/command/agent entry, generate improved metadata. Respond with ONLY valid JSON, no markdown fencing.`;
|
|
10
7
|
/**
|
|
11
8
|
* Use an LLM to enhance a stash entry's metadata: improve description,
|
|
12
9
|
* generate searchHints, and suggest tags.
|
|
10
|
+
*
|
|
11
|
+
* When `akmConfig` is provided, routes through
|
|
12
|
+
* `tryLlmFeature("metadata_enhance", ...)` so the feature gate is honoured and
|
|
13
|
+
* errors are swallowed to `{}`. When `akmConfig` is `undefined` the gate is
|
|
14
|
+
* bypassed entirely — the LLM call runs unconditionally and errors propagate to
|
|
15
|
+
* the caller (pre-gate behaviour, used by direct callers such as tests).
|
|
13
16
|
*/
|
|
14
|
-
export async function enhanceMetadata(config, entry, fileContent, signal) {
|
|
17
|
+
export async function enhanceMetadata(config, entry, fileContent, signal, akmConfig) {
|
|
15
18
|
const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
|
|
16
19
|
if (entry.description)
|
|
17
20
|
contextParts.push(`Current description: ${entry.description}`);
|
|
18
21
|
if (entry.tags?.length)
|
|
19
22
|
contextParts.push(`Current tags: ${entry.tags.join(", ")}`);
|
|
20
23
|
if (fileContent) {
|
|
21
|
-
// Limit content to first
|
|
22
|
-
const truncated = fileContent.length >
|
|
24
|
+
// Limit content to first 4000 chars to stay within token limits (matches other modules)
|
|
25
|
+
const truncated = fileContent.length > 4000 ? `${fileContent.slice(0, 4000)}\n... (truncated)` : fileContent;
|
|
23
26
|
contextParts.push(`File content:\n${truncated}`);
|
|
24
27
|
}
|
|
25
28
|
const userPrompt = `${contextParts.join("\n")}
|
|
@@ -30,24 +33,34 @@ Generate improved metadata for this ${entry.type}. Return JSON with these fields
|
|
|
30
33
|
- "tags": an array of 3-8 relevant keyword tags
|
|
31
34
|
|
|
32
35
|
Return ONLY the JSON object, no explanation.`;
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
const runLlm = async () => {
|
|
37
|
+
const raw = await chatCompletion(config, [
|
|
38
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
39
|
+
{ role: "user", content: userPrompt },
|
|
40
|
+
], { signal });
|
|
41
|
+
const parsed = parseJsonResponse(raw);
|
|
42
|
+
if (!parsed)
|
|
43
|
+
return {};
|
|
44
|
+
const result = {};
|
|
45
|
+
if (typeof parsed.description === "string" && parsed.description) {
|
|
46
|
+
result.description = parsed.description;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(parsed.searchHints)) {
|
|
49
|
+
result.searchHints = parsed.searchHints
|
|
50
|
+
.filter((s) => typeof s === "string" && s.trim().length > 0)
|
|
51
|
+
.slice(0, 8);
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(parsed.tags)) {
|
|
54
|
+
result.tags = parsed.tags.filter((s) => typeof s === "string" && s.trim().length > 0).slice(0, 10);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
// When no akmConfig is provided, bypass the feature gate entirely: run the
|
|
59
|
+
// LLM call directly and let errors propagate to the caller (pre-gate
|
|
60
|
+
// behaviour). When akmConfig is present, honour the feature flag and swallow
|
|
61
|
+
// errors to {} via tryLlmFeature.
|
|
62
|
+
if (akmConfig === undefined) {
|
|
63
|
+
return runLlm();
|
|
51
64
|
}
|
|
52
|
-
return
|
|
65
|
+
return tryLlmFeature("metadata_enhance", akmConfig, runLlm, {}, { timeoutMs: config.timeoutMs });
|
|
53
66
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
You are extracting durable engineering insights from a software session transcript. Most sessions produce zero new durable insights — the agent already captures what's worth keeping via explicit `akm remember` / `akm feedback` calls during the session. Your job is to identify learnings that slipped through.
|
|
2
|
+
|
|
3
|
+
## What counts as "durable insight"
|
|
4
|
+
|
|
5
|
+
Things worth extracting:
|
|
6
|
+
- Recovery patterns ("X fails when Y; the fix is Z")
|
|
7
|
+
- Hidden constraints discovered mid-task ("this codebase requires X before deploy")
|
|
8
|
+
- Architecture observations ("module A is consumed by B and C via the D bus")
|
|
9
|
+
- Non-obvious workarounds for real defects
|
|
10
|
+
- Domain knowledge the agent learned and would benefit a future session
|
|
11
|
+
|
|
12
|
+
Things NOT to extract:
|
|
13
|
+
- Successful command sequences (already in git history / shell history)
|
|
14
|
+
- Tool counts / aggregates
|
|
15
|
+
- The agent's own narrative about what it was doing
|
|
16
|
+
- Restatements of what the user asked for
|
|
17
|
+
- Generic platitudes ("test your code", "be careful with X")
|
|
18
|
+
- Anything already preserved via the agent's explicit calls (see below)
|
|
19
|
+
|
|
20
|
+
## Already preserved by the agent — DO NOT re-extract
|
|
21
|
+
|
|
22
|
+
{{ALREADY_PRESERVED}}
|
|
23
|
+
|
|
24
|
+
## Session metadata
|
|
25
|
+
|
|
26
|
+
- Harness: {{HARNESS}}
|
|
27
|
+
- Title: {{TITLE}}
|
|
28
|
+
- Started: {{STARTED_AT}}
|
|
29
|
+
- Ended: {{ENDED_AT}}
|
|
30
|
+
- Project hint: {{PROJECT_HINT}}
|
|
31
|
+
|
|
32
|
+
## Filtered session transcript
|
|
33
|
+
|
|
34
|
+
The transcript below has already had read-only `akm` meta-ops and platform boilerplate stripped. Only content that might carry signal remains.
|
|
35
|
+
|
|
36
|
+
{{TRANSCRIPT}}
|
|
37
|
+
|
|
38
|
+
## Output contract
|
|
39
|
+
|
|
40
|
+
Respond with EXACTLY one JSON object matching this shape:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
{
|
|
44
|
+
"candidates": [
|
|
45
|
+
{
|
|
46
|
+
"type": "memory" | "lesson" | "knowledge",
|
|
47
|
+
"name": "<kebab-case-slug>",
|
|
48
|
+
"description": "<one sentence 20-400 chars>",
|
|
49
|
+
"when_to_use": "<one sentence 15-400 chars; REQUIRED only when type=lesson>",
|
|
50
|
+
"body": "<markdown body, 200-3000 chars typical>",
|
|
51
|
+
"confidence": <number 0.0-1.0>,
|
|
52
|
+
"evidence": "<one-line pointer to the moment in the session>"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"rationale_if_empty": "<one sentence; REQUIRED when candidates is empty>"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Rules
|
|
60
|
+
|
|
61
|
+
1. **Zero candidates is a valid and frequent answer.** Most sessions yield no new durable insight. When that's the case, return `{"candidates": [], "rationale_if_empty": "..."}` explaining what you saw and why it didn't rise to durable-knowledge level. Do not fabricate.
|
|
62
|
+
|
|
63
|
+
2. **Pick the right type per candidate:**
|
|
64
|
+
- `memory` — a fact or short observation. Use for "X works on this codebase", "auth uses Y library version Z".
|
|
65
|
+
- `lesson` — a "do X / avoid Y" pattern, ALWAYS with `when_to_use`. Use for hard-won learnings about pitfalls, recovery patterns, or non-obvious gotchas.
|
|
66
|
+
- `knowledge` — substantive multi-section reference doc. Rare from one session.
|
|
67
|
+
|
|
68
|
+
3. **Calibrate `confidence` honestly:**
|
|
69
|
+
- `0.9+` — high certainty this is a real durable insight a reviewer would clearly accept
|
|
70
|
+
- `0.7-0.89` — clear improvement, but a reviewer might prefer different framing or scope
|
|
71
|
+
- `0.5-0.69` — marginal / judgment call
|
|
72
|
+
- `<0.5` — don't include; prefer fewer-but-better candidates
|
|
73
|
+
|
|
74
|
+
4. **`evidence` must reference the session** — a brief pointer like "agent's tool failure at ts=...", "user's correction at ...", "after the recovery in the Bash sequence around ...". Without evidence the candidate is hard to validate; default to lower confidence when evidence is vague.
|
|
75
|
+
|
|
76
|
+
5. **Do not duplicate already-preserved content.** If a candidate substantively overlaps with anything in the "Already preserved" list above, skip it.
|
|
77
|
+
|
|
78
|
+
6. **No speculation.** Only extract things the session genuinely demonstrates. If the agent struggled and didn't resolve, that may itself be a lesson (`when_to_use: "When attempting X, expect Y to fail"`) — but only if the failure mode is concrete enough to be useful next time.
|
|
79
|
+
|
|
80
|
+
7. Respond with the JSON object only. No prose before or after. No code fences.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Extract entities and relations from the asset body below.
|
|
2
|
+
|
|
3
|
+
Rules:
|
|
4
|
+
- Output ONLY a JSON object: {"entities": ["Entity One", ...], "relations": [{"from": "A", "to": "B", "type": "uses"}, ...]}.
|
|
5
|
+
- Entities are short, canonical noun phrases (project names, services, tools, people, file/dir names, technical concepts).
|
|
6
|
+
- Relations connect two entities that both appear in the entities array.
|
|
7
|
+
- "type" is a short verb phrase (e.g. "uses", "depends on", "owns", "documents"). Optional; omit when unsure.
|
|
8
|
+
- Drop pleasantries, meta-commentary, and timestamps.
|
|
9
|
+
- Limit to at most {{MAX_ENTITIES}} entities and {{MAX_RELATIONS}} relations per asset.
|
|
10
|
+
- Return {"entities": [], "relations": []} if the body has no extractable graph content.
|
|
11
|
+
- DO NOT return markdown code blocks, ONLY valid JSON objects.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
|
|
15
|
+
Input:
|
|
16
|
+
## Deployment Notes
|
|
17
|
+
The auth-service uses PostgreSQL for user sessions. It depends on the redis-cache
|
|
18
|
+
for rate limiting. The terraform-provisioner deploys everything to the prod cluster.
|
|
19
|
+
Owner: @alice.
|
|
20
|
+
|
|
21
|
+
Output:
|
|
22
|
+
{"entities":["auth-service","PostgreSQL","redis-cache","terraform-provisioner","prod cluster","@alice"],"relations":[{"from":"auth-service","to":"PostgreSQL","type":"uses"},{"from":"auth-service","to":"redis-cache","type":"depends on"},{"from":"terraform-provisioner","to":"prod cluster","type":"deploys"},{"from":"terraform-provisioner","to":"auth-service","type":"deploys"},{"from":"@alice","to":"auth-service","type":"owns"}]}
|
|
23
|
+
|
|
24
|
+
Input:
|
|
25
|
+
## Meeting: API Redesign
|
|
26
|
+
Discussed moving from REST to GraphQL. The frontend team will use Apollo Client.
|
|
27
|
+
Backend needs to implement resolvers. Timeline: Q2.
|
|
28
|
+
|
|
29
|
+
Output:
|
|
30
|
+
{"entities":["REST","GraphQL","Apollo Client","frontend team","backend","resolvers","Q2"],"relations":[{"from":"frontend team","to":"Apollo Client","type":"uses"},{"from":"backend","to":"resolvers","type":"implements"},{"from":"frontend team","to":"GraphQL","type":"migrates to"}]}
|
|
31
|
+
|
|
32
|
+
===============
|
|
33
|
+
|
|
34
|
+
Request:
|
|
35
|
+
|