akm-cli 0.9.17-alpha.3 → 0.9.17-alpha.4
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 +731 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +361 -751
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -441
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { writeFileAtomic } from "../core/common.js";
|
|
7
|
-
import { ConfigError, rethrowIfTestIsolationError } from "../core/errors.js";
|
|
7
|
+
import { ConfigError, rethrowIfTestIsolationError, TransientError } from "../core/errors.js";
|
|
8
8
|
import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
|
|
9
|
-
import { acquireMaintenanceBarrier } from "../core/maintenance-barrier.js";
|
|
10
9
|
import { classifyPathAccess, describeInaccessiblePath } from "../core/path-access.js";
|
|
11
10
|
import { getDataDir, getLockfileLockPath, getLockfilePath } from "../core/paths.js";
|
|
12
11
|
import { warn } from "../core/warn.js";
|
|
@@ -27,23 +26,18 @@ async function acquireLockSentinel() {
|
|
|
27
26
|
let delayMs = LOCK_RETRY_INITIAL_DELAY_MS;
|
|
28
27
|
let announced = false;
|
|
29
28
|
for (;;) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (ownership) {
|
|
34
|
-
return () => releaseLock(ownership);
|
|
35
|
-
}
|
|
36
|
-
const probe = probeLock(sentinelPath);
|
|
37
|
-
if (probe.state === "stale" && reclaimStaleLock(sentinelPath, probe)) {
|
|
38
|
-
continue; // Reclaimed — retry immediately.
|
|
39
|
-
}
|
|
29
|
+
const ownership = tryAcquireLockSync(sentinelPath, createLockPayload());
|
|
30
|
+
if (ownership) {
|
|
31
|
+
return () => releaseLock(ownership);
|
|
40
32
|
}
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const probe = probeLock(sentinelPath);
|
|
34
|
+
if (probe.state === "stale" && reclaimStaleLock(sentinelPath, probe)) {
|
|
35
|
+
continue; // Reclaimed — retry immediately.
|
|
43
36
|
}
|
|
44
|
-
// Another process holds the lock
|
|
37
|
+
// Another process holds the lock: ordinary contention, exit 75, not a
|
|
38
|
+
// config error a supervisor should stop retrying on.
|
|
45
39
|
if (Date.now() >= deadline) {
|
|
46
|
-
throw new
|
|
40
|
+
throw new TransientError(`Could not acquire lockfile sentinel at ${sentinelPath} after ${(timeoutMs / 1000).toFixed(1)}s; refusing to write without exclusive ownership.`, "LOCKFILE_CONTENDED");
|
|
47
41
|
}
|
|
48
42
|
if (!announced) {
|
|
49
43
|
announced = true;
|
|
@@ -93,37 +87,33 @@ function assertLockfilePathReadable(target) {
|
|
|
93
87
|
"records, and writing over them would destroy every bundle they track. Fix the ownership or mode of that " +
|
|
94
88
|
"path (or point AKM_DATA_DIR / XDG_DATA_HOME somewhere this user owns) and retry.", "DATA_DIR_UNREADABLE");
|
|
95
89
|
}
|
|
96
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Like {@link readLockfile}, but THROWS instead of silently degrading to `[]`
|
|
92
|
+
* when the on-disk lockfile exists yet cannot be read or parsed (R-012, #791).
|
|
93
|
+
*
|
|
94
|
+
* `readLockfile`'s fail-open contract is intentional for READ paths — a
|
|
95
|
+
* corrupt lock degrades a managed bundle to "unmanaged" rather than erroring
|
|
96
|
+
* every read-only command. But every writer below reads the current entries
|
|
97
|
+
* and writes the whole array back; if that read silently returned `[]` for a
|
|
98
|
+
* corrupt or unreadable file, the write would replace it with only the one
|
|
99
|
+
* entry being changed — permanently destroying every other lock record (a
|
|
100
|
+
* corrupt lockfile once wiped every entry this way). A missing file is NOT
|
|
101
|
+
* corruption: there is nothing to preserve, so that case returns `[]`.
|
|
102
|
+
*/
|
|
103
|
+
function readLockfileOrThrow() {
|
|
97
104
|
const lockfilePath = getLockfilePath();
|
|
98
|
-
let fd;
|
|
99
105
|
let raw;
|
|
100
|
-
let mode;
|
|
101
106
|
try {
|
|
102
|
-
|
|
103
|
-
// chmod between separate path-based calls cannot synthesize a generation
|
|
104
|
-
// that never existed on disk.
|
|
105
|
-
fd = fs.openSync(lockfilePath, "r");
|
|
106
|
-
raw = fs.readFileSync(fd, "utf8");
|
|
107
|
-
mode = fs.fstatSync(fd).mode & 0o777;
|
|
107
|
+
raw = fs.readFileSync(lockfilePath, "utf8");
|
|
108
108
|
}
|
|
109
109
|
catch (err) {
|
|
110
110
|
rethrowIfTestIsolationError(err);
|
|
111
|
-
if (err.code === "ENOENT")
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
// UNREADABLE lockfile has everything to preserve and we cannot see it —
|
|
116
|
-
// degrading it to `[]` here is precisely the destructive overwrite this
|
|
117
|
-
// function was written to prevent, only triggered by a permission fault
|
|
118
|
-
// instead of a corrupt file (#791). Classify AFTER the failed read so the
|
|
119
|
-
// happy path costs no extra syscall and the answer describes the failure
|
|
120
|
-
// we actually got.
|
|
111
|
+
if (err.code === "ENOENT")
|
|
112
|
+
return [];
|
|
113
|
+
// An UNREADABLE lockfile has everything to preserve and we cannot see it.
|
|
114
|
+
// Classify AFTER the failed read so the happy path costs no extra syscall.
|
|
121
115
|
assertLockfilePathReadable(lockfilePath);
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
if (fd !== undefined)
|
|
126
|
-
fs.closeSync(fd);
|
|
116
|
+
return [];
|
|
127
117
|
}
|
|
128
118
|
let parsed;
|
|
129
119
|
try {
|
|
@@ -145,23 +135,7 @@ function readLockfileSnapshotOrThrow() {
|
|
|
145
135
|
throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: ${invalid.length} existing entr${invalid.length === 1 ? "y is" : "ies are"} malformed. ` +
|
|
146
136
|
"Fix or remove the file by hand before retrying — those entries would otherwise be lost.", "INVALID_CONFIG_FILE");
|
|
147
137
|
}
|
|
148
|
-
return
|
|
149
|
-
entries: parsed.filter(isValidLockfileEntry),
|
|
150
|
-
raw,
|
|
151
|
-
mode,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
function readLockfileOrThrow() {
|
|
155
|
-
return readLockfileSnapshotOrThrow().entries;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Read the exact lock generation that a source-lifecycle transaction may
|
|
159
|
-
* replace. Unlike {@link readLockfile}, this refuses corrupt, malformed, or
|
|
160
|
-
* unreadable state so an update can never treat state it could not snapshot as
|
|
161
|
-
* an empty generation.
|
|
162
|
-
*/
|
|
163
|
-
export function readLockfileForUpdate() {
|
|
164
|
-
return readLockfileSnapshotOrThrow();
|
|
138
|
+
return parsed;
|
|
165
139
|
}
|
|
166
140
|
/**
|
|
167
141
|
* The materialized content root recorded in the lock for a managed (git/npm)
|
|
@@ -201,66 +175,44 @@ export async function writeLockfile(entries) {
|
|
|
201
175
|
release();
|
|
202
176
|
}
|
|
203
177
|
}
|
|
204
|
-
|
|
205
|
-
* Publish an update from one exact raw + parsed lockfile generation and return
|
|
206
|
-
* the exact bytes that were written. Formatting-only concurrent edits are a
|
|
207
|
-
* generation change here: rollback must never overwrite bytes it did not
|
|
208
|
-
* publish merely because they parse to an equivalent array.
|
|
209
|
-
*/
|
|
210
|
-
export async function publishLockfileUpdate(expected, desired) {
|
|
178
|
+
export async function upsertLockEntry(entry) {
|
|
211
179
|
const release = await acquireLockSentinel();
|
|
212
180
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
writeLockfileUnlocked(desired);
|
|
220
|
-
return readLockfileSnapshotOrThrow();
|
|
181
|
+
// R-012: readLockfileOrThrow (not readLockfile) — a corrupt/malformed
|
|
182
|
+
// lockfile must abort the upsert loudly rather than read as `[]` and get
|
|
183
|
+
// silently overwritten with just this one entry.
|
|
184
|
+
const entries = readLockfileOrThrow();
|
|
185
|
+
const withoutExisting = entries.filter((e) => e.id !== entry.id);
|
|
186
|
+
writeLockfileUnlocked([...withoutExisting, entry]);
|
|
221
187
|
}
|
|
222
188
|
finally {
|
|
223
189
|
release();
|
|
224
190
|
}
|
|
225
191
|
}
|
|
226
|
-
/**
|
|
227
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Rename a lock entry's id in place (D6 — `akm bundle rename`), keeping every
|
|
194
|
+
* other resolved field (`localRoot`, `resolvedVersion`, …) unchanged. Returns
|
|
195
|
+
* `true` when an entry for `oldId` existed and was renamed, `false` when
|
|
196
|
+
* there was nothing to rename (e.g. a filesystem bundle, which has no lock
|
|
197
|
+
* entry).
|
|
198
|
+
*/
|
|
199
|
+
export async function renameLockEntry(oldId, newId) {
|
|
228
200
|
const release = await acquireLockSentinel();
|
|
229
201
|
try {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
202
|
+
// R-012: see upsertLockEntry — a corrupt lockfile must abort loudly
|
|
203
|
+
// rather than read as `[]` and get silently overwritten.
|
|
204
|
+
const entries = readLockfileOrThrow();
|
|
205
|
+
const existing = entries.find((e) => e.id === oldId);
|
|
206
|
+
if (!existing)
|
|
234
207
|
return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (desired.raw === null) {
|
|
238
|
-
fs.rmSync(lockfilePath, { force: true });
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
|
|
242
|
-
writeFileAtomic(lockfilePath, desired.raw, desired.mode);
|
|
243
|
-
}
|
|
208
|
+
const renamed = entries.map((e) => (e.id === oldId ? { ...e, id: newId } : e));
|
|
209
|
+
writeLockfileUnlocked(renamed);
|
|
244
210
|
return true;
|
|
245
211
|
}
|
|
246
212
|
finally {
|
|
247
213
|
release();
|
|
248
214
|
}
|
|
249
215
|
}
|
|
250
|
-
export async function upsertLockEntry(entry) {
|
|
251
|
-
const release = await acquireLockSentinel();
|
|
252
|
-
try {
|
|
253
|
-
// R-012: readLockfileOrThrow (not readLockfile) — a corrupt/malformed
|
|
254
|
-
// lockfile must abort the upsert loudly rather than read as `[]` and get
|
|
255
|
-
// silently overwritten with just this one entry.
|
|
256
|
-
const entries = readLockfileOrThrow();
|
|
257
|
-
const withoutExisting = entries.filter((e) => e.id !== entry.id);
|
|
258
|
-
writeLockfileUnlocked([...withoutExisting, entry]);
|
|
259
|
-
}
|
|
260
|
-
finally {
|
|
261
|
-
release();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
216
|
export async function removeLockEntry(id) {
|
|
265
217
|
// Returning early says "there is no lock record to remove", and the uninstall
|
|
266
218
|
// that called us reports success on that basis. Only an absent data dir earns
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
/** Env var that switches the whole embedding facade into deterministic mode. */
|
|
5
5
|
export const DETERMINISTIC_EMBED_ENV = "AKM_EMBED_DETERMINISTIC";
|
|
6
6
|
/**
|
|
7
|
-
* Vector width. Matches the default local model (`bge-small`, 384 dims) so
|
|
8
|
-
* index
|
|
9
|
-
* any extra config.
|
|
7
|
+
* Vector width. Matches the default local model (`bge-small`, 384 dims), so a
|
|
8
|
+
* deterministic index is the same shape as a default local one.
|
|
10
9
|
*/
|
|
11
10
|
export const DETERMINISTIC_EMBED_DIM = 384;
|
|
12
11
|
/**
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
* Embedding profile: the text an embedding model expects around a query and
|
|
6
|
+
* around a document. Retrieval models are trained with these prompts, and
|
|
7
|
+
* sending raw text costs recall — Qwen3-Embedding without its query
|
|
8
|
+
* instruction lost 0.096 nDCG@10 on the retrieval suite.
|
|
9
|
+
*
|
|
10
|
+
* Presets are matched on the model name; `embedding.queryTemplate` and
|
|
11
|
+
* `embedding.documentTemplate` override them (an empty string means none).
|
|
12
|
+
* `{text}` marks where the text goes; a template without it is a prefix.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { isDeterministicEmbedEnabled } from "./deterministic.js";
|
|
16
|
+
import { DEFAULT_LOCAL_MODEL } from "./local.js";
|
|
17
|
+
const RETRIEVAL_QUERY_PREFIX = "Represent this sentence for searching relevant passages: {text}";
|
|
18
|
+
const PRESETS = [
|
|
19
|
+
{
|
|
20
|
+
model: /qwen3-embedding/i,
|
|
21
|
+
profile: {
|
|
22
|
+
queryTemplate: "Instruct: Given a question or task, retrieve the knowledge asset that helps with it\nQuery:{text}",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
model: /nomic-embed/i,
|
|
27
|
+
profile: { queryTemplate: "search_query: {text}", documentTemplate: "search_document: {text}" },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
model: /bge-(?:small|base|large)-en|mxbai-embed|arctic-embed/i,
|
|
31
|
+
profile: { queryTemplate: RETRIEVAL_QUERY_PREFIX },
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
model: /(?:^|[^a-z0-9])e5-(?:small|base|large)(?!.*instruct)/i,
|
|
35
|
+
profile: { queryTemplate: "query: {text}", documentTemplate: "passage: {text}" },
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
/** The model a config embeds with, as the presets match it. */
|
|
39
|
+
function embeddingModelName(config) {
|
|
40
|
+
if (config?.endpoint)
|
|
41
|
+
return config.model ?? "";
|
|
42
|
+
return config?.localModel ?? DEFAULT_LOCAL_MODEL;
|
|
43
|
+
}
|
|
44
|
+
/** The configured model's templates: its preset, with each side replaced by an explicit config value. */
|
|
45
|
+
export function resolveEmbeddingProfile(config) {
|
|
46
|
+
const model = embeddingModelName(config);
|
|
47
|
+
const preset = isDeterministicEmbedEnabled() ? {} : (PRESETS.find((entry) => entry.model.test(model))?.profile ?? {});
|
|
48
|
+
const queryTemplate = config?.queryTemplate ?? preset.queryTemplate;
|
|
49
|
+
const documentTemplate = config?.documentTemplate ?? preset.documentTemplate;
|
|
50
|
+
return {
|
|
51
|
+
...(queryTemplate ? { queryTemplate } : {}),
|
|
52
|
+
...(documentTemplate ? { documentTemplate } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Put `text` into `template`; no template returns the text unchanged. */
|
|
56
|
+
export function applyEmbeddingTemplate(template, text) {
|
|
57
|
+
if (!template)
|
|
58
|
+
return text;
|
|
59
|
+
// A replacer function keeps `$&`-style sequences in the text literal.
|
|
60
|
+
return template.includes("{text}") ? template.replaceAll("{text}", () => text) : `${template}${text}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The document template's part of the embedding fingerprint: empty without a
|
|
64
|
+
* template, so vectors embedded without one stay current.
|
|
65
|
+
*/
|
|
66
|
+
export function documentTemplateFingerprint(config) {
|
|
67
|
+
const template = resolveEmbeddingProfile(config).documentTemplate;
|
|
68
|
+
if (!template)
|
|
69
|
+
return "";
|
|
70
|
+
return `|doc:${createHash("sha256").update(template).digest("hex").slice(0, 12)}`;
|
|
71
|
+
}
|
|
@@ -428,7 +428,7 @@ export class RemoteEmbedder {
|
|
|
428
428
|
textBatches.splice(dispatchedBatchCount, textBatches.length - dispatchedBatchCount, ...replanned);
|
|
429
429
|
}
|
|
430
430
|
warnVerbose(`[embed] provider rejected a ${rejectedRequestTokens}-token request as over its context; request budget lowered to ${effectiveTokenBudget.toLocaleString()} for the rest of this run`);
|
|
431
|
-
commitBatch(rejectedIndices, rejectedIndices.map(() => undefined),
|
|
431
|
+
commitBatch(rejectedIndices, rejectedIndices.map(() => undefined), {
|
|
432
432
|
batchIndex: rejectedBatchIndex,
|
|
433
433
|
batchCount: textBatches.length,
|
|
434
434
|
docCount: rejectedIndices.length,
|
|
@@ -471,7 +471,7 @@ export class RemoteEmbedder {
|
|
|
471
471
|
// fabricated "batch-request-failed" skip (#954). Checked (and rethrown)
|
|
472
472
|
// once the pool drains, the same way `signal?.aborted` is today.
|
|
473
473
|
let firstOnBatchError;
|
|
474
|
-
const commitBatch = (indices, embeddings,
|
|
474
|
+
const commitBatch = (indices, embeddings, outcome) => {
|
|
475
475
|
if (!onBatch)
|
|
476
476
|
return;
|
|
477
477
|
// Once persistence has failed once, an already in-flight batch that
|
|
@@ -481,7 +481,7 @@ export class RemoteEmbedder {
|
|
|
481
481
|
if (firstOnBatchError !== undefined)
|
|
482
482
|
return;
|
|
483
483
|
try {
|
|
484
|
-
onBatch(indices, embeddings,
|
|
484
|
+
onBatch(indices, embeddings, outcome);
|
|
485
485
|
}
|
|
486
486
|
catch (err) {
|
|
487
487
|
firstOnBatchError = err;
|
|
@@ -523,16 +523,14 @@ export class RemoteEmbedder {
|
|
|
523
523
|
const requestTimeoutMs = scaleEmbeddingTimeoutMs(configuredTimeoutMs, requestTokens, effectiveTokenBudget);
|
|
524
524
|
const requestStart = Date.now();
|
|
525
525
|
let batchEmbeddings;
|
|
526
|
-
let responseModel;
|
|
527
526
|
let outcome;
|
|
528
527
|
let failureReason;
|
|
529
528
|
try {
|
|
530
|
-
const
|
|
529
|
+
const vectors = await this.requestBatch(batch, headers, ollamaOpts, requestTimeoutMs, signal);
|
|
531
530
|
for (let k = 0; k < indices.length; k++) {
|
|
532
531
|
results[indices[k]] = vectors[k];
|
|
533
532
|
}
|
|
534
533
|
batchEmbeddings = indices.map((i) => results[i]);
|
|
535
|
-
responseModel = model;
|
|
536
534
|
outcome = "stored";
|
|
537
535
|
}
|
|
538
536
|
catch (err) {
|
|
@@ -571,7 +569,7 @@ export class RemoteEmbedder {
|
|
|
571
569
|
// failed or succeeded yet, so there is nothing to persist:
|
|
572
570
|
// `embeddings` are all `undefined` and the materializer's onBatch
|
|
573
571
|
// must not touch storage for this event.
|
|
574
|
-
commitBatch(indices, indices.map(() => undefined),
|
|
572
|
+
commitBatch(indices, indices.map(() => undefined), {
|
|
575
573
|
batchIndex,
|
|
576
574
|
batchCount: textBatches.length,
|
|
577
575
|
docCount: indices.length,
|
|
@@ -635,7 +633,7 @@ export class RemoteEmbedder {
|
|
|
635
633
|
outcome = "failed";
|
|
636
634
|
failureReason = message;
|
|
637
635
|
}
|
|
638
|
-
commitBatch(indices, batchEmbeddings,
|
|
636
|
+
commitBatch(indices, batchEmbeddings, {
|
|
639
637
|
batchIndex,
|
|
640
638
|
batchCount: textBatches.length,
|
|
641
639
|
docCount: indices.length,
|
|
@@ -665,7 +663,7 @@ export class RemoteEmbedder {
|
|
|
665
663
|
// Never made a request — excluded from the default-level per-batch
|
|
666
664
|
// line (there is no request outcome to report), but still counted
|
|
667
665
|
// in the run's oversized-skip total via `onSkip` above.
|
|
668
|
-
commitBatch([idx], [undefined],
|
|
666
|
+
commitBatch([idx], [undefined], {
|
|
669
667
|
batchIndex,
|
|
670
668
|
batchCount: textBatches.length,
|
|
671
669
|
docCount: 1,
|
|
@@ -701,11 +699,8 @@ export class RemoteEmbedder {
|
|
|
701
699
|
return results;
|
|
702
700
|
}
|
|
703
701
|
/**
|
|
704
|
-
* Send one batch request and return its embeddings in input order
|
|
705
|
-
*
|
|
706
|
-
* used by the embedding-fingerprint canary to verify a config-string
|
|
707
|
-
* rename against what the endpoint actually served, not just re-assert the
|
|
708
|
-
* configured string. Throws on any failure.
|
|
702
|
+
* Send one batch request and return its embeddings in input order. Throws
|
|
703
|
+
* on any failure.
|
|
709
704
|
*
|
|
710
705
|
* `timeoutMs` is the caller's ALREADY-SCALED per-request timeout (#954
|
|
711
706
|
* — see {@link scaleEmbeddingTimeoutMs}), not re-resolved here:
|
|
@@ -756,7 +751,7 @@ export class RemoteEmbedder {
|
|
|
756
751
|
}
|
|
757
752
|
results.push(l2Normalize(d.embedding));
|
|
758
753
|
}
|
|
759
|
-
return
|
|
754
|
+
return results;
|
|
760
755
|
}
|
|
761
756
|
buildHeaders() {
|
|
762
757
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -491,11 +491,10 @@ function formatContextHint(llmRunner) {
|
|
|
491
491
|
return llmRunner.connection.contextLength ? `, configured contextLength=${llmRunner.connection.contextLength}` : "";
|
|
492
492
|
}
|
|
493
493
|
/** Dispatch one raw graph prompt through the common resolved-request adapter. */
|
|
494
|
-
async function callGraphLlm(runner, messages, request,
|
|
494
|
+
async function callGraphLlm(runner, messages, request, onNotices) {
|
|
495
495
|
return callStructured({
|
|
496
496
|
feature: "graph_extraction",
|
|
497
497
|
runner,
|
|
498
|
-
...(lease ? { lease } : {}),
|
|
499
498
|
messages,
|
|
500
499
|
request,
|
|
501
500
|
onNotices,
|
|
@@ -623,7 +622,7 @@ export async function extractGraphFromBodies(llmRunner, bodies, signal, akmConfi
|
|
|
623
622
|
signal,
|
|
624
623
|
responseSchema: batchResponseSchema,
|
|
625
624
|
onRetryAttempt: () => bumpTelemetry(options.telemetry, "retryAttempts"),
|
|
626
|
-
}, options.
|
|
625
|
+
}, options.onNotices);
|
|
627
626
|
if (!raw)
|
|
628
627
|
return { kind: "value", value: null };
|
|
629
628
|
// Array-preferring salvage (#635): the batch contract is a top-level
|
|
@@ -643,7 +642,7 @@ export async function extractGraphFromBodies(llmRunner, bodies, signal, akmConfi
|
|
|
643
642
|
timeoutMs: llmRunner.timeoutMs,
|
|
644
643
|
signal,
|
|
645
644
|
responseSchema: batchResponseSchema,
|
|
646
|
-
}, options.
|
|
645
|
+
}, options.onNotices);
|
|
647
646
|
parsed = retryRaw ? parseEmbeddedJsonResponse(retryRaw, { expect: "array" }) : undefined;
|
|
648
647
|
}
|
|
649
648
|
if (!Array.isArray(parsed)) {
|
|
@@ -807,7 +806,6 @@ export async function extractGraphFromBody(llmRunner, body, signal, akmConfig, o
|
|
|
807
806
|
feature: "graph_extraction",
|
|
808
807
|
akmConfig,
|
|
809
808
|
runner: llmRunner,
|
|
810
|
-
...(options.lease ? { lease: options.lease } : {}),
|
|
811
809
|
messages: [
|
|
812
810
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
813
811
|
{ role: "user", content: userPrompt },
|
|
@@ -863,10 +861,3 @@ export async function extractGraphFromBody(llmRunner, body, signal, akmConfig, o
|
|
|
863
861
|
result.truncatedChunks = (result.truncatedChunks ?? 0) + truncatedChunkCount;
|
|
864
862
|
return result;
|
|
865
863
|
}
|
|
866
|
-
// deduplicateGraph lives in src/indexer/graph/graph-dedup.ts (pure utility, no
|
|
867
|
-
// LLM calls) — import it from there directly. The re-export that used to live
|
|
868
|
-
// here created a value edge back to graph-dedup.ts, which (via its type-only
|
|
869
|
-
// import of GraphExtraction/GraphRelation below) formed a 2-file import cycle
|
|
870
|
-
// (chunk 9 WI-9.8 KILL 4 sever). No src or test consumer used this re-export
|
|
871
|
-
// (graph-extraction.ts and the test suite already import graph-dedup.ts
|
|
872
|
-
// directly), so it is deleted outright rather than repointed.
|
package/dist/llm/index-passes.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { warn } from "../core/warn.js";
|
|
5
5
|
import { cloneExecutionJsonObject } from "../execution/json.js";
|
|
6
|
-
import {
|
|
7
|
-
import { prepareInlineExecution } from "../integrations/agent/inline-execution.js";
|
|
6
|
+
import { buildExecution, resolveExecution } from "../integrations/agent/execution.js";
|
|
8
7
|
const NO_LOWERING_NOTICES = Object.freeze([]);
|
|
9
8
|
function own(value, key) {
|
|
10
9
|
return value !== undefined && Object.hasOwn(value, key);
|
|
@@ -39,14 +38,13 @@ export function resolveIndexPassExecution(passName, config) {
|
|
|
39
38
|
...indexExecutionDefaults(defaults),
|
|
40
39
|
...(!own(defaults, "engine") && fallbackLlmEngine ? { engine: fallbackLlmEngine } : {}),
|
|
41
40
|
};
|
|
42
|
-
const prepared =
|
|
41
|
+
const prepared = resolveExecution({
|
|
43
42
|
content: "",
|
|
44
43
|
config,
|
|
45
|
-
invocationKind: "direct",
|
|
46
44
|
invocationDefaults,
|
|
47
45
|
current: indexExecutionDefaults(pass),
|
|
48
46
|
});
|
|
49
|
-
const lowered =
|
|
47
|
+
const lowered = buildExecution(prepared.request, prepared.runner);
|
|
50
48
|
if (lowered.runner.kind !== "llm") {
|
|
51
49
|
warn("[akm] Index pass %s requires an LLM engine; %s is not one. Skipping this pass.", passName, selectedEngine);
|
|
52
50
|
return Object.freeze({ runner: undefined, notices: NO_LOWERING_NOTICES });
|
package/dist/llm/memory-infer.js
CHANGED
|
@@ -68,7 +68,7 @@ const DERIVED_MEMORY_JSON_SCHEMA = {
|
|
|
68
68
|
* feature gate, error classification, and onFallback hook are honoured uniformly
|
|
69
69
|
* (Fix C5).
|
|
70
70
|
*/
|
|
71
|
-
export async function compressMemoryToDerivedMemory(llmRunner, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt, onNotices
|
|
71
|
+
export async function compressMemoryToDerivedMemory(llmRunner, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt, onNotices) {
|
|
72
72
|
const trimmedBody = body.trim();
|
|
73
73
|
if (!trimmedBody)
|
|
74
74
|
return undefined;
|
|
@@ -83,7 +83,6 @@ export async function compressMemoryToDerivedMemory(llmRunner, body, signal, akm
|
|
|
83
83
|
feature: "memory_inference",
|
|
84
84
|
akmConfig,
|
|
85
85
|
runner: llmRunner,
|
|
86
|
-
...(lease ? { lease } : {}),
|
|
87
86
|
messages: [
|
|
88
87
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
89
88
|
{ role: "user", content: userPrompt },
|
|
@@ -22,7 +22,7 @@ const SYSTEM_PROMPT = metadataEnhanceSystemPrompt;
|
|
|
22
22
|
* `akmConfig` is `undefined` the gate is bypassed entirely: the LLM call runs
|
|
23
23
|
* unconditionally and errors propagate to direct callers such as tests.
|
|
24
24
|
*/
|
|
25
|
-
export async function enhanceMetadata(runner, entry, fileContent, signal, akmConfig, onNotices
|
|
25
|
+
export async function enhanceMetadata(runner, entry, fileContent, signal, akmConfig, onNotices) {
|
|
26
26
|
const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
|
|
27
27
|
if (entry.description)
|
|
28
28
|
contextParts.push(`Current description: ${entry.description}`);
|
|
@@ -52,7 +52,6 @@ Return ONLY the JSON object, no explanation.`;
|
|
|
52
52
|
feature: "metadata_enhance",
|
|
53
53
|
akmConfig,
|
|
54
54
|
runner,
|
|
55
|
-
...(lease ? { lease } : {}),
|
|
56
55
|
messages: [
|
|
57
56
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
58
57
|
{ role: "user", content: userPrompt },
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { ConfigError } from "../core/errors.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { buildExecution, resolveExecution } from "../integrations/agent/execution.js";
|
|
6
|
+
import { runExecution } from "../integrations/agent/runner-dispatch.js";
|
|
7
7
|
import { isContextSizeError, LlmCallError } from "./client.js";
|
|
8
8
|
import { isLlmFeatureEnabled, tryLlmFeature, } from "./feature-gate.js";
|
|
9
9
|
/**
|
|
@@ -62,22 +62,6 @@ function dispatchFailure(result) {
|
|
|
62
62
|
const message = result.error ?? result.stderr ?? result.reason ?? "LLM dispatch failed";
|
|
63
63
|
return result.llmErrorCode ? new LlmCallError(message, result.llmErrorCode) : new Error(message);
|
|
64
64
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Validate one already-selected symbolic LLM runner before an operation makes
|
|
67
|
-
* any durable mutation. Callers must apply their feature/authorization gates
|
|
68
|
-
* first. Acquisition crosses the canonical prepare -> lower -> lease boundary,
|
|
69
|
-
* so required credentials are snapshotted by the same central authority as a
|
|
70
|
-
* real call without contacting the provider.
|
|
71
|
-
*/
|
|
72
|
-
export async function preflightStructuredLlmRunner(runner) {
|
|
73
|
-
const prepared = prepareInlineExecutionWithRunner({
|
|
74
|
-
content: "Validate the selected LLM runner before operation dispatch.",
|
|
75
|
-
runner,
|
|
76
|
-
invocationKind: "direct",
|
|
77
|
-
});
|
|
78
|
-
const lowered = lowerResolvedExecutionRequestWithRunner(prepared.request, prepared.runner);
|
|
79
|
-
return acquireLoweredExecutionDispatchLease(lowered);
|
|
80
|
-
}
|
|
81
65
|
export async function callStructured(opts) {
|
|
82
66
|
const { feature, akmConfig, enabled, messages, request, parse, onError, fallback, onFallback } = opts;
|
|
83
67
|
// A disabled feature owns a true no-work path: it does not need a runner,
|
|
@@ -97,19 +81,16 @@ export async function callStructured(opts) {
|
|
|
97
81
|
const terminal = requireTerminalUserMessage(messages);
|
|
98
82
|
const prepareInvocation = () => {
|
|
99
83
|
const current = resolveStructuredCurrent(opts.current, request);
|
|
100
|
-
const prepared =
|
|
84
|
+
const prepared = resolveExecution({
|
|
101
85
|
content: terminal.content,
|
|
102
86
|
conversation: terminal.conversation,
|
|
103
87
|
runner,
|
|
104
|
-
invocationKind: "direct",
|
|
105
88
|
...(current ? { current } : {}),
|
|
106
|
-
...(opts.authorizeTools ? { authorizeTools: opts.authorizeTools } : {}),
|
|
107
89
|
});
|
|
108
|
-
const lowered =
|
|
90
|
+
const lowered = buildExecution(prepared.request, prepared.runner);
|
|
109
91
|
opts.onNotices?.(lowered.notices);
|
|
110
92
|
return async () => {
|
|
111
|
-
const result = await
|
|
112
|
-
...(opts.lease ? { lease: opts.lease } : {}),
|
|
93
|
+
const result = await runExecution(lowered, {
|
|
113
94
|
...(request?.chat ? { chat: request.chat } : {}),
|
|
114
95
|
...(request?.onRetryAttempt ? { onRetryAttempt: request.onRetryAttempt } : {}),
|
|
115
96
|
...(own(request, "signal") ? { runOptions: { signal: request?.signal } } : {}),
|
|
@@ -1,6 +1,29 @@
|
|
|
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
|
+
* Generic `md` / `html` / `text` rendering of a shaped output envelope (D7).
|
|
6
|
+
*
|
|
7
|
+
* Every command gets `json`, `jsonl`, and `yaml` for free because those are
|
|
8
|
+
* serializations of the envelope rather than renderings of it. `md`, `html`,
|
|
9
|
+
* and `text` are renderings, and before D7 only `akm health` had `md`/`html`:
|
|
10
|
+
* `md` silently emitted the JSON envelope everywhere else and `html` threw.
|
|
11
|
+
* `text` had the same silent-JSON gap for any command with no registered
|
|
12
|
+
* text formatter, closed later than `md`/`html` — see `renderGenericText`
|
|
13
|
+
* below for why it is its own function rather than a reuse of
|
|
14
|
+
* `renderGenericMarkdown`.
|
|
15
|
+
*
|
|
16
|
+
* These functions close that gap structurally rather than per command. A
|
|
17
|
+
* command that registers a bespoke renderer (`akm health`) still wins; every
|
|
18
|
+
* other command falls back to a real rendering derived from whatever its
|
|
19
|
+
* envelope happens to contain. The shape is discovered, not declared, so a new
|
|
20
|
+
* command is covered the day it is added.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately dependency-free apart from the shared detail types and
|
|
23
|
+
* `escapeHtml`: this module sits underneath the per-command renderer
|
|
24
|
+
* registries and must not import them.
|
|
25
|
+
*/
|
|
26
|
+
import { escapeHtml } from "./html-render.js";
|
|
4
27
|
/**
|
|
5
28
|
* True when `value` is a non-empty array of plain objects — the shape worth
|
|
6
29
|
* rendering as a table rather than as a nested list. Uniformity is judged on
|
|
@@ -106,17 +129,6 @@ export function renderGenericMarkdown(command, value) {
|
|
|
106
129
|
return `${rendered}\n`;
|
|
107
130
|
}
|
|
108
131
|
// ── HTML ─────────────────────────────────────────────────────────────────────
|
|
109
|
-
const HTML_ESCAPES = {
|
|
110
|
-
"&": "&",
|
|
111
|
-
"<": "<",
|
|
112
|
-
">": ">",
|
|
113
|
-
'"': """,
|
|
114
|
-
"'": "'",
|
|
115
|
-
};
|
|
116
|
-
/** Escape every value that reaches the document — this is untrusted content. */
|
|
117
|
-
function escapeHtml(text) {
|
|
118
|
-
return text.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
|
|
119
|
-
}
|
|
120
132
|
function htmlTable(rows) {
|
|
121
133
|
const columns = tableColumns(rows);
|
|
122
134
|
if (columns.length === 0)
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
* `--format html` rendering primitives (#582).
|
|
6
6
|
*
|
|
7
7
|
* Templates live in `src/assets/templates/html/` (mirrored to
|
|
8
|
-
* `dist/assets/templates/html/` by `scripts/copy-assets.ts`).
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
8
|
+
* `dist/assets/templates/html/` by `scripts/copy-assets.ts`). `akm health` is
|
|
9
|
+
* the only command with a bespoke `<command>.html` template (substituted here
|
|
10
|
+
* via plain `%%TOKEN%%` string replacement — no template engine, by design);
|
|
11
|
+
* `src/cli/shared.ts`'s `output()` calls it directly for `command === "health"`
|
|
12
|
+
* and every other command falls back to `renderGenericHtml` (`./generic-render`).
|
|
13
|
+
* `--format html` therefore works for every command (D7) — this module's
|
|
14
|
+
* template path is just health's opt-in bespoke rendering, not a format-wide
|
|
15
|
+
* gate.
|
|
14
16
|
*/
|
|
15
17
|
import fs from "node:fs";
|
|
16
18
|
import path from "node:path";
|
|
@@ -81,10 +83,11 @@ function readTemplate(templatePath) {
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
|
-
* Minimal HTML entity escaping for text interpolated into
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
86
|
+
* Minimal HTML entity escaping for text interpolated into a document — a
|
|
87
|
+
* template substitution here, or a value placed into `generic-render.ts`'s
|
|
88
|
+
* envelope rendering. Escapes the single quote as well as the double quote so
|
|
89
|
+
* escaped values are safe in both `"…"` and `'…'` attribute contexts, not only
|
|
90
|
+
* the double-quoted attributes the bundled templates use today.
|
|
88
91
|
*/
|
|
89
92
|
export function escapeHtml(value) {
|
|
90
93
|
return value
|