akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -18,11 +18,20 @@ import { getStateDbPath, withStateDb } from "../../core/state-db.js";
18
18
  import { warn } from "../../core/warn.js";
19
19
  import { getMarkdownFragmentContent, hasMarkdownFragmentContent, } from "../../indexer/passes/metadata.js";
20
20
  import { buildSearchText } from "../../indexer/search/search-fields.js";
21
+ import { sha256Hex } from "../../runtime.js";
21
22
  import { ENTRY_COLUMNS, rowToIndexedEntry } from "./index-entry-mapper.js";
22
23
  import { deleteFtsEntries, replaceFtsEntry } from "./index-fts-repository.js";
23
24
  import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
24
- import { deleteEntryVectors, isVecAvailable } from "./index-vec-repository.js";
25
+ import { deleteEntryVectors } from "./index-vec-repository.js";
25
26
  // ── Entry operations ────────────────────────────────────────────────────────
27
+ /**
28
+ * Hash of the text an entry's vector is embedded from (`buildSearchText`),
29
+ * stored as `entries.embed_hash`: a change deletes the entry's vector, and the
30
+ * next embedding pass re-embeds it.
31
+ */
32
+ function embedHash(entry) {
33
+ return sha256Hex(buildSearchText(entry));
34
+ }
26
35
  /**
27
36
  * Insert or update one canonical entry and all synchronously derived search
28
37
  * state. Returns the stable row id.
@@ -31,7 +40,7 @@ import { deleteEntryVectors, isVecAvailable } from "./index-vec-repository.js";
31
40
  * SQLite transaction. Callers therefore cannot publish an entry and forget a
32
41
  * second FTS maintenance step.
33
42
  */
34
- export function upsertEntry(db, filePath, entry, searchText, provenance, contentHash) {
43
+ export function upsertEntry(db, filePath, entry, provenance, contentHash) {
35
44
  // Hot path during indexing — cache prepared statements per database
36
45
  // connection so we don't pay the SQL parse/compile cost on every call.
37
46
  const stmts = getUpsertStmts(db);
@@ -39,14 +48,15 @@ export function upsertEntry(db, filePath, entry, searchText, provenance, content
39
48
  // dedicated `derived_from` column so retrieval-time lookup (parent→child)
40
49
  // does not have to scan + JSON-decode every memory row.
41
50
  const derivedFrom = typeof entry.derivedFrom === "string" && entry.derivedFrom.trim() ? entry.derivedFrom.trim() : null;
51
+ const hash = embedHash(entry);
42
52
  // `content_hash` is optional on the LLM-enrichment re-upsert; a missing hash
43
53
  // preserves the scan writer's current value.
44
54
  const apply = () => {
45
55
  const previous = stmts.findByItemRef.get(provenance.itemRef);
46
- const result = stmts.upsert.get(provenance.itemRef, provenance.bundleId, provenance.componentId, provenance.conceptId, provenance.adapterId, entry.type, filePath, contentHash ?? null, JSON.stringify(entry), searchText, derivedFrom);
56
+ const result = stmts.upsert.get(provenance.itemRef, provenance.bundleId, provenance.componentId, provenance.conceptId, provenance.adapterId, entry.type, filePath, contentHash ?? null, JSON.stringify(entry), derivedFrom, hash);
47
57
  if (!result)
48
58
  throw new Error("upsertEntry: item_ref not found after upsert");
49
- if (previous?.id === result.id && previous.search_text !== searchText)
59
+ if (previous?.id === result.id && previous.embed_hash !== hash)
50
60
  deleteEntryVectors(db, result.id);
51
61
  replaceFtsEntry(db, result.id, entry, hasMarkdownFragmentContent(entry) ? (getMarkdownFragmentContent(entry) ?? null) : undefined);
52
62
  return result.id;
@@ -68,8 +78,8 @@ const UPSERT_SET_CLAUSE = `SET
68
78
  type = excluded.type,
69
79
  file_path = excluded.file_path,
70
80
  document_json = excluded.document_json,
71
- search_text = excluded.search_text,
72
81
  derived_from = excluded.derived_from,
82
+ embed_hash = excluded.embed_hash,
73
83
  content_hash = COALESCE(excluded.content_hash, content_hash)`;
74
84
  function getUpsertStmts(db) {
75
85
  const existing = upsertStmtsByDb.get(db);
@@ -82,13 +92,13 @@ function getUpsertStmts(db) {
82
92
  upsert: db.prepare(`
83
93
  INSERT INTO entries (
84
94
  item_ref, bundle_id, component_id, concept_id, adapter_id, type,
85
- file_path, content_hash, document_json, search_text, derived_from
95
+ file_path, content_hash, document_json, derived_from, embed_hash
86
96
  )
87
97
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
88
98
  ON CONFLICT(item_ref) DO UPDATE ${UPSERT_SET_CLAUSE}
89
99
  RETURNING id
90
100
  `),
91
- findByItemRef: db.prepare("SELECT id, search_text FROM entries WHERE item_ref = ?"),
101
+ findByItemRef: db.prepare("SELECT id, embed_hash FROM entries WHERE item_ref = ?"),
92
102
  };
93
103
  upsertStmtsByDb.set(db, stmts);
94
104
  return stmts;
@@ -171,7 +181,7 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
171
181
  * rename. (`asset_salience` / `asset_outcome` live in state.db keyed by
172
182
  * `asset_ref` TEXT and are re-keyed separately by `akm mv` — see
173
183
  * the state rekey helper.) `document_json.name` (and `filename`, when
174
- * present) is patched and `search_text` rebuilt so search reflects the new
184
+ * present) is patched and `embed_hash` recomputed so search reflects the new
175
185
  * name. Its FTS projection and stale vector are updated in the same
176
186
  * transaction as the canonical identity.
177
187
  *
@@ -187,7 +197,7 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
187
197
  * A stale row already occupying the new item ref (the caller has verified no
188
198
  * FILE exists at the target, so such a row can only be a leftover for a
189
199
  * deleted file) is evicted first — through {@link deleteRelatedRows}, so its
190
- * child rows (embeddings, entries_vec, utility scores, usage events) go with
200
+ * child rows (embeddings, utility scores, usage events) go with
191
201
  * it. A bare `DELETE FROM entries` would trip the non-CASCADE `embeddings`
192
202
  * FK under `PRAGMA foreign_keys = ON` and roll back the whole re-key.
193
203
  * The moved row keeps its id.
@@ -199,7 +209,7 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
199
209
  export function rekeyEntryInPlace(db, opts) {
200
210
  const oldItemRef = `${opts.sourceName}//${opts.oldRef}`;
201
211
  const row = db
202
- .prepare("SELECT id, file_path, document_json, search_text, type FROM entries WHERE item_ref = ?")
212
+ .prepare("SELECT id, file_path, document_json, embed_hash, type FROM entries WHERE item_ref = ?")
203
213
  .get(oldItemRef);
204
214
  if (!row)
205
215
  return null;
@@ -211,7 +221,7 @@ export function rekeyEntryInPlace(db, opts) {
211
221
  // Patch the JSON payload. On corrupt document_json still re-key identity/path so
212
222
  // the utility history survives; the next full index heals the JSON.
213
223
  let documentJson = row.document_json;
214
- let searchText = row.search_text;
224
+ let hash = row.embed_hash;
215
225
  let document;
216
226
  try {
217
227
  const entry = JSON.parse(row.document_json);
@@ -221,7 +231,7 @@ export function rekeyEntryInPlace(db, opts) {
221
231
  if (opts.newDerivedFrom !== undefined)
222
232
  entry.derivedFrom = opts.newDerivedFrom;
223
233
  documentJson = JSON.stringify(entry);
224
- searchText = buildSearchText(entry);
234
+ hash = embedHash(entry);
225
235
  document = entry;
226
236
  }
227
237
  catch {
@@ -235,7 +245,7 @@ export function rekeyEntryInPlace(db, opts) {
235
245
  db.transaction(() => {
236
246
  const stale = db.prepare("SELECT id FROM entries WHERE item_ref = ?").get(newItemRef);
237
247
  if (stale && stale.id !== row.id) {
238
- // Full child-row cleanup (embeddings, entries_vec, utility scores,
248
+ // Full child-row cleanup (embeddings, utility scores,
239
249
  // usage events, FTS + dirty marks) BEFORE the entries delete: the
240
250
  // `embeddings` FK is non-CASCADE and `foreign_keys = ON`, so a bare
241
251
  // entries delete would throw and roll back the entire re-key; and
@@ -243,11 +253,11 @@ export function rekeyEntryInPlace(db, opts) {
243
253
  deleteRelatedRows(db, [{ id: stale.id }]);
244
254
  db.prepare("DELETE FROM entries WHERE id = ?").run(stale.id);
245
255
  }
246
- db.prepare("UPDATE entries SET file_path = ?, document_json = ?, search_text = ?, item_ref = ?, concept_id = ? WHERE id = ?").run(opts.newFilePath, documentJson, searchText, newItemRef, opts.newRef, row.id);
256
+ db.prepare("UPDATE entries SET file_path = ?, document_json = ?, embed_hash = ?, item_ref = ?, concept_id = ? WHERE id = ?").run(opts.newFilePath, documentJson, hash, newItemRef, opts.newRef, row.id);
247
257
  if (opts.newDerivedFrom !== undefined) {
248
258
  db.prepare("UPDATE entries SET derived_from = ? WHERE id = ?").run(opts.newDerivedFrom, row.id);
249
259
  }
250
- if (row.search_text !== searchText)
260
+ if (row.embed_hash !== hash)
251
261
  deleteEntryVectors(db, row.id);
252
262
  if (document)
253
263
  replaceFtsEntry(db, row.id, document, hasMarkdownFragmentContent(document) ? (getMarkdownFragmentContent(document) ?? null) : undefined);
@@ -293,50 +303,6 @@ function rewriteUsageEventRefForMove(opts) {
293
303
  throw new Error(`Failed to rewrite usage events for move: ${message}`, { cause: error });
294
304
  }
295
305
  }
296
- /**
297
- * Phase 2A / Rec 5: bulk-load positive feedback event counts for the given
298
- * entry ids. Used by the utility-decay forgetting curve to stabilize
299
- * (extend the half-life of) memories that have repeatedly proven useful.
300
- *
301
- * Returns a `Map<entryId, count>` containing only entries with at least one
302
- * positive feedback event — missing ids implicitly map to `0`. Chunks at
303
- * `SQLITE_CHUNK_SIZE` (500) to respect `SQLITE_MAX_VARIABLE_NUMBER`.
304
- *
305
- * Cheap when called with zero ids, and silently empty when state.db (or its
306
- * `usage_events` table) is absent.
307
- *
308
- * Chunk-8 WI-8.3: usage_events lives in state.db — this reads it there (no
309
- * entries join needed; the ids are supplied by the caller). Gated by the caller
310
- * (`shouldQueryPositiveFeedbackCounts`) so the state.db open is not on the
311
- * default search hot path.
312
- */
313
- export function getPositiveFeedbackCountsByIds(ids) {
314
- const result = new Map();
315
- if (ids.length === 0 || !fs.existsSync(getStateDbPath()))
316
- return result;
317
- bestEffort(() => {
318
- withStateDb((stateDb) => {
319
- for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
320
- const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
321
- const placeholders = chunk.map(() => "?").join(",");
322
- const rows = stateDb
323
- .prepare(`SELECT entry_id, COUNT(*) AS cnt
324
- FROM usage_events
325
- WHERE event_type = 'feedback'
326
- AND signal = 'positive'
327
- AND entry_id IN (${placeholders})
328
- GROUP BY entry_id`)
329
- .all(...chunk);
330
- for (const row of rows) {
331
- if (row.entry_id !== null && row.cnt > 0) {
332
- result.set(row.entry_id, row.cnt);
333
- }
334
- }
335
- }
336
- });
337
- }, "positive feedback counts are best-effort");
338
- return result;
339
- }
340
306
  /**
341
307
  * Rows whose `file_path` sits directly in `dirPath`. A half-open byte range
342
308
  * over `idx_entries_file_path` (`[dir + sep, dir + sep + 1)`) turns the lookup
@@ -382,14 +348,30 @@ export function deleteEntriesByBundle(db, bundleId) {
382
348
  })();
383
349
  }
384
350
  /**
385
- * Delete the complete regenerable entry generation through the same child-row
386
- * authority used by targeted deletes. The caller may retain cross-database
387
- * usage events so the finalize pass can relink them to the new row ids.
351
+ * The `file_path` of every entry indexed under one bundle — used by
352
+ * `akm bundle rename` (D6) to find bundle CONTENT that still spells the old
353
+ * `<bundle>//` prefix (xrefs, `supersededBy`, task `uses:`), which the rename
354
+ * reports rather than rewrites.
388
355
  */
389
- export function deleteAllEntries(db, options = {}) {
356
+ export function getFilePathsByBundle(db, bundleId) {
357
+ const rows = db.prepare("SELECT DISTINCT file_path FROM entries WHERE bundle_id = ?").all(bundleId);
358
+ return rows.map((row) => row.file_path);
359
+ }
360
+ /**
361
+ * Re-key every entry row's `bundle_id`/`item_ref` from `oldBundleId` to
362
+ * `newBundleId` in place (`akm bundle rename`, D6). Unlike
363
+ * {@link rekeyEntryInPlace} (one asset, `akm mv`), this is a bulk identity
364
+ * change with no content move: `concept_id`/`file_path`/`document_json` are
365
+ * untouched, so no FTS/vector rebuild is needed (FTS and `embeddings` key on
366
+ * the entry's row `id`, which this preserves, not on `item_ref`). Returns the
367
+ * number of rows renamed.
368
+ */
369
+ export function renameEntriesBundleId(db, oldBundleId, newBundleId) {
390
370
  return db.transaction(() => {
391
- const rows = db.prepare("SELECT id FROM entries").all();
392
- return deleteEntryRows(db, rows, options);
371
+ const result = db
372
+ .prepare("UPDATE entries SET bundle_id = ?, item_ref = ? || '//' || concept_id WHERE bundle_id = ?")
373
+ .run(newBundleId, newBundleId, oldBundleId);
374
+ return Number(result.changes);
393
375
  })();
394
376
  }
395
377
  /**
@@ -415,7 +397,6 @@ function deleteRelatedRows(db, ids, options = {}) {
415
397
  if (ids.length === 0)
416
398
  return;
417
399
  const numericIds = ids.map((r) => r.id);
418
- const vecAvail = isVecAvailable(db);
419
400
  // FTS is part of the canonical mutation boundary, not a caller-maintained
420
401
  // dirty queue. Delete it before the parent row inside this transaction.
421
402
  deleteFtsEntries(db, numericIds);
@@ -424,47 +405,18 @@ function deleteRelatedRows(db, ids, options = {}) {
424
405
  const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
425
406
  const placeholders = chunk.map(() => "?").join(",");
426
407
  bestEffort(() => db.prepare(`DELETE FROM embeddings WHERE id IN (${placeholders})`).run(...chunk), "delete embeddings for entries");
427
- if (vecAvail) {
428
- bestEffort(() => db.prepare(`DELETE FROM entries_vec WHERE id IN (${placeholders})`).run(...chunk), "delete entries_vec for entries");
429
- }
430
408
  // Clean up utility scores before deleting entries
431
409
  bestEffort(() => db.prepare(`DELETE FROM utility_scores WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores for entries");
432
410
  bestEffort(() => db.prepare(`DELETE FROM utility_scores_scoped WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores_scoped for entries");
433
411
  }
434
- // Graph rows are independently keyed and intentionally survive entry
435
- // deletion. Resolve their owning roots through the canonical physical path
436
- // before entries disappear, then refresh the derived summary counts.
437
- const affectedGraphRoots = new Set();
438
- for (let i = 0; i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
439
- const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
440
- const placeholders = chunk.map(() => "?").join(",");
441
- bestEffort(() => {
442
- const rows = db
443
- .prepare(`SELECT DISTINCT gf.stash_root
444
- FROM graph_files gf
445
- JOIN entries e ON e.file_path = gf.file_path
446
- WHERE e.id IN (${placeholders})`)
447
- .all(...chunk);
448
- for (const row of rows)
449
- affectedGraphRoots.add(row.stash_root);
450
- }, "resolve graph roots for graph_meta recompute");
451
- }
452
- for (const stashRoot of affectedGraphRoots) {
453
- bestEffort(() => db
454
- .prepare(`UPDATE graph_meta
455
- SET extracted_files = (SELECT COUNT(*) FROM graph_files WHERE stash_root = ?),
456
- entity_count = (SELECT COUNT(*) FROM graph_file_entities WHERE stash_root = ?),
457
- relation_count = (SELECT COUNT(*) FROM graph_file_relations WHERE stash_root = ?)
458
- WHERE stash_root = ?`)
459
- .run(stashRoot, stashRoot, stashRoot, stashRoot), "sync graph_meta counts after entries delete");
460
- }
461
412
  // usage_events lives in state.db, outside this transaction. Index persistence
462
413
  // disables this cleanup and runs it only after its index.db transaction
463
414
  // commits; standalone delete callers retain the immediate behavior.
464
415
  if (options.cleanupUsageEvents !== false)
465
416
  deleteUsageEventsByEntryIds(numericIds);
466
417
  // graph_files is keyed by its own stash_root/file_path/body_hash identity,
467
- // so deleting an entry row intentionally leaves extracted graph data intact.
418
+ // so deleting an entry row intentionally leaves extracted graph data intact,
419
+ // and with it the graph_meta counts the graph writer derives from those rows.
468
420
  }
469
421
  export function deleteUsageEventsByEntryIds(entryIds) {
470
422
  if (entryIds.length === 0 || !fs.existsSync(getStateDbPath()))
@@ -481,7 +433,7 @@ export function deleteUsageEventsByEntryIds(entryIds) {
481
433
  }
482
434
  /**
483
435
  * Delete entries by their primary key IDs, along with all related rows
484
- * (embeddings, entries_vec, entries_fts, utility scores, usage_events).
436
+ * (embeddings, entries_fts, utility scores, usage_events).
485
437
  *
486
438
  * Used by explicit `--clean` reconciliation before embeddings and final
487
439
  * verification to remove stale entries whose source files no longer exist.
@@ -645,6 +597,20 @@ export function getEntryById(db, id) {
645
597
  adapterId: row.adapter_id,
646
598
  };
647
599
  }
600
+ /** The `item_ref` and `type` of every id that has an entry row. */
601
+ export function getEntryRefsAndTypes(db, ids) {
602
+ const out = new Map();
603
+ for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
604
+ const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
605
+ const placeholders = chunk.map(() => "?").join(",");
606
+ const rows = db
607
+ .prepare(`SELECT id, item_ref, type FROM entries WHERE id IN (${placeholders})`)
608
+ .all(...chunk);
609
+ for (const row of rows)
610
+ out.set(row.id, { itemRef: row.item_ref, type: row.type });
611
+ }
612
+ return out;
613
+ }
648
614
  export function getEntriesByDir(db, dirPath) {
649
615
  return parseEntryRows(selectRowsInDirectory(db, dirPath, ENTRY_COLUMNS), "getEntriesByDir");
650
616
  }
@@ -809,15 +775,7 @@ function resolveUsageEventEntryId(db, ref) {
809
775
  * distinct linked entry_ids in usage_events is small — and the re-resolution
810
776
  * reads `entries` from `indexDb`.
811
777
  */
812
- function qualifiedUsageEventsTable(stateSchema) {
813
- if (stateSchema === undefined)
814
- return "usage_events";
815
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(stateSchema))
816
- throw new Error("Invalid attached state schema name.");
817
- return `"${stateSchema}".usage_events`;
818
- }
819
- export function relinkUsageEvents(indexDb, stateDb, options = {}) {
820
- const usageEvents = qualifiedUsageEventsTable(options.stateSchema);
778
+ export function relinkUsageEvents(indexDb, stateDb) {
821
779
  bestEffort(() => {
822
780
  // Step 1: null out stale entry_ids (entry was deleted, re-keyed, etc).
823
781
  // Leaving them in place would let `recomputeUtilityScores` aggregate by an
@@ -826,7 +784,7 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
826
784
  // transaction. Nulled rows can be re-resolved by step 2 below; events whose
827
785
  // entry is permanently gone simply stay null and age out via retention.
828
786
  const linkedRows = stateDb
829
- .prepare(`SELECT DISTINCT entry_id AS id, entry_ref AS ref FROM ${usageEvents} WHERE entry_id IS NOT NULL`)
787
+ .prepare("SELECT DISTINCT entry_id AS id, entry_ref AS ref FROM usage_events WHERE entry_id IS NOT NULL")
830
788
  .all();
831
789
  const entryIdentity = indexDb.prepare("SELECT item_ref AS itemRef FROM entries WHERE id = ?");
832
790
  const staleLinks = linkedRows.filter(({ id, ref }) => {
@@ -834,7 +792,7 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
834
792
  return live == null || (ref !== null && live.itemRef !== ref);
835
793
  });
836
794
  if (staleLinks.length > 0) {
837
- const nullOut = stateDb.prepare(`UPDATE ${usageEvents} SET entry_id = NULL WHERE entry_id = ? AND entry_ref IS ?`);
795
+ const nullOut = stateDb.prepare("UPDATE usage_events SET entry_id = NULL WHERE entry_id = ? AND entry_ref IS ?");
838
796
  const nullTx = stateDb.transaction(() => {
839
797
  for (const { id, ref } of staleLinks)
840
798
  nullOut.run(id, ref);
@@ -844,9 +802,9 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
844
802
  // Step 2: re-resolve each fully-qualified ref. Bare rows are not current
845
803
  // durable identities and remain detached.
846
804
  const refs = stateDb
847
- .prepare(`SELECT DISTINCT entry_ref AS ref FROM ${usageEvents} WHERE entry_id IS NULL AND entry_ref IS NOT NULL`)
805
+ .prepare("SELECT DISTINCT entry_ref AS ref FROM usage_events WHERE entry_id IS NULL AND entry_ref IS NOT NULL")
848
806
  .all();
849
- const update = stateDb.prepare(`UPDATE ${usageEvents} SET entry_id = ? WHERE entry_ref = ? AND entry_id IS NULL`);
807
+ const update = stateDb.prepare("UPDATE usage_events SET entry_id = ? WHERE entry_ref = ? AND entry_id IS NULL");
850
808
  const relinkTx = stateDb.transaction(() => {
851
809
  for (const { ref } of refs) {
852
810
  let id;
@@ -23,7 +23,7 @@ import { warn } from "../../core/warn.js";
23
23
  * Durable identity columns are surfaced here so every mapped entry carries
24
24
  * current indexed provenance.
25
25
  */
26
- export const ENTRY_COLUMNS = "id, item_ref, bundle_id, component_id, concept_id, adapter_id, type, file_path, content_hash, document_json, search_text";
26
+ export const ENTRY_COLUMNS = "id, item_ref, bundle_id, component_id, concept_id, adapter_id, type, file_path, content_hash, document_json";
27
27
  /**
28
28
  * Map one raw `entries` row to a {@link DbIndexedEntry}, parsing `document_json`.
29
29
  * Returns `null` (and warns, tagged with `context`) when the JSON is corrupt so
@@ -42,7 +42,6 @@ export function rowToIndexedEntry(row, context) {
42
42
  id: row.id,
43
43
  filePath: row.file_path,
44
44
  entry,
45
- searchText: row.search_text,
46
45
  itemRef: row.item_ref,
47
46
  conceptId: row.concept_id,
48
47
  bundleId: row.bundle_id,