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.
Files changed (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. 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, getStateProposalLenient, listStateProposalIdsByPrefix, listStateProposals, upsertProposal, } from "../../storage/repositories/proposals-repository.js";
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
- // Lenient: archiving (accept OR reject) a legacy row with malformed
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
- persistProposalUpdate(db, updated, stashDir);
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
- persistProposalUpdate(db, updated, stashDir);
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
- // Lenient: a reject transaction must run to completion even on a legacy
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 = getProposalLenient(p.stashDir, p.proposalId, ctx);
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
- // Lenient: see requireProposalLenient the reject decision itself must not
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
- persistProposalUpdate(db, proposalToValidate, stashDir);
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 { parseTaskDocument } from "../../../tasks/parser.js";
9
- import { parseWorkflow } from "../../../workflows/parser.js";
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
- parseTaskDocument({
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 changePath = proposal.changes[0]?.path ?? "";
67
- const result = parseWorkflow(content, { path: changePath || proposal.ref });
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 line ${error.line}: ${error.message}`,
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 the relevance signal keyword fallback
310
- // searches exist only to ADD recall when that ranking is thin, never to
311
- // re-rank it. So we PRESERVE base order and APPEND fallback-only hits below
312
- // it. A single-token fallback match on an exact title/path normalizes to a
313
- // high FTS score, but those scores are not comparable to the full-query
314
- // (hybrid) scores; re-sorting the union by raw score (the prior behaviour)
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 must rank BELOW every full-query hit through the rest of
345
- // the pipeline. The downstream selector (`selectCuratedStashHits`) RE-SORTS by
346
- // score and derives its relevance floor from the top score, so preserving
347
- // order here is not enough — a single-token FTS match (normalized ~0.9) would
348
- // otherwise become the leader and evict the contextual full-query memories.
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
- ? sortedExtra.map((hit, i) => ({ ...hit, score: minBaseScore - 1e-6 * (i + 1) }))
358
- : sortedExtra;
359
- const mergedHits = [...baseStash, ...cappedExtra];
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].filter((entry) => {
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(toErrorMessage(result.reason));
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].name ? `"${entries[i].name}"` : entries[i].url;
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(`[akm] Ignoring AKM_REGISTRY_URL entry: empty provider type before "::" in "${trimmed}"`);
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(`[akm] Ignoring AKM_REGISTRY_URL entry: must start with http:// or https://, got "${url}"`);
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((r) => r.enabled !== false);
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 = entry.name ? `${entry.name} (${entry.url})` : entry.url;
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", { ok: true, ...result, similar });
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", { ok: true, ...result });
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 ?? "keyword");
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 source from `config.sources[].name` — filters stash results to
331
- * that single source only. The named-source path is detected and resolved
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
  *