akm-cli 0.9.1 → 0.9.2-alpha.2
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 +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -52,7 +52,7 @@ import { appendEvent } from "../../core/events.js";
|
|
|
52
52
|
import { proposalContent } from "../../core/file-change.js";
|
|
53
53
|
import { _setTxnMutationHookForTests, advanceTxn, beginTxn, canonicalTxnRoot, cleanupTxn, fsyncTxnDir, fsyncTxnFile, listTxnJournals, mintTxnId, registerTxnKind, sweepJournallessTxnDir, txnDirFor, txnMutationHook, txnNamespaceDir, } from "../../core/fs-txn.js";
|
|
54
54
|
import { canonicalBundleIdForTarget, resolveBundleWriteTarget } from "../../core/mutation-target.js";
|
|
55
|
-
import { withImmediateTransaction, withStateDb } from "../../core/state-db.js";
|
|
55
|
+
import { getStateDbPath, withImmediateTransaction, withStateDb } from "../../core/state-db.js";
|
|
56
56
|
import { warn } from "../../core/warn.js";
|
|
57
57
|
import { recordWrittenPath } from "../../core/write-provenance.js";
|
|
58
58
|
import { assertAkmAssetWrite, assertWriteTargetPathsClean, captureGitPublication, captureWriteTargetPathSnapshot, prepareWriteTargetForMutation, publishWriteTargetTransaction, resolveWriteTarget, } from "../../core/write-source.js";
|
|
@@ -61,7 +61,8 @@ import { indexWrittenAssets } from "../../indexer/index-written-assets.js";
|
|
|
61
61
|
import { deriveInstallations } from "../../indexer/installations.js";
|
|
62
62
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
63
63
|
import { insertEventOnce } from "../../storage/repositories/events-repository.js";
|
|
64
|
-
import { getStateProposal,
|
|
64
|
+
import { getStateProposal, listStateProposalIdsByPrefix, listStateProposals, upsertProposal, } from "../../storage/repositories/proposals-repository.js";
|
|
65
|
+
import { openSqliteReadSnapshot } from "../../storage/sqlite-read-snapshot.js";
|
|
65
66
|
import { pkgVersion } from "../../version.js";
|
|
66
67
|
import { runBaseChecks } from "../lint/base-linter.js";
|
|
67
68
|
import { formatNewAssetDiff, formatUnifiedDiff } from "./diff-format.js";
|
|
@@ -153,46 +154,6 @@ function newId(ctx) {
|
|
|
153
154
|
function withProposalsDb(_stashDir, ctx, fn) {
|
|
154
155
|
return withStateDb(fn, { path: ctx?.dbPath });
|
|
155
156
|
}
|
|
156
|
-
function persistProposalUpdate(db, proposal, stashDir) {
|
|
157
|
-
// Only take the full re-serialize (upsertProposal -> proposalToRowValues)
|
|
158
|
-
// path when `changes` is well-formed enough to survive it — a non-empty
|
|
159
|
-
// path on every entry, matching proposalToRowValues' own validation. A
|
|
160
|
-
// legacy row with a present `proposedTarget` but an empty/missing
|
|
161
|
-
// `changes[].path` (e.g. a malformed 0.8-era row) would otherwise die in
|
|
162
|
-
// proposalToRowValues; falling through to the targeted UPDATE below keeps
|
|
163
|
-
// `changes`/`proposedTarget` untouched in metadata_json and only flips the
|
|
164
|
-
// status/review/etc. fields the terminal transition actually needs.
|
|
165
|
-
const changesAreWellFormed = proposal.changes.length > 0 && proposal.changes.every((change) => change.path.length > 0);
|
|
166
|
-
if (changesAreWellFormed && proposal.proposedTarget) {
|
|
167
|
-
upsertProposal(db, proposal, stashDir);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const row = db
|
|
171
|
-
.prepare("SELECT metadata_json FROM proposals WHERE id = ? AND stash_dir = ?")
|
|
172
|
-
.get(proposal.id, stashDir);
|
|
173
|
-
if (!row)
|
|
174
|
-
throw new NotFoundError(`Proposal "${proposal.id}" not found.`, "PROPOSAL_NOT_FOUND");
|
|
175
|
-
const metadata = JSON.parse(row.metadata_json);
|
|
176
|
-
for (const field of [
|
|
177
|
-
"sourceRun",
|
|
178
|
-
"beforeHash",
|
|
179
|
-
"review",
|
|
180
|
-
"confidence",
|
|
181
|
-
"gateDecision",
|
|
182
|
-
"backupContent",
|
|
183
|
-
"acceptedTarget",
|
|
184
|
-
"eligibilitySource",
|
|
185
|
-
]) {
|
|
186
|
-
const value = proposal[field];
|
|
187
|
-
if (value === undefined)
|
|
188
|
-
delete metadata[field];
|
|
189
|
-
else
|
|
190
|
-
metadata[field] = value;
|
|
191
|
-
}
|
|
192
|
-
db.prepare(`UPDATE proposals
|
|
193
|
-
SET ref = ?, status = ?, source = ?, updated_at = ?, content = ?, frontmatter_json = ?, metadata_json = ?
|
|
194
|
-
WHERE id = ? AND stash_dir = ?`).run(proposal.ref, proposal.status, proposal.source, proposal.updatedAt, proposal.payload.content, proposal.payload.frontmatter ? JSON.stringify(proposal.payload.frontmatter) : null, JSON.stringify(metadata), proposal.id, stashDir);
|
|
195
|
-
}
|
|
196
157
|
function proposalRefIdentity(ref) {
|
|
197
158
|
try {
|
|
198
159
|
const parsed = parseBundleRef(ref);
|
|
@@ -565,6 +526,50 @@ export function listProposals(stashDir, options = {}, ctx) {
|
|
|
565
526
|
});
|
|
566
527
|
});
|
|
567
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Read proposal context without creating or migrating state.db.
|
|
531
|
+
*
|
|
532
|
+
* Prompt-building consumers call this before their first model dispatch. A
|
|
533
|
+
* missing proposal store is therefore an empty snapshot, not a reason to
|
|
534
|
+
* create durable state before a required symbolic credential is validated at
|
|
535
|
+
* the dispatch boundary.
|
|
536
|
+
*/
|
|
537
|
+
export function listProposalsReadOnly(stashDir, options = {}, ctx) {
|
|
538
|
+
const dbPath = ctx?.dbPath ?? getStateDbPath();
|
|
539
|
+
if (!fs.existsSync(dbPath))
|
|
540
|
+
return [];
|
|
541
|
+
let db;
|
|
542
|
+
try {
|
|
543
|
+
db = openSqliteReadSnapshot(dbPath);
|
|
544
|
+
if (!db)
|
|
545
|
+
return [];
|
|
546
|
+
if (!options.includeArchive && options.status !== undefined && options.status !== "pending")
|
|
547
|
+
return [];
|
|
548
|
+
const status = options.includeArchive ? options.status : "pending";
|
|
549
|
+
const wantRef = options.ref !== undefined ? filterRefIdentity(options.ref) : undefined;
|
|
550
|
+
return listStateProposals(db, {
|
|
551
|
+
stashDir,
|
|
552
|
+
...(status !== undefined ? { status } : {}),
|
|
553
|
+
}).filter((proposal) => {
|
|
554
|
+
if (wantRef !== undefined && !proposalMatchesRef(proposal.ref, wantRef))
|
|
555
|
+
return false;
|
|
556
|
+
if (!options.type)
|
|
557
|
+
return true;
|
|
558
|
+
try {
|
|
559
|
+
return parseRefInput(proposal.ref).type === options.type;
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
finally {
|
|
570
|
+
db?.close();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
568
573
|
/**
|
|
569
574
|
* Look up a proposal by id (live or archived).
|
|
570
575
|
* Throws `NotFoundError` when no match exists in this stash.
|
|
@@ -579,25 +584,6 @@ function requireProposal(db, stashDir, id) {
|
|
|
579
584
|
}
|
|
580
585
|
return proposal;
|
|
581
586
|
}
|
|
582
|
-
/**
|
|
583
|
-
* Lenient counterpart to {@link requireProposal} / {@link getProposal}, used
|
|
584
|
-
* ONLY on the terminal-status (reject/archive) read paths — `archiveProposal`
|
|
585
|
-
* and the reject-transaction machinery (`rejectProposalDurably`,
|
|
586
|
-
* `finalizeRejectTransaction`). Those paths must succeed even when the row's
|
|
587
|
-
* metadata fails strict decoding (see {@link proposalRowToProposalLenient});
|
|
588
|
-
* every other reader (accept, revert, show, list's pending branch) keeps
|
|
589
|
-
* strict decoding via `getProposal`/`requireProposal` unchanged.
|
|
590
|
-
*/
|
|
591
|
-
function requireProposalLenient(db, stashDir, id) {
|
|
592
|
-
const proposal = getStateProposalLenient(db, id, stashDir);
|
|
593
|
-
if (!proposal) {
|
|
594
|
-
throw new NotFoundError(`Proposal "${id}" not found.`, "PROPOSAL_NOT_FOUND");
|
|
595
|
-
}
|
|
596
|
-
return proposal;
|
|
597
|
-
}
|
|
598
|
-
function getProposalLenient(stashDir, id, ctx) {
|
|
599
|
-
return withProposalsDb(stashDir, ctx, (db) => requireProposalLenient(db, stashDir, id));
|
|
600
|
-
}
|
|
601
587
|
/**
|
|
602
588
|
* Resolve a proposal by full UUID, UUID prefix, or asset ref.
|
|
603
589
|
*
|
|
@@ -638,22 +624,6 @@ export function resolveProposalId(stashDir, idOrRef, ctx) {
|
|
|
638
624
|
throw new NotFoundError(`Proposal "${idOrRef}" not found.`, "PROPOSAL_NOT_FOUND");
|
|
639
625
|
});
|
|
640
626
|
}
|
|
641
|
-
/**
|
|
642
|
-
* Resolve a reject target's id, tolerating a legacy row whose metadata fails
|
|
643
|
-
* strict decoding. `akm proposal reject` only needs the id — but
|
|
644
|
-
* {@link resolveProposalId}'s exact-UUID fast path decodes the FULL proposal
|
|
645
|
-
* just to hand it back, which throws on those rows before the
|
|
646
|
-
* terminal-status machinery (which reads leniently — see
|
|
647
|
-
* `requireProposalLenient`) ever runs. Falls back to `resolveProposalId`
|
|
648
|
-
* unchanged for ref / uuid-prefix inputs, where existing resolution
|
|
649
|
-
* (including its error semantics) is preserved.
|
|
650
|
-
*/
|
|
651
|
-
export function resolveProposalIdForReject(stashDir, idOrRef, ctx) {
|
|
652
|
-
const exact = withProposalsDb(stashDir, ctx, (db) => getStateProposalLenient(db, idOrRef, stashDir));
|
|
653
|
-
if (exact)
|
|
654
|
-
return exact.id;
|
|
655
|
-
return resolveProposalId(stashDir, idOrRef, ctx).id;
|
|
656
|
-
}
|
|
657
627
|
/**
|
|
658
628
|
* Archive a proposal: flip its status to `accepted` / `rejected`, bump
|
|
659
629
|
* `updatedAt`, and record the review block. Used by both accept and reject
|
|
@@ -662,10 +632,7 @@ export function resolveProposalIdForReject(stashDir, idOrRef, ctx) {
|
|
|
662
632
|
export function archiveProposal(stashDir, id, status, reason, ctx, gateDecision) {
|
|
663
633
|
return withProposalsDb(stashDir, ctx, (db) => {
|
|
664
634
|
return withImmediateTransaction(db, () => {
|
|
665
|
-
|
|
666
|
-
// changes/proposedTarget metadata must still be able to flip status —
|
|
667
|
-
// see requireProposalLenient.
|
|
668
|
-
const existing = requireProposalLenient(db, stashDir, id);
|
|
635
|
+
const existing = requireProposal(db, stashDir, id);
|
|
669
636
|
if (existing.status !== "pending") {
|
|
670
637
|
throw new UsageError(`Proposal ${id} is not pending (current status: ${existing.status}). Only pending proposals can be ${status}.`, "INVALID_FLAG_VALUE");
|
|
671
638
|
}
|
|
@@ -681,7 +648,7 @@ export function archiveProposal(stashDir, id, status, reason, ctx, gateDecision)
|
|
|
681
648
|
},
|
|
682
649
|
...(gateDecision ? { gateDecision: { ...gateDecision, decidedAt: gateDecision.decidedAt ?? decidedAt } } : {}),
|
|
683
650
|
};
|
|
684
|
-
|
|
651
|
+
upsertProposal(db, updated, stashDir);
|
|
685
652
|
return updated;
|
|
686
653
|
});
|
|
687
654
|
});
|
|
@@ -711,7 +678,7 @@ export function recordGateDecision(stashDir, id, decision, ctx) {
|
|
|
711
678
|
...existing,
|
|
712
679
|
gateDecision: { ...decision, decidedAt: decision.decidedAt ?? nowIso(ctx) },
|
|
713
680
|
};
|
|
714
|
-
|
|
681
|
+
upsertProposal(db, updated, stashDir);
|
|
715
682
|
return updated;
|
|
716
683
|
});
|
|
717
684
|
});
|
|
@@ -1182,9 +1149,7 @@ const REJECT_TXN_PHASES = ["prepared", "state-persisted", "event-finalized", "co
|
|
|
1182
1149
|
function finalizeRejectTransaction(txn, ctx) {
|
|
1183
1150
|
const p = txn.journal.payload;
|
|
1184
1151
|
const decidedAt = txn.journal.decidedAt;
|
|
1185
|
-
|
|
1186
|
-
// row whose metadata fails strict decoding — see requireProposalLenient.
|
|
1187
|
-
let proposal = getProposalLenient(p.stashDir, p.proposalId, ctx);
|
|
1152
|
+
let proposal = getProposal(p.stashDir, p.proposalId, ctx);
|
|
1188
1153
|
if (txn.journal.phase === "prepared") {
|
|
1189
1154
|
if (proposal.status === "pending") {
|
|
1190
1155
|
proposal = archiveProposal(p.stashDir, p.proposalId, "rejected", p.reason, { ...ctx, now: () => Date.parse(decidedAt) }, p.gateDecision);
|
|
@@ -1196,7 +1161,7 @@ function finalizeRejectTransaction(txn, ctx) {
|
|
|
1196
1161
|
txnMutationHook("reject-state-persisted");
|
|
1197
1162
|
}
|
|
1198
1163
|
if (txn.journal.phase === "state-persisted") {
|
|
1199
|
-
proposal =
|
|
1164
|
+
proposal = getProposal(p.stashDir, p.proposalId, ctx);
|
|
1200
1165
|
const eventRef = proposal.ref;
|
|
1201
1166
|
const eventMeta = {
|
|
1202
1167
|
proposalId: proposal.id,
|
|
@@ -1250,9 +1215,7 @@ export function rejectProposalDurably(stashDir, proposalId, reason, ctx, gateDec
|
|
|
1250
1215
|
const recovered = recoverRejectTransaction(stashDir, proposalId, ctx);
|
|
1251
1216
|
if (recovered)
|
|
1252
1217
|
return recovered;
|
|
1253
|
-
|
|
1254
|
-
// be blocked by a legacy row's malformed changes/proposedTarget metadata.
|
|
1255
|
-
const proposal = getProposalLenient(stashDir, proposalId, ctx);
|
|
1218
|
+
const proposal = getProposal(stashDir, proposalId, ctx);
|
|
1256
1219
|
if (proposal.status !== "pending") {
|
|
1257
1220
|
throw new UsageError(`Proposal ${proposalId} is not pending (current status: ${proposal.status}). Only pending proposals can be rejected.`, "INVALID_FLAG_VALUE");
|
|
1258
1221
|
}
|
|
@@ -1700,7 +1663,7 @@ async function promoteProposalWithLease(stashDir, config, id, options, ctx) {
|
|
|
1700
1663
|
// final promoted payload (not the defective original).
|
|
1701
1664
|
if (repairedContent !== proposalContent(proposal)) {
|
|
1702
1665
|
withProposalsDb(stashDir, ctx, (db) => {
|
|
1703
|
-
|
|
1666
|
+
upsertProposal(db, proposalToValidate, stashDir);
|
|
1704
1667
|
});
|
|
1705
1668
|
}
|
|
1706
1669
|
await recoverProposalTransactionsForStash(stashDir, config, ctx, id);
|
|
@@ -5,8 +5,8 @@ import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
|
5
5
|
import { parseRefInput } from "../../../core/asset/resolve-ref.js";
|
|
6
6
|
import { proposalContent } from "../../../core/file-change.js";
|
|
7
7
|
import { lintLessonContent } from "../../../core/lesson-lint.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { parseTaskV3Yaml } from "../../../tasks/source-v3.js";
|
|
9
|
+
import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
|
|
10
10
|
import { defaultProposalQualityValidators } from "./proposal-quality-validators.js";
|
|
11
11
|
const genericProposalValidator = {
|
|
12
12
|
name: "generic-proposal-validator",
|
|
@@ -52,10 +52,9 @@ const canonicalProposalValidators = {
|
|
|
52
52
|
const name = ctx.parsedRef?.name;
|
|
53
53
|
if (!name)
|
|
54
54
|
return [];
|
|
55
|
-
|
|
55
|
+
parseTaskV3Yaml({
|
|
56
56
|
yaml: proposalContent(proposal),
|
|
57
57
|
filePath: proposal.changes[0]?.path || proposal.ref,
|
|
58
|
-
id: name,
|
|
59
58
|
});
|
|
60
59
|
return [];
|
|
61
60
|
},
|
|
@@ -63,13 +62,16 @@ const canonicalProposalValidators = {
|
|
|
63
62
|
const content = proposalContent(proposal);
|
|
64
63
|
if (!content.trim())
|
|
65
64
|
return [];
|
|
66
|
-
const
|
|
67
|
-
const result =
|
|
65
|
+
const sourcePath = proposal.changes[0]?.path || proposal.ref;
|
|
66
|
+
const result = compileWorkflowSource(content, {
|
|
67
|
+
path: sourcePath,
|
|
68
|
+
workspaceRoot: proposal.proposedTarget.root,
|
|
69
|
+
});
|
|
68
70
|
if (result.ok)
|
|
69
71
|
return [];
|
|
70
72
|
return result.errors.map((error) => ({
|
|
71
73
|
kind: "invalid-workflow-structure",
|
|
72
|
-
message: `Workflow proposal ${proposal.id} (${proposal.ref}) is invalid at
|
|
74
|
+
message: `Workflow proposal ${proposal.id} (${proposal.ref}) is invalid [${error.code}] at ${error.path}:${error.line}: ${error.message}`,
|
|
73
75
|
}));
|
|
74
76
|
},
|
|
75
77
|
};
|
|
@@ -159,6 +159,7 @@ export async function curateSearchResults(query, result, limit, selectedType, ev
|
|
|
159
159
|
summary: buildCurateSummary(query, items),
|
|
160
160
|
items,
|
|
161
161
|
...(result.warnings?.length ? { warnings: result.warnings } : {}),
|
|
162
|
+
...(result.searchMode ? { searchMode: result.searchMode } : {}),
|
|
162
163
|
...(result.tip ? { tip: result.tip } : {}),
|
|
163
164
|
};
|
|
164
165
|
}
|
|
@@ -306,16 +307,12 @@ export function deriveCurateFallbackQueries(query) {
|
|
|
306
307
|
return tokens;
|
|
307
308
|
}
|
|
308
309
|
export function mergeCurateSearchResponses(base, extras) {
|
|
309
|
-
// The base (full-query) ranking is
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// let that keyword junk leapfrog the contextually-relevant full-query hits.
|
|
316
|
-
// Dup refs (present in both base and a fallback) keep their base POSITION but
|
|
317
|
-
// take the MAX score, since matching both the full query and a key term is a
|
|
318
|
-
// stronger relevance signal for the downstream score floor.
|
|
310
|
+
// The base (full-query) ranking is authoritative by default; keyword
|
|
311
|
+
// fallbacks add recall without comparing their raw scores to the full-query
|
|
312
|
+
// score scale. Base order is preserved and fallback-only hits append below,
|
|
313
|
+
// except for the narrow weak-provenance promotion documented below. Duplicate
|
|
314
|
+
// refs keep their base position but take the maximum score for the downstream
|
|
315
|
+
// relevance floor.
|
|
319
316
|
const bestExtraStashScore = new Map();
|
|
320
317
|
for (const result of extras) {
|
|
321
318
|
for (const hit of result.hits.filter((entry) => entry.type !== "registry")) {
|
|
@@ -341,22 +338,39 @@ export function mergeCurateSearchResponses(base, extras) {
|
|
|
341
338
|
extraOnly.set(hit.ref, hit);
|
|
342
339
|
}
|
|
343
340
|
}
|
|
344
|
-
// Fallback-only hits
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
// We therefore restamp fallback-only scores into a band strictly below the
|
|
350
|
-
// minimum base score (keeping their own relative order). When there are no
|
|
351
|
-
// base hits, fallback IS the result, so scores are kept as-is.
|
|
341
|
+
// Fallback-only hits ordinarily stay below every full-query hit through the
|
|
342
|
+
// downstream selector, which re-sorts by score and derives its relevance
|
|
343
|
+
// floor from the leader. Restamping them below the minimum base score keeps a
|
|
344
|
+
// single-token score from leapfrogging contextual results. The one exception
|
|
345
|
+
// is a strong name match repairing a lexically weak base, selected below.
|
|
352
346
|
const sortedExtra = [...extraOnly.values()].sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
347
|
+
const lexicalAttribution = (hit) => getSearchHitAttribution(hit)?.lexical;
|
|
348
|
+
// The full-query base remains authoritative unless its lexical provenance is
|
|
349
|
+
// itself weak: relaxed recovery, or no hit whose name covers the query. In
|
|
350
|
+
// that case one fallback discovery with strong name coverage may lead. This
|
|
351
|
+
// preserves contextual base ranking while allowing measured recall to repair
|
|
352
|
+
// a thin match without comparing unrelated raw FTS score scales.
|
|
353
|
+
const weakLexicalBase = baseStash.length > 0 &&
|
|
354
|
+
baseStash.every((hit) => {
|
|
355
|
+
const lexical = lexicalAttribution(hit);
|
|
356
|
+
return lexical !== undefined && (lexical.execution === "relaxed" || lexical.nameMatchTier < 2);
|
|
357
|
+
});
|
|
358
|
+
const promotedExtraIndex = weakLexicalBase
|
|
359
|
+
? sortedExtra.findIndex((hit) => (lexicalAttribution(hit)?.nameMatchTier ?? 0) >= 2)
|
|
360
|
+
: -1;
|
|
361
|
+
const promotedExtra = promotedExtraIndex >= 0 ? sortedExtra[promotedExtraIndex] : undefined;
|
|
362
|
+
const remainingExtra = promotedExtraIndex >= 0 ? sortedExtra.filter((_, index) => index !== promotedExtraIndex) : sortedExtra;
|
|
353
363
|
const minBaseScore = baseStash.length
|
|
354
364
|
? Math.min(...baseStash.map((hit) => hit.score ?? 0))
|
|
355
365
|
: Number.POSITIVE_INFINITY;
|
|
356
366
|
const cappedExtra = baseStash.length
|
|
357
|
-
?
|
|
358
|
-
:
|
|
359
|
-
const mergedHits = [
|
|
367
|
+
? remainingExtra.map((hit, i) => ({ ...hit, score: minBaseScore - 1e-6 * (i + 1) }))
|
|
368
|
+
: remainingExtra;
|
|
369
|
+
const mergedHits = [
|
|
370
|
+
...(promotedExtra ? [{ ...promotedExtra, score: minBaseScore }] : []),
|
|
371
|
+
...baseStash,
|
|
372
|
+
...cappedExtra,
|
|
373
|
+
];
|
|
360
374
|
// Registry hits are supplemental fill — same rule: base first (max score on
|
|
361
375
|
// dups), then fallback-only registry hits appended by score.
|
|
362
376
|
const bestExtraRegScore = new Map();
|
|
@@ -389,11 +403,20 @@ export function mergeCurateSearchResponses(base, extras) {
|
|
|
389
403
|
...baseReg,
|
|
390
404
|
...[...extraRegOnly.values()].sort((a, b) => (b.score ?? 0) - (a.score ?? 0)),
|
|
391
405
|
];
|
|
406
|
+
const modes = [base.searchMode, ...extras.map((result) => result.searchMode)].filter((mode) => mode !== undefined);
|
|
407
|
+
const searchMode = modes.includes("fts-fallback")
|
|
408
|
+
? "fts-fallback"
|
|
409
|
+
: modes.includes("semantic")
|
|
410
|
+
? "semantic"
|
|
411
|
+
: modes.includes("keyword")
|
|
412
|
+
? "keyword"
|
|
413
|
+
: undefined;
|
|
392
414
|
return {
|
|
393
415
|
...base,
|
|
394
416
|
hits: mergedHits,
|
|
395
417
|
...(mergedRegistryHits.length > 0 ? { registryHits: mergedRegistryHits } : {}),
|
|
396
418
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
419
|
+
...(searchMode ? { searchMode } : {}),
|
|
397
420
|
...(mergedHits.length > 0 || mergedRegistryHits.length > 0 ? { tip: undefined } : {}),
|
|
398
421
|
};
|
|
399
422
|
}
|
|
@@ -575,8 +598,16 @@ function collapseCurateFamilies(query, hits) {
|
|
|
575
598
|
if (!representative)
|
|
576
599
|
continue;
|
|
577
600
|
collapsedFamilies.push(representative);
|
|
578
|
-
const supportCandidates = [group.root, ...group.references]
|
|
601
|
+
const supportCandidates = [group.root, ...group.references]
|
|
602
|
+
.filter((entry) => {
|
|
579
603
|
return entry !== undefined && entry.hit.ref !== representative.hit.ref;
|
|
604
|
+
})
|
|
605
|
+
.sort((a, b) => {
|
|
606
|
+
if (a === group.root)
|
|
607
|
+
return -1;
|
|
608
|
+
if (b === group.root)
|
|
609
|
+
return 1;
|
|
610
|
+
return a.hit.ref.localeCompare(b.hit.ref);
|
|
580
611
|
});
|
|
581
612
|
for (const support of supportCandidates) {
|
|
582
613
|
appendCurateSupportRef(supportRefsByRef, representative.hit.ref, {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import { toErrorMessage } from "../../core/common.js";
|
|
5
4
|
import { DEFAULT_CONFIG } from "../../core/config/config.js";
|
|
5
|
+
import { formatRegistryCredentialWarning, formatRegistryError, formatRegistryLabel, hasRegistryUrlCredentials, redactCredentialBearingUrls, } from "../../core/registry-url.js";
|
|
6
6
|
import { warn } from "../../core/warn.js";
|
|
7
7
|
import { resolveRegistryProviderFactory } from "../../registry/factory.js";
|
|
8
8
|
// ── Eagerly import providers to trigger self-registration ───────────────────
|
|
@@ -16,8 +16,13 @@ export async function searchRegistry(query, options) {
|
|
|
16
16
|
const limit = clampLimit(options?.limit);
|
|
17
17
|
// resolveRegistries() already filters by enabled; explicit registries are filtered here
|
|
18
18
|
const raw = options?.registries ?? resolveRegistries();
|
|
19
|
-
const entries = options?.registries ? raw.filter((r) => r.enabled !== false) : raw;
|
|
20
19
|
const warnings = [];
|
|
20
|
+
const entries = (options?.registries ? raw.filter((r) => r.enabled !== false) : raw).filter((entry) => {
|
|
21
|
+
if (!hasRegistryUrlCredentials(entry.url))
|
|
22
|
+
return true;
|
|
23
|
+
warnings.push(formatRegistryCredentialWarning(entry));
|
|
24
|
+
return false;
|
|
25
|
+
});
|
|
21
26
|
// Resolve and search all providers concurrently
|
|
22
27
|
const results = await Promise.allSettled(entries.map((entry) => {
|
|
23
28
|
const provider = createProvider(entry, warnings);
|
|
@@ -34,13 +39,13 @@ export async function searchRegistry(query, options) {
|
|
|
34
39
|
for (let i = 0; i < results.length; i++) {
|
|
35
40
|
const result = results[i];
|
|
36
41
|
if (result.status === "rejected") {
|
|
37
|
-
warnings.push(
|
|
42
|
+
warnings.push(`Registry ${formatRegistryLabel(entries[i])}: ${formatRegistryError(result.reason)}`);
|
|
38
43
|
continue;
|
|
39
44
|
}
|
|
40
45
|
const value = result.value;
|
|
41
46
|
if (!value)
|
|
42
47
|
continue;
|
|
43
|
-
const registryLabel = entries[i]
|
|
48
|
+
const registryLabel = formatRegistryLabel(entries[i]);
|
|
44
49
|
let dropped = 0;
|
|
45
50
|
const validHits = [];
|
|
46
51
|
for (const hit of value.hits) {
|
|
@@ -75,7 +80,7 @@ export async function searchRegistry(query, options) {
|
|
|
75
80
|
warnings.push(`Registry ${registryLabel} returned ${dropped} incomplete hit(s); dropped from response.`);
|
|
76
81
|
}
|
|
77
82
|
if (value.warnings)
|
|
78
|
-
warnings.push(...value.warnings);
|
|
83
|
+
warnings.push(...value.warnings.map(redactCredentialBearingUrls));
|
|
79
84
|
}
|
|
80
85
|
// Sort merged hits by normalized score descending, apply limit
|
|
81
86
|
allHits.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
@@ -121,7 +126,7 @@ export function resolveRegistries(configRegistries) {
|
|
|
121
126
|
provider = trimmed.slice(0, colonColonIdx).trim();
|
|
122
127
|
url = trimmed.slice(colonColonIdx + 2).trim();
|
|
123
128
|
if (!provider) {
|
|
124
|
-
warn(
|
|
129
|
+
warn('[akm] Ignoring AKM_REGISTRY_URL entry: empty provider type before "::".');
|
|
125
130
|
continue;
|
|
126
131
|
}
|
|
127
132
|
}
|
|
@@ -129,7 +134,11 @@ export function resolveRegistries(configRegistries) {
|
|
|
129
134
|
url = trimmed;
|
|
130
135
|
}
|
|
131
136
|
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
132
|
-
warn(
|
|
137
|
+
warn("[akm] Ignoring AKM_REGISTRY_URL entry: URL must start with http:// or https://.");
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (hasRegistryUrlCredentials(url)) {
|
|
141
|
+
warn(`[akm] ${formatRegistryCredentialWarning({ url })}`);
|
|
133
142
|
continue;
|
|
134
143
|
}
|
|
135
144
|
entries.push(provider ? { url, provider } : { url });
|
|
@@ -137,14 +146,21 @@ export function resolveRegistries(configRegistries) {
|
|
|
137
146
|
return entries;
|
|
138
147
|
}
|
|
139
148
|
const registries = configRegistries ?? DEFAULT_CONFIG.registries ?? [];
|
|
140
|
-
return registries.filter((
|
|
149
|
+
return registries.filter((entry) => {
|
|
150
|
+
if (entry.enabled === false)
|
|
151
|
+
return false;
|
|
152
|
+
if (!hasRegistryUrlCredentials(entry.url))
|
|
153
|
+
return true;
|
|
154
|
+
warn(`[akm] ${formatRegistryCredentialWarning(entry)}`);
|
|
155
|
+
return false;
|
|
156
|
+
});
|
|
141
157
|
}
|
|
142
158
|
// ── Provider resolution ─────────────────────────────────────────────────────
|
|
143
159
|
function createProvider(entry, warnings) {
|
|
144
160
|
const providerType = entry.provider ?? "static-index";
|
|
145
161
|
const factory = resolveRegistryProviderFactory(providerType);
|
|
146
162
|
if (!factory) {
|
|
147
|
-
const label =
|
|
163
|
+
const label = formatRegistryLabel(entry);
|
|
148
164
|
warnings.push(`Registry ${label}: unknown provider type "${providerType}"`);
|
|
149
165
|
return null;
|
|
150
166
|
}
|
|
@@ -230,6 +230,7 @@ export const rememberCommand = defineJsonCommand({
|
|
|
230
230
|
let observed_at;
|
|
231
231
|
let expires;
|
|
232
232
|
let subjective;
|
|
233
|
+
let executionNotices;
|
|
233
234
|
// Resolve --expires to an ISO date string
|
|
234
235
|
if (args.expires) {
|
|
235
236
|
const durationMs = parseDuration(args.expires);
|
|
@@ -261,6 +262,7 @@ export const rememberCommand = defineJsonCommand({
|
|
|
261
262
|
description = enriched.description;
|
|
262
263
|
if (!observed_at && enriched.observed_at)
|
|
263
264
|
observed_at = enriched.observed_at;
|
|
265
|
+
executionNotices = enriched.notices;
|
|
264
266
|
}
|
|
265
267
|
// ── Required-field check (before any write) ───────────────────────────
|
|
266
268
|
// Tags remain required when the user explicitly asked for tag-bearing
|
|
@@ -318,15 +320,25 @@ export const rememberCommand = defineJsonCommand({
|
|
|
318
320
|
tagCount: tags.length,
|
|
319
321
|
enriched: args.enrich === true,
|
|
320
322
|
auto: args.auto === true,
|
|
323
|
+
...(executionNotices && executionNotices.length > 0 ? { notices: executionNotices } : {}),
|
|
321
324
|
...(hasScope ? { scope: scopeFields } : {}),
|
|
322
325
|
},
|
|
323
326
|
});
|
|
324
327
|
if (args.showSimilar) {
|
|
325
328
|
const similar = await fetchSimilarMemories((body ?? args.content ?? "").slice(0, 500), result.ref, eventSource);
|
|
326
|
-
output("remember", {
|
|
329
|
+
output("remember", {
|
|
330
|
+
ok: true,
|
|
331
|
+
...result,
|
|
332
|
+
similar,
|
|
333
|
+
...(executionNotices && executionNotices.length > 0 ? { notices: executionNotices } : {}),
|
|
334
|
+
});
|
|
327
335
|
}
|
|
328
336
|
else {
|
|
329
|
-
output("remember", {
|
|
337
|
+
output("remember", {
|
|
338
|
+
ok: true,
|
|
339
|
+
...result,
|
|
340
|
+
...(executionNotices && executionNotices.length > 0 ? { notices: executionNotices } : {}),
|
|
341
|
+
});
|
|
330
342
|
}
|
|
331
343
|
},
|
|
332
344
|
});
|
|
@@ -127,9 +127,10 @@ export async function akmSearch(input) {
|
|
|
127
127
|
hits: localHits,
|
|
128
128
|
tip: hasResults ? undefined : localResult?.tip,
|
|
129
129
|
warnings: localResult?.warnings?.length ? localResult.warnings : undefined,
|
|
130
|
+
searchMode: localResult?.mode ?? "keyword",
|
|
130
131
|
timing: { totalMs: Date.now() - t0, rankMs: localResult?.rankMs, embedMs: localResult?.embedMs },
|
|
131
132
|
};
|
|
132
|
-
maybeLogSearchEvent(input, query, response, localResult?.mode
|
|
133
|
+
maybeLogSearchEvent(input, query, response, usageSearchMode(localResult?.mode));
|
|
133
134
|
return response;
|
|
134
135
|
}
|
|
135
136
|
const registryHits = (registryResult?.hits ?? []).map((hit) => {
|
|
@@ -174,11 +175,16 @@ export async function akmSearch(input) {
|
|
|
174
175
|
registryHits,
|
|
175
176
|
tip: hasResults ? undefined : "No matching stash assets or registry entries were found.",
|
|
176
177
|
warnings: warnings.length ? warnings : undefined,
|
|
178
|
+
searchMode: localResult?.mode ?? "keyword",
|
|
177
179
|
timing: { totalMs: Date.now() - t0 },
|
|
178
180
|
};
|
|
179
|
-
maybeLogSearchEvent(input, query, response);
|
|
181
|
+
maybeLogSearchEvent(input, query, response, usageSearchMode(localResult?.mode));
|
|
180
182
|
return response;
|
|
181
183
|
}
|
|
184
|
+
/** Usage telemetry retains its historical semantic|keyword vocabulary. */
|
|
185
|
+
function usageSearchMode(mode) {
|
|
186
|
+
return mode === "semantic" ? "semantic" : "keyword";
|
|
187
|
+
}
|
|
182
188
|
function maybeLogSearchEvent(input, query, response, mode) {
|
|
183
189
|
if (input.skipLogging)
|
|
184
190
|
return;
|
|
@@ -327,8 +333,8 @@ function normalizeLimit(limit) {
|
|
|
327
333
|
* - `local` (default) — search the local stash index only
|
|
328
334
|
* - `registry` — search remote registries only
|
|
329
335
|
* - `all` — search local and registries
|
|
330
|
-
* - Any named
|
|
331
|
-
*
|
|
336
|
+
* - Any named configured bundle — filters local results to that bundle only.
|
|
337
|
+
* The named-source path is detected and resolved
|
|
332
338
|
* inside `akmSearch`; this function returns the raw name so the caller can
|
|
333
339
|
* pass it through to `akmSearch` which accepts `SearchSource | string`.
|
|
334
340
|
*
|