akm-cli 0.8.7 → 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 +428 -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} +48 -36
- 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
|
@@ -3,24 +3,15 @@
|
|
|
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 { writeFileAtomic } from "../core/common";
|
|
7
|
-
import { rethrowIfTestIsolationError } from "../core/errors";
|
|
8
|
-
import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock";
|
|
9
|
-
import { getDataDir } from "../core/paths";
|
|
10
|
-
// ── Paths ───────────────────────────────────────────────────────────────────
|
|
11
|
-
const LOCKFILE_NAME = "akm.lock";
|
|
12
|
-
function getLockfilePath() {
|
|
13
|
-
return path.join(getDataDir(), LOCKFILE_NAME);
|
|
14
|
-
}
|
|
6
|
+
import { writeFileAtomic } from "../core/common.js";
|
|
7
|
+
import { rethrowIfTestIsolationError } from "../core/errors.js";
|
|
8
|
+
import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
|
|
9
|
+
import { getDataDir, getLockfileLockPath, getLockfilePath } from "../core/paths.js";
|
|
15
10
|
// ── Lock sentinel ────────────────────────────────────────────────────────────
|
|
16
11
|
const LOCK_MAX_RETRIES = 3;
|
|
17
12
|
const LOCK_RETRY_DELAY_MS = 100;
|
|
18
|
-
function getLockSentinelPath() {
|
|
19
|
-
// The sentinel always lives next to the lock file it guards.
|
|
20
|
-
return `${path.join(getDataDir(), LOCKFILE_NAME)}.lck`;
|
|
21
|
-
}
|
|
22
13
|
async function acquireLockSentinel() {
|
|
23
|
-
const sentinelPath =
|
|
14
|
+
const sentinelPath = getLockfileLockPath();
|
|
24
15
|
// Ensure the directory exists before attempting to create the sentinel.
|
|
25
16
|
fs.mkdirSync(path.dirname(sentinelPath), { recursive: true });
|
|
26
17
|
for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
|
|
@@ -40,7 +31,7 @@ async function acquireLockSentinel() {
|
|
|
40
31
|
return false;
|
|
41
32
|
}
|
|
42
33
|
function releaseLockSentinel() {
|
|
43
|
-
releaseLock(
|
|
34
|
+
releaseLock(getLockfileLockPath());
|
|
44
35
|
}
|
|
45
36
|
// ── Read / Write ────────────────────────────────────────────────────────────
|
|
46
37
|
export function readLockfile() {
|
|
@@ -61,7 +52,7 @@ export function readLockfile() {
|
|
|
61
52
|
}
|
|
62
53
|
export function writeLockfile(entries) {
|
|
63
54
|
// Always write to $DATA — never to the legacy $CONFIG location.
|
|
64
|
-
const lockfilePath =
|
|
55
|
+
const lockfilePath = getLockfilePath();
|
|
65
56
|
const dir = path.dirname(lockfilePath);
|
|
66
57
|
fs.mkdirSync(dir, { recursive: true });
|
|
67
58
|
writeFileAtomic(lockfilePath, `${JSON.stringify(entries, null, 2)}\n`);
|
|
@@ -1,10 +1,25 @@
|
|
|
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 {
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import { getHarness, SESSION_LOG_HARNESSES } from "../harnesses/index.js";
|
|
5
|
+
import { ClaudeCodeProvider } from "../harnesses/claude/session-log.js";
|
|
6
|
+
import { OpenCodeProvider } from "../harnesses/opencode/session-log.js";
|
|
7
|
+
export { extractInlineRefMentions } from "./inline-refs.js";
|
|
7
8
|
const HARNESSES = [new ClaudeCodeProvider(), new OpenCodeProvider()];
|
|
9
|
+
// #562: the unified HARNESS_REGISTRY is the single source of truth for which
|
|
10
|
+
// harnesses expose session logs. Validate (behaviour-preserving) that every
|
|
11
|
+
// session-log provider instantiated above resolves to a registry harness whose
|
|
12
|
+
// `sessionLogs` capability is set — and via the id-normalization bridge, so a
|
|
13
|
+
// provider named "claude-code" still maps to the canonical "claude" harness.
|
|
14
|
+
// This turns a silently-drifting third registry into a startup invariant.
|
|
15
|
+
for (const provider of HARNESSES) {
|
|
16
|
+
const harness = getHarness(provider.name);
|
|
17
|
+
if (!harness?.capabilities.sessionLogs) {
|
|
18
|
+
throw new Error(`[akm] session-log provider "${provider.name}" is not registered as a sessionLogs harness in HARNESS_REGISTRY (src/integrations/harnesses). Add it there.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Touch the derived list so the dependency is explicit and tree-shake-safe.
|
|
22
|
+
void SESSION_LOG_HARNESSES;
|
|
8
23
|
const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENOENT|timeout/i;
|
|
9
24
|
/**
|
|
10
25
|
* Returns all available session log harnesses for the current machine.
|
|
@@ -51,19 +66,77 @@ export function aggregateSessionEvents(events) {
|
|
|
51
66
|
}));
|
|
52
67
|
}
|
|
53
68
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
69
|
+
* Collect normalized session events from a set of harnesses for the health
|
|
70
|
+
* candidate scan (#568).
|
|
71
|
+
*
|
|
72
|
+
* Pipeline selection per harness (capability-gated):
|
|
73
|
+
* - readSession-capable harness (`supportsReadSession !== false`): drive the
|
|
74
|
+
* richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
|
|
75
|
+
* structured content — tool calls, assistant content blocks, thinking,
|
|
76
|
+
* tool_result — into event text (e.g. ClaudeCodeProvider's `parseClaudeEvent`
|
|
77
|
+
* surfaces `[tool:*]` / `[tool_result]` blocks that the legacy flat
|
|
78
|
+
* `readEvents` scan drops entirely). This is what lets health advisories see
|
|
79
|
+
* repeated tool failures / long runs that the flat scan hid.
|
|
80
|
+
* - legacy-only harness (`supportsReadSession === false`): fall back to the
|
|
81
|
+
* legacy flat `readEvents()` scan (behaviour-preserving).
|
|
82
|
+
*
|
|
83
|
+
* Extracted as a pure function (harnesses injected) so it is unit-testable
|
|
84
|
+
* without touching the real on-disk session-log locations.
|
|
85
|
+
*
|
|
86
|
+
* `maxSessionsPerHarness` bounds the rich path: `readSession()` reads each
|
|
87
|
+
* session file IN FULL (unlike the legacy flat scan, which only touched files
|
|
88
|
+
* with mtime ≥ sinceMs and skipped non-string content). On a machine with a
|
|
89
|
+
* deep `~/.claude/projects` history a 30-day window can hold hundreds of
|
|
90
|
+
* multi-MB session files, and reading+parsing every one in full made the
|
|
91
|
+
* health command (`akm health`, which calls this synchronously) blow past its
|
|
92
|
+
* latency budget. `listSessions()` returns summaries sorted newest-first, so
|
|
93
|
+
* capping to the most-recent N sessions per harness keeps the richer signal
|
|
94
|
+
* for what actually matters (recent activity) while bounding cost. The legacy
|
|
95
|
+
* flat-scan path is naturally cheaper and is left uncapped.
|
|
56
96
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
97
|
+
const DEFAULT_MAX_SESSIONS_PER_HARNESS = 50;
|
|
98
|
+
export function collectSessionEvents(harnesses, sinceMs, maxSessionsPerHarness = DEFAULT_MAX_SESSIONS_PER_HARNESS) {
|
|
59
99
|
const events = [];
|
|
60
|
-
for (const harness of
|
|
100
|
+
for (const harness of harnesses) {
|
|
61
101
|
try {
|
|
62
|
-
|
|
102
|
+
if (harness.supportsReadSession === false) {
|
|
103
|
+
// Legacy-only harness: only the flat event scan is available.
|
|
104
|
+
events.push(...harness.readEvents({ sinceMs }));
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
// Rich path: enumerate sessions cheaply, then read each one's full
|
|
108
|
+
// structured event stream. Falls back to readEvents if listSessions
|
|
109
|
+
// surfaces nothing (e.g. a harness that wired readSession but whose
|
|
110
|
+
// listSessions returns empty on this machine) so we never regress
|
|
111
|
+
// coverage relative to the legacy scan.
|
|
112
|
+
const summaries = harness.listSessions({ sinceMs });
|
|
113
|
+
if (summaries.length === 0) {
|
|
114
|
+
events.push(...harness.readEvents({ sinceMs }));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// summaries are newest-first; bound the full-file reads (see doc above).
|
|
118
|
+
for (const summary of summaries.slice(0, maxSessionsPerHarness)) {
|
|
119
|
+
try {
|
|
120
|
+
const session = harness.readSession(summary);
|
|
121
|
+
events.push(...session.events);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// a single unreadable session is non-fatal
|
|
125
|
+
}
|
|
126
|
+
}
|
|
63
127
|
}
|
|
64
128
|
catch {
|
|
65
129
|
// individual harness failures are non-fatal
|
|
66
130
|
}
|
|
67
131
|
}
|
|
132
|
+
return events;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Scan recent session logs from all available harnesses and return
|
|
136
|
+
* repeated failure patterns that might warrant new AKM assets.
|
|
137
|
+
*/
|
|
138
|
+
export function getExecutionLogCandidates(sinceDays = 7) {
|
|
139
|
+
const sinceMs = Date.now() - sinceDays * 24 * 60 * 60 * 1000;
|
|
140
|
+
const events = collectSessionEvents(getAvailableHarnesses(), sinceMs);
|
|
68
141
|
return aggregateSessionEvents(events);
|
|
69
142
|
}
|
package/dist/llm/call-ai.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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 { getDefaultLlmConfig } from "../core/config";
|
|
5
|
-
import { warn } from "../core/warn";
|
|
6
|
-
import { resolveAgentProfile, runAgent } from "../integrations/agent";
|
|
7
|
-
import { chatCompletion } from "./client";
|
|
4
|
+
import { getDefaultLlmConfig } from "../core/config/config.js";
|
|
5
|
+
import { warn } from "../core/warn.js";
|
|
6
|
+
import { resolveAgentProfile, runAgent } from "../integrations/agent/index.js";
|
|
7
|
+
import { chatCompletion } from "./client.js";
|
|
8
8
|
/**
|
|
9
9
|
* Unified AI call: prefers the default agent profile, falls back to the
|
|
10
10
|
* default LLM profile. When neither is configured, returns a structured
|
package/dist/llm/client.js
CHANGED
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
*
|
|
11
11
|
* `llm.ts` re-exports everything from this module for backward compatibility.
|
|
12
12
|
*/
|
|
13
|
-
import { fetchWithTimeout } from "../core/common";
|
|
14
|
-
import { resolveSecret } from "../core/config";
|
|
15
|
-
import { escapeJsonStringControls, parseJsonResponse, stripCodeFences, stripThinkBlocks } from "../core/parse";
|
|
13
|
+
import { fetchWithTimeout } from "../core/common.js";
|
|
14
|
+
import { resolveSecret } from "../core/config/config.js";
|
|
15
|
+
import { escapeJsonStringControls, parseJsonResponse, stripCodeFences, stripThinkBlocks } from "../core/parse.js";
|
|
16
|
+
import { warnVerbose } from "../core/warn.js";
|
|
17
|
+
import { emitLlmUsage, extractUsageTokens } from "./usage-telemetry.js";
|
|
16
18
|
// Re-export shared parse utilities so existing importers of `client.ts` continue
|
|
17
19
|
// to resolve `parseJsonResponse` and `parseEmbeddedJsonResponse` from this module.
|
|
18
|
-
export { escapeJsonStringControls, parseEmbeddedJsonResponse, parseJsonResponse, stripCodeFences, stripThinkBlocks, } from "../core/parse";
|
|
20
|
+
export { escapeJsonStringControls, parseEmbeddedJsonResponse, parseJsonResponse, stripCodeFences, stripThinkBlocks, } from "../core/parse.js";
|
|
19
21
|
/** Maximum length of an LLM error response body included in thrown errors. */
|
|
20
22
|
const ERROR_BODY_MAX_LEN = 200;
|
|
21
23
|
/**
|
|
@@ -46,6 +48,27 @@ export function redactErrorBody(input) {
|
|
|
46
48
|
}
|
|
47
49
|
return out;
|
|
48
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Detect a response body that is an HTML document rather than the expected
|
|
53
|
+
* JSON. LM Studio (and similar local providers) can serve their web UI on
|
|
54
|
+
* partial-load / startup failures, producing an HTML page where the OpenAI
|
|
55
|
+
* API contract promises JSON.
|
|
56
|
+
*/
|
|
57
|
+
function isHtmlResponse(body) {
|
|
58
|
+
const lower = body.trimStart().toLowerCase();
|
|
59
|
+
return lower.startsWith("<!doctype html") || lower.startsWith("<html");
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Produce a short plain-text excerpt of an HTML body for inclusion in error
|
|
63
|
+
* messages: strip tags, collapse whitespace, and truncate.
|
|
64
|
+
*/
|
|
65
|
+
function htmlExcerpt(body) {
|
|
66
|
+
const text = body
|
|
67
|
+
.replace(/<[^>]*>/g, " ")
|
|
68
|
+
.replace(/\s+/g, " ")
|
|
69
|
+
.trim();
|
|
70
|
+
return text.length > ERROR_BODY_MAX_LEN ? `${text.slice(0, ERROR_BODY_MAX_LEN)}…` : text;
|
|
71
|
+
}
|
|
49
72
|
export class LlmCallError extends Error {
|
|
50
73
|
code;
|
|
51
74
|
statusCode;
|
|
@@ -56,8 +79,95 @@ export class LlmCallError extends Error {
|
|
|
56
79
|
this.name = "LlmCallError";
|
|
57
80
|
}
|
|
58
81
|
}
|
|
82
|
+
// ── Single bounded retry for transient failures ─────────────────────────────
|
|
83
|
+
/** Lower bound of the jittered retry backoff (inclusive), in milliseconds. */
|
|
84
|
+
const RETRY_BACKOFF_MIN_MS = 200;
|
|
85
|
+
/** Upper bound of the jittered retry backoff (exclusive-ish), in milliseconds. */
|
|
86
|
+
const RETRY_BACKOFF_MAX_MS = 800;
|
|
87
|
+
/**
|
|
88
|
+
* Fraction of the effective timeout budget that, once consumed by the first
|
|
89
|
+
* attempt, causes the retry to be skipped — there is not enough budget left
|
|
90
|
+
* for a meaningful second attempt.
|
|
91
|
+
*/
|
|
92
|
+
const RETRY_BUDGET_FRACTION = 0.9;
|
|
93
|
+
/**
|
|
94
|
+
* Sleep for `ms` milliseconds. Extracted as a named helper so tests can stub
|
|
95
|
+
* the backoff via the internal `sleep` option on {@link chatCompletion} and
|
|
96
|
+
* avoid real delays.
|
|
97
|
+
*/
|
|
98
|
+
export function sleep(ms) {
|
|
99
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
100
|
+
}
|
|
101
|
+
/** Compute a uniform jittered backoff in the [200, 800)ms range. */
|
|
102
|
+
function retryBackoffMs() {
|
|
103
|
+
return RETRY_BACKOFF_MIN_MS + Math.random() * (RETRY_BACKOFF_MAX_MS - RETRY_BACKOFF_MIN_MS);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Detect whether an error message indicates a context-size-exceeded condition.
|
|
107
|
+
* Mirrors the heuristic in `graph-extract.ts` — retrying a context overflow
|
|
108
|
+
* cannot shrink the input, so it must not be retried.
|
|
109
|
+
*/
|
|
110
|
+
function looksLikeContextOverflow(message) {
|
|
111
|
+
const lower = message.toLowerCase();
|
|
112
|
+
return (lower.includes("context") &&
|
|
113
|
+
(lower.includes("context size") ||
|
|
114
|
+
lower.includes("context length") ||
|
|
115
|
+
lower.includes("context_window") ||
|
|
116
|
+
lower.includes("prompt too long") ||
|
|
117
|
+
lower.includes("exceeds")));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Decide whether a first-attempt {@link LlmCallError} is eligible for a single
|
|
121
|
+
* retry. Retryable: HTTP 5xx (`provider_error` with statusCode >= 500) and
|
|
122
|
+
* `network_error` whose message looks like a transient connection reset
|
|
123
|
+
* (ECONNRESET / EPIPE / "fetch failed"). NOT retryable: 4xx, `rate_limited`
|
|
124
|
+
* (429), `timeout`, `parse_error`, and context-overflow-classified errors.
|
|
125
|
+
*/
|
|
126
|
+
function isRetryable(err) {
|
|
127
|
+
if (looksLikeContextOverflow(err.message))
|
|
128
|
+
return false;
|
|
129
|
+
if (err.code === "provider_error") {
|
|
130
|
+
return typeof err.statusCode === "number" && err.statusCode >= 500;
|
|
131
|
+
}
|
|
132
|
+
if (err.code === "network_error") {
|
|
133
|
+
const lower = err.message.toLowerCase();
|
|
134
|
+
return lower.includes("econnreset") || lower.includes("epipe") || lower.includes("fetch failed");
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
59
138
|
export async function chatCompletion(config, messages, options) {
|
|
60
|
-
const
|
|
139
|
+
const effectiveTimeoutMs = options?.timeoutMs ?? config.timeoutMs ?? 120_000;
|
|
140
|
+
const started = Date.now();
|
|
141
|
+
try {
|
|
142
|
+
return await chatCompletionAttempt(config, messages, options, effectiveTimeoutMs);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
if (!(err instanceof LlmCallError) || !isRetryable(err))
|
|
146
|
+
throw err;
|
|
147
|
+
// Timeout-budget guard: if the first attempt already burned most of the
|
|
148
|
+
// budget, a second attempt cannot complete — skip the retry.
|
|
149
|
+
const elapsed = Date.now() - started;
|
|
150
|
+
const remaining = effectiveTimeoutMs - elapsed;
|
|
151
|
+
if (elapsed >= effectiveTimeoutMs * RETRY_BUDGET_FRACTION || remaining <= 0) {
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
// Signal the caller so it can bump `retryAttempts` (NOT `failureCount`).
|
|
155
|
+
options?.onRetryAttempt?.();
|
|
156
|
+
// Log the first failure at debug (verbose-only) level; the retry outcome is
|
|
157
|
+
// authoritative.
|
|
158
|
+
warnVerbose(`[akm] LLM transient failure (${err.code}); retrying once: ${err.message}`);
|
|
159
|
+
const wait = retryBackoffMs();
|
|
160
|
+
await (options?.sleep ?? sleep)(wait);
|
|
161
|
+
// The retry must not exceed the original budget.
|
|
162
|
+
return await chatCompletionAttempt(config, messages, options, remaining);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* A single chat-completion attempt: one HTTP request/response cycle with no
|
|
167
|
+
* retry. {@link chatCompletion} wraps this with a single bounded retry for
|
|
168
|
+
* transient failures.
|
|
169
|
+
*/
|
|
170
|
+
async function chatCompletionAttempt(config, messages, options, timeoutMs) {
|
|
61
171
|
const headers = { "Content-Type": "application/json" };
|
|
62
172
|
const resolvedKey = resolveSecret(config.apiKey);
|
|
63
173
|
if (resolvedKey) {
|
|
@@ -70,6 +180,10 @@ export async function chatCompletion(config, messages, options) {
|
|
|
70
180
|
const responseFormat = options?.responseSchema && config.supportsJsonSchema
|
|
71
181
|
? { response_format: { type: "json_schema", json_schema: { schema: options.responseSchema, strict: true } } }
|
|
72
182
|
: {};
|
|
183
|
+
// Wall-clock start for per-call usage telemetry (#576). Captured here so the
|
|
184
|
+
// emitted duration covers the full request/response/parse cycle of a single
|
|
185
|
+
// attempt, not the retry-wrapping `chatCompletion`.
|
|
186
|
+
const requestStartedAt = Date.now();
|
|
73
187
|
let response;
|
|
74
188
|
try {
|
|
75
189
|
response = await fetchWithTimeout(config.endpoint, {
|
|
@@ -110,12 +224,38 @@ export async function chatCompletion(config, messages, options) {
|
|
|
110
224
|
if (status === 429) {
|
|
111
225
|
throw new LlmCallError(`LLM request rate limited (429) ${config.endpoint}: ${safeBody}`, "rate_limited", status);
|
|
112
226
|
}
|
|
227
|
+
if (status >= 500 && isHtmlResponse(rawBody)) {
|
|
228
|
+
throw new LlmCallError(`LLM provider returned HTML instead of JSON (${status}) ${config.endpoint}: ${htmlExcerpt(rawBody)}`, "provider_html_error", status);
|
|
229
|
+
}
|
|
113
230
|
if (status >= 500) {
|
|
114
231
|
throw new LlmCallError(`LLM provider error (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
|
|
115
232
|
}
|
|
116
233
|
throw new LlmCallError(`LLM request failed (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
|
|
117
234
|
}
|
|
118
|
-
|
|
235
|
+
// A 2xx response is still an error if the body is HTML where JSON was
|
|
236
|
+
// expected (e.g. a provider serving its web UI). Read the raw body first so
|
|
237
|
+
// we can categorize an HTML page distinctly from a malformed-JSON parse_error.
|
|
238
|
+
const rawOkBody = await response.text();
|
|
239
|
+
if (isHtmlResponse(rawOkBody)) {
|
|
240
|
+
throw new LlmCallError(`LLM provider returned HTML instead of JSON (${response.status}) ${config.endpoint}: ${htmlExcerpt(rawOkBody)}`, "provider_html_error", response.status);
|
|
241
|
+
}
|
|
242
|
+
let json;
|
|
243
|
+
try {
|
|
244
|
+
json = JSON.parse(rawOkBody);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
throw new LlmCallError(`LLM response was not valid JSON ${config.endpoint}: ${redactErrorBody(rawOkBody)}`, "parse_error", response.status);
|
|
248
|
+
}
|
|
249
|
+
// Per-call usage telemetry (#576). Best-effort and fully isolated: a missing
|
|
250
|
+
// or garbled usage block still records duration + model, and a throwing sink
|
|
251
|
+
// can never fail the call (emitLlmUsage swallows its own errors). The stage
|
|
252
|
+
// is supplied ambiently by emitLlmUsage; no `stage` param is threaded here.
|
|
253
|
+
emitLlmUsage({
|
|
254
|
+
model: typeof json.model === "string" && json.model ? json.model : config.model,
|
|
255
|
+
durationMs: Date.now() - requestStartedAt,
|
|
256
|
+
finishReason: typeof json.choices?.[0]?.finish_reason === "string" ? json.choices[0].finish_reason : undefined,
|
|
257
|
+
...extractUsageTokens(json.usage),
|
|
258
|
+
});
|
|
119
259
|
const content = (json.choices?.[0]?.message?.content ?? "").trim();
|
|
120
260
|
const reasoning = (json.choices?.[0]?.message?.reasoning_content ?? "").trim();
|
|
121
261
|
return content || reasoning;
|
package/dist/llm/embedder.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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 { embedCacheKey, getCachedEmbedding, setCachedEmbedding } from "./embedders/cache";
|
|
5
|
-
import { isTransformersAvailable, LocalEmbedder } from "./embedders/local";
|
|
6
|
-
import { hasRemoteEndpoint, RemoteEmbedder } from "./embedders/remote";
|
|
4
|
+
import { embedCacheKey, getCachedEmbedding, setCachedEmbedding } from "./embedders/cache.js";
|
|
5
|
+
import { isTransformersAvailable, LocalEmbedder } from "./embedders/local.js";
|
|
6
|
+
import { hasRemoteEndpoint, RemoteEmbedder } from "./embedders/remote.js";
|
|
7
7
|
// ── Re-exports (public API) ─────────────────────────────────────────────────
|
|
8
|
-
export { clearEmbeddingCache } from "./embedders/cache";
|
|
9
|
-
export { DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "./embedders/local";
|
|
8
|
+
export { clearEmbeddingCache } from "./embedders/cache.js";
|
|
9
|
+
export { DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "./embedders/local.js";
|
|
10
10
|
// ── Singleton local embedder ────────────────────────────────────────────────
|
|
11
11
|
// `_localEmbedder` is an intentional module-level singleton but constructed
|
|
12
12
|
// lazily on first use. The underlying @huggingface/transformers pipeline is
|
|
@@ -76,7 +76,7 @@ export async function embedBatch(texts, embeddingConfig, signal) {
|
|
|
76
76
|
// (notably `db.ts`) can pull the math function without dragging in this
|
|
77
77
|
// facade and its `@huggingface/transformers` import chain. Re-export
|
|
78
78
|
// preserves the existing public API.
|
|
79
|
-
export { cosineSimilarity } from "./embedders/types";
|
|
79
|
+
export { cosineSimilarity } from "./embedders/types.js";
|
|
80
80
|
// ── Availability check ──────────────────────────────────────────────────────
|
|
81
81
|
/**
|
|
82
82
|
* Check whether embedding is available with a detailed reason on failure.
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* shared instance for the production code path.
|
|
11
11
|
*/
|
|
12
12
|
import path from "node:path";
|
|
13
|
-
import { getCacheDir } from "../../core/paths";
|
|
14
|
-
import { warn } from "../../core/warn";
|
|
13
|
+
import { getCacheDir } from "../../core/paths.js";
|
|
14
|
+
import { warn } from "../../core/warn.js";
|
|
15
|
+
import { getDirname, resolveModule } from "../../runtime.js";
|
|
15
16
|
/**
|
|
16
17
|
* Default local transformer model for embeddings.
|
|
17
18
|
* `bge-small-en-v1.5` scores higher on MTEB benchmarks than the previous
|
|
@@ -169,31 +170,17 @@ function shouldRetryWithoutExplicitDtype(error) {
|
|
|
169
170
|
}
|
|
170
171
|
/**
|
|
171
172
|
* Check whether the `@huggingface/transformers` package can be resolved.
|
|
172
|
-
* Uses `
|
|
173
|
-
* heavy WASM/model side-effects) just to test
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* (e.g. running under Node), so the function still works in mixed runtimes.
|
|
173
|
+
* Uses the runtime boundary's `resolveModule` so we never load the module
|
|
174
|
+
* (which would trigger heavy WASM/model side-effects) just to test
|
|
175
|
+
* availability. `resolveModule` uses `Bun.resolveSync` on Bun and
|
|
176
|
+
* `require.resolve` on Node.
|
|
177
177
|
*/
|
|
178
178
|
export function isTransformersAvailable() {
|
|
179
179
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
try {
|
|
189
|
-
const req = globalThis.require;
|
|
190
|
-
if (req && typeof req.resolve === "function") {
|
|
191
|
-
req.resolve("@huggingface/transformers");
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
180
|
+
resolveModule("@huggingface/transformers", getDirname(import.meta.url));
|
|
181
|
+
return true;
|
|
194
182
|
}
|
|
195
183
|
catch {
|
|
196
184
|
return false;
|
|
197
185
|
}
|
|
198
|
-
return false;
|
|
199
186
|
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Calls the configured `/embeddings` endpoint and L2-normalizes the returned
|
|
8
8
|
* vectors so the scoring pipeline's L2-to-cosine conversion is correct.
|
|
9
9
|
*/
|
|
10
|
-
import { fetchWithTimeout, isHttpUrl } from "../../core/common";
|
|
11
|
-
import { resolveSecret } from "../../core/config";
|
|
10
|
+
import { fetchWithTimeout, isHttpUrl } from "../../core/common.js";
|
|
11
|
+
import { resolveSecret } from "../../core/config/config.js";
|
|
12
12
|
const DEFAULT_REMOTE_BATCH_SIZE = 100;
|
|
13
13
|
/** Cheap token estimator: 4 chars ≈ 1 token. Used in verbose logging and error messages. */
|
|
14
14
|
export function estimateTokenCount(text) {
|
|
@@ -32,4 +32,4 @@ export function cosineSimilarity(a, b) {
|
|
|
32
32
|
}
|
|
33
33
|
// Imported lazily to keep this types module dependency-free where possible;
|
|
34
34
|
// `warn` is a thin printf wrapper so the cost is negligible.
|
|
35
|
-
import { warn } from "../../core/warn";
|
|
35
|
+
import { warn } from "../../core/warn.js";
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* straight through.
|
|
22
22
|
*/
|
|
23
23
|
import userPromptTemplate from "../assets/prompts/graph-extract-user-prompt.md" with { type: "text" };
|
|
24
|
-
import { toErrorMessage } from "../core/common";
|
|
25
|
-
import { warn, warnVerbose } from "../core/warn";
|
|
26
|
-
import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
|
|
27
|
-
import { tryLlmFeature } from "./feature-gate";
|
|
24
|
+
import { toErrorMessage } from "../core/common.js";
|
|
25
|
+
import { warn, warnVerbose } from "../core/warn.js";
|
|
26
|
+
import { chatCompletion, LlmCallError, parseEmbeddedJsonResponse } from "./client.js";
|
|
27
|
+
import { tryLlmFeature } from "./feature-gate.js";
|
|
28
28
|
/**
|
|
29
29
|
* Separator token used between assets in a batch prompt.
|
|
30
30
|
* Chosen to be visually clear and unlikely to appear verbatim in asset bodies.
|
|
@@ -48,14 +48,22 @@ const USER_PROMPT_PREFIX = userPromptTemplate
|
|
|
48
48
|
/**
|
|
49
49
|
* Detect whether an error message indicates a context size exceeded condition.
|
|
50
50
|
* Covers common patterns from OpenAI-compatible APIs (LM Studio, Ollama, etc).
|
|
51
|
+
*
|
|
52
|
+
* Requires BOTH a context keyword AND token-count/overflow evidence so that
|
|
53
|
+
* model prose merely mentioning "context size" / "context length" (e.g. gemma
|
|
54
|
+
* narrating about a document) does not get misclassified as a provider
|
|
55
|
+
* context-limit error (#496).
|
|
51
56
|
*/
|
|
52
|
-
function isContextSizeError(message) {
|
|
57
|
+
export function isContextSizeError(message) {
|
|
53
58
|
const lower = message.toLowerCase();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
const contextKw = /context (size|length|window)|prompt too long|exceeds.*context/.test(lower);
|
|
60
|
+
if (!contextKw) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const evidence = /\b\d+\s*(token|tokens|tk)\b/.test(lower) ||
|
|
64
|
+
/max(imum)?\s+(context|token|input)/.test(lower) ||
|
|
65
|
+
/exceeded|over.*limit|too.*long/.test(lower);
|
|
66
|
+
return evidence;
|
|
59
67
|
}
|
|
60
68
|
const GENERIC_ENTITIES = new Set([
|
|
61
69
|
"agent",
|
|
@@ -529,6 +537,7 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
529
537
|
temperature: 0.1,
|
|
530
538
|
timeoutMs: llmConfig.timeoutMs,
|
|
531
539
|
signal,
|
|
540
|
+
onRetryAttempt: () => bumpTelemetry(options.telemetry, "retryAttempts"),
|
|
532
541
|
});
|
|
533
542
|
if (!raw)
|
|
534
543
|
return null;
|
|
@@ -671,7 +680,12 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
671
680
|
const raw = await chatCompletion(llmConfig, [
|
|
672
681
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
673
682
|
{ role: "user", content: userPrompt },
|
|
674
|
-
], {
|
|
683
|
+
], {
|
|
684
|
+
temperature: 0.1,
|
|
685
|
+
timeoutMs: llmConfig.timeoutMs,
|
|
686
|
+
signal,
|
|
687
|
+
onRetryAttempt: () => bumpTelemetry(options.telemetry, "retryAttempts"),
|
|
688
|
+
});
|
|
675
689
|
if (!raw)
|
|
676
690
|
return empty();
|
|
677
691
|
const parsed = parseEmbeddedJsonResponse(raw);
|
|
@@ -696,6 +710,11 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
696
710
|
`Consider increasing llm.contextLength in config.json.`);
|
|
697
711
|
return empty("context_limit", "failed");
|
|
698
712
|
}
|
|
713
|
+
else if (err instanceof LlmCallError && err.code === "provider_html_error") {
|
|
714
|
+
bumpTelemetry(options.telemetry, "htmlErrorCount");
|
|
715
|
+
warn(`graph extraction: provider returned HTML instead of JSON for asset; promptChars=${userPrompt.length}${formatContextHint(llmConfig)}: ${errMsg}`);
|
|
716
|
+
return empty("llm_error", "failed");
|
|
717
|
+
}
|
|
699
718
|
else {
|
|
700
719
|
bumpTelemetry(options.telemetry, "failureCount");
|
|
701
720
|
warn(`graph extraction failed for asset; promptChars=${userPrompt.length}${formatContextHint(llmConfig)}: ${errMsg}`);
|
|
@@ -708,4 +727,4 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
708
727
|
});
|
|
709
728
|
}
|
|
710
729
|
// deduplicateGraph moved to src/indexer/graph-dedup.ts (pure utility, no LLM calls).
|
|
711
|
-
export { deduplicateGraph } from "../indexer/graph-dedup";
|
|
730
|
+
export { deduplicateGraph } from "../indexer/graph/graph-dedup.js";
|
package/dist/llm/index-passes.js
CHANGED
|
@@ -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 { getDefaultLlmConfig, getIndexPassConfig } from "../core/config";
|
|
4
|
+
import { getDefaultLlmConfig, getIndexPassConfig } from "../core/config/config.js";
|
|
5
5
|
/**
|
|
6
6
|
* Map a pass name (as used by callers — "memory", "graph", etc.) to the
|
|
7
7
|
* matching key under `profiles.improve.default.processes`. Pass names with
|
package/dist/llm/memory-infer.js
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* the connection via `resolveIndexPassLLM("memory", config)` and pass it
|
|
19
19
|
* straight through.
|
|
20
20
|
*/
|
|
21
|
-
import { toErrorMessage } from "../core/common";
|
|
22
|
-
import { warn } from "../core/warn";
|
|
23
|
-
import { chatCompletion, parseEmbeddedJsonResponse } from "./client";
|
|
24
|
-
import { tryLlmFeature } from "./feature-gate";
|
|
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
25
|
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
26
26
|
const MAX_BODY_CHARS = 4000;
|
|
27
27
|
const SYSTEM_PROMPT = "You compress a developer memory into one high-signal derived memory for later retrieval. " +
|
|
@@ -66,7 +66,7 @@ const DERIVED_MEMORY_JSON_SCHEMA = {
|
|
|
66
66
|
* Routes through `tryLlmFeature("memory_inference", ...)` so the feature gate
|
|
67
67
|
* and onFallback hook are honoured uniformly (Fix C5).
|
|
68
68
|
*/
|
|
69
|
-
export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback) {
|
|
69
|
+
export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt) {
|
|
70
70
|
const trimmedBody = body.trim();
|
|
71
71
|
if (!trimmedBody)
|
|
72
72
|
return undefined;
|
|
@@ -81,6 +81,7 @@ export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akm
|
|
|
81
81
|
timeoutMs: llmConfig.timeoutMs,
|
|
82
82
|
signal,
|
|
83
83
|
responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
|
|
84
|
+
onRetryAttempt,
|
|
84
85
|
});
|
|
85
86
|
if (!raw)
|
|
86
87
|
return undefined;
|
|
@@ -113,6 +114,12 @@ export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akm
|
|
|
113
114
|
return { title, description, tags, searchHints, content };
|
|
114
115
|
}
|
|
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
|
+
}
|
|
116
123
|
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
117
124
|
return undefined;
|
|
118
125
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
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 { chatCompletion, parseJsonResponse } from "./client";
|
|
5
|
-
import { tryLlmFeature } from "./feature-gate";
|
|
4
|
+
import { chatCompletion, parseJsonResponse } from "./client.js";
|
|
5
|
+
import { tryLlmFeature } from "./feature-gate.js";
|
|
6
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.`;
|
|
7
7
|
/**
|
|
8
8
|
* Use an LLM to enhance a stash entry's metadata: improve description,
|