akm-cli 0.9.0-beta.6 → 0.9.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +663 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -14
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-cli.js +220 -227
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +153 -147
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +76 -72
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1083
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1313 -1278
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +243 -599
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +55 -4
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +160 -7
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +32 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +510 -2537
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +115 -0
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +212 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +7 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -882
- package/dist/commands/read/curate.js +419 -103
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +29 -41
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +15 -1
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +21 -4
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +389 -58
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +21 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +142 -1091
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +356 -780
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +103 -24
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +106 -0
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +291 -310
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +9 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +39 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +7 -1
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +59 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +69 -67
- package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
- package/dist/integrations/harnesses/claude/index.js +2 -0
- package/dist/integrations/harnesses/claude/session-log.js +11 -1
- package/dist/integrations/harnesses/index.js +2 -3
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +2 -0
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
- package/dist/integrations/harnesses/types.js +1 -0
- package/dist/integrations/session-logs/index.js +16 -0
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +57 -15
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +85 -14
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +2 -1
- package/dist/output/text/helpers.js +29 -1
- package/dist/output/text/workflow.js +1 -0
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/scripts/migrate-storage.js +2718 -2354
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +891 -597
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/tar-utils.js +16 -8
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +0 -0
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/cli.js +1 -0
- package/dist/workflows/db.js +3 -4
- package/dist/workflows/runtime/runs.js +43 -118
- package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +5 -4
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
|
-
import {
|
|
6
|
-
import { output, parseAllFlagValues, runWithJsonErrors } from "../cli/shared.js";
|
|
5
|
+
import { defineJsonCommand, output, parseAllFlagValues } from "../cli/shared.js";
|
|
7
6
|
import { parseAssetRef } from "../core/asset/asset-ref.js";
|
|
8
7
|
import { assembleAsset } from "../core/asset/asset-serialize.js";
|
|
9
8
|
import { parseFrontmatter, parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
@@ -11,10 +10,9 @@ import { writeFileAtomic } from "../core/common.js";
|
|
|
11
10
|
import { FEEDBACK_FAILURE_MODES, loadConfig } from "../core/config/config.js";
|
|
12
11
|
import { UsageError } from "../core/errors.js";
|
|
13
12
|
import { appendEvent } from "../core/events.js";
|
|
13
|
+
import { getDbPath } from "../core/paths.js";
|
|
14
14
|
import { warn } from "../core/warn.js";
|
|
15
15
|
import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, getEntryFilePathById, openExistingDatabase, } from "../indexer/db/db.js";
|
|
16
|
-
import { ensureIndex } from "../indexer/ensure-index.js";
|
|
17
|
-
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
18
16
|
import { countFeedbackSignals, insertUsageEvent } from "../indexer/usage/usage-events.js";
|
|
19
17
|
// ── Tag validation ────────────────────────────────────────────────────────────
|
|
20
18
|
const TAG_KEY_RE = /^[a-z_][a-z0-9_]*$/;
|
|
@@ -109,7 +107,7 @@ function appendLessonStrength(type, name, feedbackRef) {
|
|
|
109
107
|
return { strength: strengthList.length };
|
|
110
108
|
}
|
|
111
109
|
// ── Command definition ────────────────────────────────────────────────────────
|
|
112
|
-
export const feedbackCommand =
|
|
110
|
+
export const feedbackCommand = defineJsonCommand({
|
|
113
111
|
meta: {
|
|
114
112
|
name: "feedback",
|
|
115
113
|
description: "Record positive or negative feedback for any indexed stash asset.\n\n" +
|
|
@@ -153,163 +151,171 @@ export const feedbackCommand = defineCommand({
|
|
|
153
151
|
"`lessonStrength[]` frontmatter array (dedup, idempotent). Ignored on non-lesson targets.",
|
|
154
152
|
},
|
|
155
153
|
},
|
|
156
|
-
run({ args }) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
154
|
+
async run({ args }) {
|
|
155
|
+
const ref = (args.ref ?? "").trim();
|
|
156
|
+
if (!ref) {
|
|
157
|
+
throw new UsageError("Asset ref is required. Usage: akm feedback <ref> --positive|--negative", "MISSING_REQUIRED_ARGUMENT", "Pass a ref like `skill:deploy` and either --positive or --negative.");
|
|
158
|
+
}
|
|
159
|
+
parseAssetRef(ref);
|
|
160
|
+
if (args.positive && args.negative) {
|
|
161
|
+
throw new UsageError("Specify either --positive or --negative, not both.");
|
|
162
|
+
}
|
|
163
|
+
if (!args.positive && !args.negative) {
|
|
164
|
+
throw new UsageError("Specify --positive or --negative.");
|
|
165
|
+
}
|
|
166
|
+
const signal = args.positive ? "positive" : "negative";
|
|
167
|
+
const reason = args.reason;
|
|
168
|
+
// F-3 / #384: Validate --failure-mode against the curated enum.
|
|
169
|
+
const failureMode = args["failure-mode"]?.trim() || undefined;
|
|
170
|
+
if (failureMode) {
|
|
171
|
+
if (args.positive) {
|
|
172
|
+
throw new UsageError("--failure-mode is only valid for negative feedback.", "INVALID_FLAG_VALUE", "Remove --failure-mode or switch to --negative.");
|
|
161
173
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
174
|
+
const cfg = loadConfig();
|
|
175
|
+
const allowedModes = cfg.feedback?.allowedFailureModes ?? FEEDBACK_FAILURE_MODES;
|
|
176
|
+
if (allowedModes.length > 0 && !allowedModes.includes(failureMode)) {
|
|
177
|
+
throw new UsageError(`Invalid --failure-mode "${failureMode}". Accepted values: ${allowedModes.join(", ")}.`, "INVALID_FLAG_VALUE", `Use one of: ${allowedModes.join(", ")}`);
|
|
165
178
|
}
|
|
166
|
-
|
|
167
|
-
|
|
179
|
+
}
|
|
180
|
+
if (args.negative === true && !reason?.trim()) {
|
|
181
|
+
// F-3 / #384: Default requireReason is now true. Load config to allow
|
|
182
|
+
// operators to opt out via feedback.requireReason: false in akm.json.
|
|
183
|
+
const cfg = loadConfig();
|
|
184
|
+
const requireReason = cfg.feedback?.requireReason ?? true; // Default: true (F-3 / #384)
|
|
185
|
+
if (requireReason) {
|
|
186
|
+
throw new UsageError("Negative feedback requires --reason (structured failure signals are needed for distillation). " +
|
|
187
|
+
"Use --failure-mode for a curated taxonomy or --reason for free text. " +
|
|
188
|
+
"Set feedback.requireReason: false in akm.json to downgrade to a warning.", "MISSING_REQUIRED_ARGUMENT", `Hint: akm feedback ${ref} --negative --reason "..." [--failure-mode incorrect|outdated|dangerous|incomplete|redundant]`);
|
|
168
189
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// F-3 / #384: Validate --failure-mode against the curated enum.
|
|
172
|
-
const failureMode = args["failure-mode"]?.trim() || undefined;
|
|
173
|
-
if (failureMode) {
|
|
174
|
-
if (args.positive) {
|
|
175
|
-
throw new UsageError("--failure-mode is only valid for negative feedback.", "INVALID_FLAG_VALUE", "Remove --failure-mode or switch to --negative.");
|
|
176
|
-
}
|
|
177
|
-
const cfg = loadConfig();
|
|
178
|
-
const allowedModes = cfg.feedback?.allowedFailureModes ?? FEEDBACK_FAILURE_MODES;
|
|
179
|
-
if (allowedModes.length > 0 && !allowedModes.includes(failureMode)) {
|
|
180
|
-
throw new UsageError(`Invalid --failure-mode "${failureMode}". Accepted values: ${allowedModes.join(", ")}.`, "INVALID_FLAG_VALUE", `Use one of: ${allowedModes.join(", ")}`);
|
|
181
|
-
}
|
|
190
|
+
else {
|
|
191
|
+
warn("Warning: negative feedback without --reason provides less distillation signal.");
|
|
182
192
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
}
|
|
194
|
+
const rawTags = parseAllFlagValues("--tag");
|
|
195
|
+
const validatedTags = validateFeedbackTags(rawTags);
|
|
196
|
+
const metadataObj = {
|
|
197
|
+
signal,
|
|
198
|
+
...(reason?.trim() ? { reason: reason.trim() } : {}),
|
|
199
|
+
...(failureMode ? { failureMode } : {}),
|
|
200
|
+
...(validatedTags.length > 0 ? { tags: validatedTags } : {}),
|
|
201
|
+
};
|
|
202
|
+
const metadataStr = Object.keys(metadataObj).length > 1 ? JSON.stringify(metadataObj) : undefined;
|
|
203
|
+
// Feedback only needs the index to exist, not to be current. A stale index
|
|
204
|
+
// is fine — the ref lookup works against any populated DB. We do NOT call
|
|
205
|
+
// ensureIndex here: it either blocks (3+ min inline reindex) or spawns a
|
|
206
|
+
// background process that holds the writer lock, causing the feedback write
|
|
207
|
+
// to spin-wait for the full reindex duration. If the DB is absent we give a
|
|
208
|
+
// clear error below rather than silently triggering a rebuild.
|
|
209
|
+
if (!fs.existsSync(getDbPath())) {
|
|
210
|
+
throw new UsageError("Index not found. Run 'akm index' first to build the index before recording feedback.", "MISSING_REQUIRED_ARGUMENT", "akm index");
|
|
211
|
+
}
|
|
212
|
+
// Feedback writes exactly 2 rows (usage_events + utility_score). SQLite
|
|
213
|
+
// WAL mode + busy_timeout=30s handles concurrent access with an ongoing
|
|
214
|
+
// `akm improve` run without needing the application-level writer lock.
|
|
215
|
+
// The lock was originally needed to prevent feedback from racing a
|
|
216
|
+
// background reindex it spawned — now that ensureIndex is removed, holding
|
|
217
|
+
// the lock only causes feedback to block for the full improve run duration.
|
|
218
|
+
let utilityResult;
|
|
219
|
+
const db = openExistingDatabase();
|
|
220
|
+
try {
|
|
221
|
+
const entryId = findEntryIdByRef(db, ref);
|
|
222
|
+
if (entryId === undefined) {
|
|
223
|
+
throw new UsageError(`Ref "${ref}" is not in the index. ` +
|
|
224
|
+
"Run 'akm search' to verify the asset exists, then 'akm index' if it was recently added.");
|
|
196
225
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
226
|
+
// Persist the feedback signal into usage_events. For positive signals,
|
|
227
|
+
// the EMA utility score is updated immediately on the next read path.
|
|
228
|
+
// For negative signals, the score is adjusted the next time `akm index`
|
|
229
|
+
// runs — the signal is durable in the DB but does NOT suppress ranking
|
|
230
|
+
// in search results until after reindexing.
|
|
231
|
+
insertUsageEvent(db, {
|
|
232
|
+
event_type: "feedback",
|
|
233
|
+
entry_ref: ref,
|
|
234
|
+
entry_id: entryId,
|
|
200
235
|
signal,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
236
|
+
metadata: metadataStr,
|
|
237
|
+
});
|
|
238
|
+
// Apply feedback-derived utility score adjustment immediately so that
|
|
239
|
+
// positive/negative signals influence search ranking without requiring
|
|
240
|
+
// a full reindex. We query the total accumulated feedback counts from
|
|
241
|
+
// usage_events so the delta reflects the entire signal history.
|
|
242
|
+
// Uses MemRL bounded-step EMA (F-5 / #386, arXiv:2601.03192).
|
|
243
|
+
try {
|
|
244
|
+
const { pos, neg } = countFeedbackSignals(db, entryId);
|
|
245
|
+
utilityResult = applyFeedbackToUtilityScore(db, entryId, pos, neg);
|
|
210
246
|
}
|
|
211
|
-
|
|
212
|
-
|
|
247
|
+
catch {
|
|
248
|
+
// best-effort — feedback recording succeeds even if utility update fails
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
closeDatabase(db);
|
|
253
|
+
}
|
|
254
|
+
appendEvent({
|
|
255
|
+
eventType: "feedback",
|
|
256
|
+
ref,
|
|
257
|
+
metadata: metadataObj,
|
|
258
|
+
});
|
|
259
|
+
// F-5 / #386: When a high-utility asset crosses below the review threshold,
|
|
260
|
+
// auto-create a review-needed escalation proposal so a human can confirm
|
|
261
|
+
// whether the negative feedback is valid before the asset falls out of
|
|
262
|
+
// the improve loop. Best-effort — failure is logged but does not fail the
|
|
263
|
+
// feedback command.
|
|
264
|
+
// Emit a structured event rather than a proposal so the review-needed
|
|
265
|
+
// signal is queryable via `akm events list --type improve_review_needed`
|
|
266
|
+
// without risking accidental asset overwrite if the proposal is accepted.
|
|
267
|
+
if (utilityResult?.crossedReviewThreshold) {
|
|
213
268
|
try {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// in search results until after reindexing.
|
|
224
|
-
insertUsageEvent(db, {
|
|
225
|
-
event_type: "feedback",
|
|
226
|
-
entry_ref: ref,
|
|
227
|
-
entry_id: entryId,
|
|
228
|
-
signal,
|
|
229
|
-
metadata: metadataStr,
|
|
269
|
+
appendEvent({
|
|
270
|
+
eventType: "improve_review_needed",
|
|
271
|
+
ref,
|
|
272
|
+
metadata: {
|
|
273
|
+
previousUtility: utilityResult.previousUtility,
|
|
274
|
+
nextUtility: utilityResult.nextUtility,
|
|
275
|
+
reason: reason?.trim() ?? null,
|
|
276
|
+
failureMode: failureMode ?? null,
|
|
277
|
+
},
|
|
230
278
|
});
|
|
231
|
-
// Apply feedback-derived utility score adjustment immediately so that
|
|
232
|
-
// positive/negative signals influence search ranking without requiring
|
|
233
|
-
// a full reindex. We query the total accumulated feedback counts from
|
|
234
|
-
// usage_events so the delta reflects the entire signal history.
|
|
235
|
-
// Uses MemRL bounded-step EMA (F-5 / #386, arXiv:2601.03192).
|
|
236
|
-
try {
|
|
237
|
-
const { pos, neg } = countFeedbackSignals(db, entryId);
|
|
238
|
-
utilityResult = applyFeedbackToUtilityScore(db, entryId, pos, neg);
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
// best-effort — feedback recording succeeds even if utility update fails
|
|
242
|
-
}
|
|
243
279
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
appendEvent({
|
|
248
|
-
eventType: "feedback",
|
|
249
|
-
ref,
|
|
250
|
-
metadata: metadataObj,
|
|
251
|
-
});
|
|
252
|
-
// F-5 / #386: When a high-utility asset crosses below the review threshold,
|
|
253
|
-
// auto-create a review-needed escalation proposal so a human can confirm
|
|
254
|
-
// whether the negative feedback is valid before the asset falls out of
|
|
255
|
-
// the improve loop. Best-effort — failure is logged but does not fail the
|
|
256
|
-
// feedback command.
|
|
257
|
-
// Emit a structured event rather than a proposal so the review-needed
|
|
258
|
-
// signal is queryable via `akm events list --type improve_review_needed`
|
|
259
|
-
// without risking accidental asset overwrite if the proposal is accepted.
|
|
260
|
-
if (utilityResult?.crossedReviewThreshold) {
|
|
261
|
-
try {
|
|
262
|
-
appendEvent({
|
|
263
|
-
eventType: "improve_review_needed",
|
|
264
|
-
ref,
|
|
265
|
-
metadata: {
|
|
266
|
-
previousUtility: utilityResult.previousUtility,
|
|
267
|
-
nextUtility: utilityResult.nextUtility,
|
|
268
|
-
reason: reason?.trim() ?? null,
|
|
269
|
-
failureMode: failureMode ?? null,
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
catch (escalationErr) {
|
|
274
|
-
warn(`[feedback] Could not emit review-needed event for ${ref}: ${escalationErr instanceof Error ? escalationErr.message : String(escalationErr)}`);
|
|
275
|
-
}
|
|
280
|
+
catch (escalationErr) {
|
|
281
|
+
warn(`[feedback] Could not emit review-needed event for ${ref}: ${escalationErr instanceof Error ? escalationErr.message : String(escalationErr)}`);
|
|
276
282
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
283
|
+
}
|
|
284
|
+
// Phase 7A / Advantage D4b: --applied-to credits a lesson. When the
|
|
285
|
+
// target is a `lesson:<name>` ref and the signal is positive, append
|
|
286
|
+
// the feedback ref to the target lesson's `lessonStrength[]`
|
|
287
|
+
// frontmatter array (dedup, idempotent). Non-lesson targets are
|
|
288
|
+
// ignored. Failures here are warnings — feedback recording is the
|
|
289
|
+
// primary contract and must not regress on lesson-write errors.
|
|
290
|
+
const appliedToRaw = args["applied-to"]?.trim();
|
|
291
|
+
let appliedToResult = null;
|
|
292
|
+
if (appliedToRaw && signal === "positive") {
|
|
293
|
+
try {
|
|
294
|
+
const parsedApplied = parseAssetRef(appliedToRaw);
|
|
295
|
+
if (parsedApplied.type === "lesson") {
|
|
296
|
+
const updated = appendLessonStrength(parsedApplied.type, parsedApplied.name, ref);
|
|
297
|
+
if (updated) {
|
|
298
|
+
appliedToResult = { lessonRef: appliedToRaw, strength: updated.strength };
|
|
293
299
|
}
|
|
294
300
|
}
|
|
295
|
-
catch (err) {
|
|
296
|
-
warn(`[feedback] --applied-to failed for ${appliedToRaw}: ${err instanceof Error ? err.message : String(err)}`);
|
|
297
|
-
}
|
|
298
301
|
}
|
|
299
|
-
|
|
300
|
-
warn(
|
|
302
|
+
catch (err) {
|
|
303
|
+
warn(`[feedback] --applied-to failed for ${appliedToRaw}: ${err instanceof Error ? err.message : String(err)}`);
|
|
301
304
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
305
|
+
}
|
|
306
|
+
else if (appliedToRaw && signal !== "positive") {
|
|
307
|
+
warn("[feedback] --applied-to is ignored without --positive; lesson credit is only recorded on positive signals.");
|
|
308
|
+
}
|
|
309
|
+
output("feedback", {
|
|
310
|
+
ok: true,
|
|
311
|
+
ref,
|
|
312
|
+
signal,
|
|
313
|
+
reason: reason?.trim() ?? null,
|
|
314
|
+
failureMode: failureMode ?? null,
|
|
315
|
+
tags: validatedTags,
|
|
316
|
+
...(appliedToResult
|
|
317
|
+
? { appliedTo: { ref: appliedToResult.lessonRef, lessonStrength: appliedToResult.strength } }
|
|
318
|
+
: {}),
|
|
313
319
|
});
|
|
314
320
|
},
|
|
315
321
|
});
|
|
@@ -9,12 +9,9 @@
|
|
|
9
9
|
* same JSON envelope (stdout/stderr/exit-code) as the inline `runWithJsonErrors`
|
|
10
10
|
* form it replaces.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
12
|
+
import { parsePositiveIntFlag } from "../../cli/parse-args.js";
|
|
13
|
+
import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
|
|
15
14
|
import { akmGraphEntities, akmGraphEntity, akmGraphExport, akmGraphOrphans, akmGraphRelated, akmGraphRelations, akmGraphSummary, akmGraphUpdate, } from "./graph.js";
|
|
16
|
-
// Single source of truth: the routing set is derived from the subCommands keys
|
|
17
|
-
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
18
15
|
const graphSubCommands = {
|
|
19
16
|
summary: defineJsonCommand({
|
|
20
17
|
meta: { name: "summary", description: "Show entity-graph counts and quality telemetry" },
|
|
@@ -118,15 +115,10 @@ const graphSubCommands = {
|
|
|
118
115
|
},
|
|
119
116
|
}),
|
|
120
117
|
};
|
|
121
|
-
const
|
|
122
|
-
export const graphCommand = defineCommand({
|
|
118
|
+
export const graphCommand = defineGroupCommand({
|
|
123
119
|
meta: { name: "graph", description: "Inspect the indexed entity graph stored in SQLite" },
|
|
124
120
|
subCommands: graphSubCommands,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (hasSubcommand(args, GRAPH_SUBCOMMAND_SET))
|
|
128
|
-
return;
|
|
129
|
-
output("graph-summary", akmGraphSummary());
|
|
130
|
-
});
|
|
121
|
+
defaultRun() {
|
|
122
|
+
output("graph-summary", akmGraphSummary());
|
|
131
123
|
},
|
|
132
124
|
});
|
|
@@ -8,10 +8,11 @@ import { loadConfig } from "../../core/config/config.js";
|
|
|
8
8
|
import { NotFoundError, UsageError } from "../../core/errors.js";
|
|
9
9
|
import { getDbPath } from "../../core/paths.js";
|
|
10
10
|
import { warn } from "../../core/warn.js";
|
|
11
|
-
import { closeDatabase, findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot,
|
|
11
|
+
import { closeDatabase, findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot, openExistingDatabase, openIndexDatabase, } from "../../indexer/db/db.js";
|
|
12
12
|
import { loadStoredGraphSnapshot } from "../../indexer/db/graph-db.js";
|
|
13
13
|
import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
|
|
14
14
|
import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
|
|
15
|
+
import { withIndexWriterLease } from "../../indexer/index-writer-lock.js";
|
|
15
16
|
import { lookup } from "../../indexer/indexer.js";
|
|
16
17
|
import { findSourceForPath, resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
17
18
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
@@ -375,85 +376,88 @@ export async function akmGraphUpdate(options) {
|
|
|
375
376
|
}
|
|
376
377
|
}
|
|
377
378
|
const scoped = Array.isArray(options.refs) && options.refs.length > 0;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
379
|
+
return withIndexWriterLease({ purpose: "graph-update" }, async () => {
|
|
380
|
+
let candidatePaths;
|
|
381
|
+
if (scoped && options.refs) {
|
|
382
|
+
// Resolve each ref to an absolute file path while the writer lease is held
|
|
383
|
+
// so the scoped graph write sees the same index snapshot it resolved from.
|
|
384
|
+
const dbPath = getDbPath();
|
|
385
|
+
let db;
|
|
386
|
+
const resolvedPaths = new Set();
|
|
387
|
+
try {
|
|
388
|
+
db = openIndexDatabase(dbPath);
|
|
389
|
+
for (const ref of options.refs) {
|
|
390
|
+
const trimmed = ref.trim();
|
|
391
|
+
if (!trimmed)
|
|
392
|
+
continue;
|
|
393
|
+
const entryId = findEntryIdByRef(db, trimmed);
|
|
394
|
+
if (entryId === undefined) {
|
|
395
|
+
warn(`[graph] ref not found in index, skipping: ${trimmed}`);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const row = getEntryById(db, entryId);
|
|
399
|
+
if (!row?.filePath) {
|
|
400
|
+
warn(`[graph] could not resolve path for ref, skipping: ${trimmed}`);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
resolvedPaths.add(row.filePath);
|
|
394
404
|
}
|
|
395
|
-
const row = getEntryById(db, entryId);
|
|
396
|
-
if (!row?.filePath) {
|
|
397
|
-
warn(`[graph] could not resolve path for ref, skipping: ${trimmed}`);
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
resolvedPaths.add(row.filePath);
|
|
401
405
|
}
|
|
406
|
+
finally {
|
|
407
|
+
if (db)
|
|
408
|
+
closeDatabase(db);
|
|
409
|
+
}
|
|
410
|
+
if (resolvedPaths.size === 0) {
|
|
411
|
+
warn("[graph] none of the provided refs resolved to indexed paths — no extraction performed.");
|
|
412
|
+
return {
|
|
413
|
+
shape: "graph-update",
|
|
414
|
+
ok: true,
|
|
415
|
+
filesExtracted: 0,
|
|
416
|
+
entitiesUpserted: 0,
|
|
417
|
+
relationsUpserted: 0,
|
|
418
|
+
durationMs: 0,
|
|
419
|
+
scoped: true,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
candidatePaths = resolvedPaths;
|
|
402
423
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
424
|
+
const extractionFn = options.graphExtractionFn ?? runGraphExtractionPass;
|
|
425
|
+
const passOptions = candidatePaths ? { candidatePaths } : {};
|
|
426
|
+
let db;
|
|
427
|
+
const startMs = Date.now();
|
|
428
|
+
try {
|
|
429
|
+
db = openIndexDatabase(getDbPath());
|
|
430
|
+
const onProgress = (event) => {
|
|
431
|
+
if (!event.currentPath)
|
|
432
|
+
return;
|
|
433
|
+
const file = path.basename(event.currentPath);
|
|
434
|
+
warn(`[graph] extracting ${event.processed}/${event.total} ${file}`);
|
|
435
|
+
};
|
|
436
|
+
const result = await extractionFn({
|
|
437
|
+
config,
|
|
438
|
+
sources,
|
|
439
|
+
signal: undefined,
|
|
440
|
+
db,
|
|
441
|
+
reEnrich: false,
|
|
442
|
+
onProgress,
|
|
443
|
+
options: passOptions,
|
|
444
|
+
});
|
|
445
|
+
const durationMs = Date.now() - startMs;
|
|
409
446
|
return {
|
|
410
447
|
shape: "graph-update",
|
|
411
448
|
ok: true,
|
|
412
|
-
filesExtracted:
|
|
413
|
-
entitiesUpserted:
|
|
414
|
-
relationsUpserted:
|
|
415
|
-
durationMs
|
|
416
|
-
scoped
|
|
449
|
+
filesExtracted: result.quality.extractedFiles,
|
|
450
|
+
entitiesUpserted: result.quality.entityCount,
|
|
451
|
+
relationsUpserted: result.quality.relationCount,
|
|
452
|
+
durationMs,
|
|
453
|
+
scoped,
|
|
417
454
|
};
|
|
418
455
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const startMs = Date.now();
|
|
425
|
-
try {
|
|
426
|
-
db = openDatabase(getDbPath());
|
|
427
|
-
const onProgress = (event) => {
|
|
428
|
-
if (!event.currentPath)
|
|
429
|
-
return;
|
|
430
|
-
const file = path.basename(event.currentPath);
|
|
431
|
-
warn(`[graph] extracting ${event.processed}/${event.total} ${file}`);
|
|
432
|
-
};
|
|
433
|
-
const result = await extractionFn({
|
|
434
|
-
config,
|
|
435
|
-
sources,
|
|
436
|
-
signal: undefined,
|
|
437
|
-
db,
|
|
438
|
-
reEnrich: false,
|
|
439
|
-
onProgress,
|
|
440
|
-
options: passOptions,
|
|
441
|
-
});
|
|
442
|
-
const durationMs = Date.now() - startMs;
|
|
443
|
-
return {
|
|
444
|
-
shape: "graph-update",
|
|
445
|
-
ok: true,
|
|
446
|
-
filesExtracted: result.quality.extractedFiles,
|
|
447
|
-
entitiesUpserted: result.quality.entityCount,
|
|
448
|
-
relationsUpserted: result.quality.relationCount,
|
|
449
|
-
durationMs,
|
|
450
|
-
scoped,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
finally {
|
|
454
|
-
if (db)
|
|
455
|
-
closeDatabase(db);
|
|
456
|
-
}
|
|
456
|
+
finally {
|
|
457
|
+
if (db)
|
|
458
|
+
closeDatabase(db);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
457
461
|
}
|
|
458
462
|
async function resolveGraphTarget(ref, source) {
|
|
459
463
|
const parsedRef = parseAssetRef(ref);
|