akm-cli 0.9.16 → 0.9.17-alpha.10

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 (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -6,26 +6,29 @@ import { isVerbose, warn, warnVerbose } from "../core/warn.js";
6
6
  import { embedBatch } from "../llm/embedder.js";
7
7
  import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../llm/embedders/deterministic.js";
8
8
  import { DEFAULT_LOCAL_MODEL } from "../llm/embedders/local.js";
9
- import { buildTokenBoundedBatches, capEmbeddingText, DEFAULT_MAX_INPUT_TOKENS, DEFAULT_REMOTE_BATCH_SIZE, DEFAULT_TOKEN_BUDGET, describeEmbeddingCredential, estimateTokenCount, hasRemoteEndpoint, normalizeEmbeddingEndpoint, } from "../llm/embedders/remote.js";
10
- import { cosineSimilarity } from "../llm/embedders/types.js";
11
- import { purgeEmbeddingSalvage, relabelEmbeddingSalvageFingerprint, reuseSalvagedEmbeddings, } from "../storage/repositories/embedding-salvage-repository.js";
9
+ import { applyEmbeddingTemplate, documentTemplateFingerprint, resolveEmbeddingProfile } from "../llm/embedders/profile.js";
10
+ import { capEmbeddingText, DEFAULT_MAX_INPUT_TOKENS, describeEmbeddingCredential, estimateTokenCount, hasRemoteEndpoint, normalizeEmbeddingEndpoint, } from "../llm/embedders/remote.js";
12
11
  import { getEmbeddableEntryCount } from "../storage/repositories/index-entries-repository.js";
13
- import { deleteMeta, getMeta, setMeta } from "../storage/repositories/index-meta-repository.js";
14
- import { EMBEDDING_DIM } from "../storage/repositories/index-schema.js";
15
- import { getAllEntriesForEmbedding, getEmbeddingCount, isVecFastPathComplete, isVecFastPathReady, purgeEmbeddings, repairVecFastPath, sampleEmbeddedEntriesForCanary, setVecFastPathReady, upsertEmbedding, } from "../storage/repositories/index-vec-repository.js";
12
+ import { getMeta, setMeta } from "../storage/repositories/index-meta-repository.js";
13
+ import { getAllEntriesForEmbedding, getEmbeddingCount, purgeEmbeddings, upsertEmbedding, } from "../storage/repositories/index-vec-repository.js";
16
14
  import { reclassifyIndexDbContention } from "./index-db-contention.js";
17
- /** Identifies the embedding provider+model+dimension a stored vector was generated with. */
15
+ /**
16
+ * Identifies what a stored vector was generated with: provider, model and
17
+ * dimension, plus the document template when the embedding profile has one
18
+ * (so changing it re-embeds the index).
19
+ */
18
20
  export function deriveSemanticProviderFingerprint(embedding) {
19
21
  if (isDeterministicEmbedEnabled()) {
20
22
  return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
21
23
  }
24
+ const template = documentTemplateFingerprint(embedding);
22
25
  if (embedding?.endpoint) {
23
26
  // Fingerprint keys on vector identity only (model + dimension). The endpoint
24
27
  // is transport/routing and has no bearing on vector compatibility, so moving
25
- // the same model+dimension to a different host must not force a full re-embed.
26
- return `remote:${embedding.model}|${embedding.dimension ?? "default"}`;
28
+ // the same model+dimension to a different host must not force a re-embed.
29
+ return `remote:${embedding.model}|${embedding.dimension ?? "default"}${template}`;
27
30
  }
28
- return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}`;
31
+ return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}${template}`;
29
32
  }
30
33
  /**
31
34
  * The heartbeat text emitted every 15s while a provider request is in
@@ -35,19 +38,6 @@ export function deriveSemanticProviderFingerprint(embedding) {
35
38
  export function formatEmbeddingHeartbeat(storedCount, total, failedCount) {
36
39
  return `Still generating embeddings: ${storedCount}/${total} stored, ${failedCount} failed; waiting on embedding provider.`;
37
40
  }
38
- /**
39
- * Number of already-embedded entries sampled for the fingerprint-rename
40
- * canary (#955) — small and cheap even against a slow local server; a
41
- * handful of chunks is a strong compatibility signal (a different model
42
- * cannot plausibly land near-identical vectors by chance).
43
- */
44
- const CANARY_SAMPLE_SIZE = 8;
45
- /**
46
- * Minimum median cosine similarity between stored and freshly re-embedded
47
- * canary vectors for a fingerprint-string change to be treated as a
48
- * same-model rename rather than a real model change (#955).
49
- */
50
- const CANARY_SIMILARITY_THRESHOLD = 0.999;
51
41
  /**
52
42
  * Consecutive transport failures after which the embedding pass stops
53
43
  * dispatching further requests and ends the run as a failure rather than
@@ -65,152 +55,6 @@ const CANARY_SIMILARITY_THRESHOLD = 0.999;
65
55
  * streaks instead.
66
56
  */
67
57
  const CIRCUIT_BREAKER_THRESHOLD = 3;
68
- /**
69
- * Pure decision: do stored vectors remain valid against freshly re-embedded
70
- * canary samples? The ONE place that computes the canary's similarity
71
- * numbers — callers must use this result rather than recomputing it (#955).
72
- *
73
- * An empty sample means nothing is stored to lose or verify against, so
74
- * there is nothing to decide — keep.
75
- *
76
- * A sample whose re-embed FAILED (`fresh === undefined`, e.g. a provider
77
- * sub-batch that was skipped) is EXCLUDED from the similarity computation
78
- * entirely, not scored as zero: a partial provider failure is not evidence
79
- * of a different model (#955). A dimension mismatch on a successful
80
- * re-embed still counts as zero similarity via {@link cosineSimilarity}'s
81
- * own dimension-mismatch guard — that IS evidence. When half or fewer of
82
- * the sampled entries re-embedded successfully, the sample is too thin to
83
- * trust either verdict — the outcome is `unverifiable`, the same outcome a
84
- * total canary failure already produces.
85
- *
86
- * Otherwise the MEDIAN pairwise cosine similarity of the verified samples
87
- * must clear {@link CANARY_SIMILARITY_THRESHOLD}; the median (not the
88
- * minimum or mean) tolerates one stale or lightly-edited sample without
89
- * either discarding a real match or being fooled by it.
90
- */
91
- export function decideEmbeddingCompatibility(pairs) {
92
- if (pairs.length === 0)
93
- return { outcome: "keep", medianSimilarity: undefined, verifiedSamples: 0 };
94
- const verified = pairs.filter((pair) => pair.fresh !== undefined);
95
- if (verified.length * 2 <= pairs.length) {
96
- return { outcome: "unverifiable", medianSimilarity: undefined, verifiedSamples: verified.length };
97
- }
98
- const similarities = verified.map((pair) => cosineSimilarity(pair.stored, pair.fresh));
99
- const medianSimilarity = medianOf(similarities);
100
- return {
101
- outcome: medianSimilarity >= CANARY_SIMILARITY_THRESHOLD ? "keep" : "rebuild",
102
- medianSimilarity,
103
- verifiedSamples: verified.length,
104
- };
105
- }
106
- function medianOf(values) {
107
- const sorted = [...values].sort((a, b) => a - b);
108
- const mid = Math.floor(sorted.length / 2);
109
- return sorted.length % 2 === 0
110
- ? (sorted[mid - 1] + sorted[mid]) / 2
111
- : sorted[mid];
112
- }
113
- /**
114
- * Identity of the embedding vectors actually observed on a run — as opposed
115
- * to {@link deriveSemanticProviderFingerprint}'s CONFIG-derived string. Keys
116
- * on what the server (or local model) actually reported plus the observed
117
- * vector width, so a gateway/transport change that keeps returning the same
118
- * underlying model can be told apart from a genuine model change without
119
- * relying on the operator's config string (#955).
120
- * Returns undefined when nothing was actually observed this call (no vector
121
- * to measure yet).
122
- */
123
- function deriveObservedEmbeddingIdentity(embedding, observedModel, observedVectorLen) {
124
- if (isDeterministicEmbedEnabled()) {
125
- return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
126
- }
127
- if (observedVectorLen === undefined)
128
- return undefined;
129
- if (embedding?.endpoint) {
130
- return `remote:${observedModel ?? embedding.model ?? "unknown"}|${observedVectorLen}`;
131
- }
132
- return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}|${observedVectorLen}`;
133
- }
134
- /**
135
- * Run the fingerprint-rename canary: re-embed a small sample of already-
136
- * stored entries with the CURRENT config and decide whether the stored
137
- * index survives. Goes through the standard {@link embedBatch} facade (not a
138
- * direct `RemoteEmbedder`) so every embedder branch — remote, local,
139
- * deterministic, and test overrides via `_setEmbedderForTests` — is
140
- * exercised identically to the main embedding pass.
141
- *
142
- * `maxInputTokens` must be the SAME cap the main pass below applies via
143
- * {@link capEmbeddingText} — the stored vector for each sampled entry was
144
- * produced from its capped text, so comparing against a fresh vector of the
145
- * uncapped text would compare unlike inputs for any entry over the cap
146
- * (#955).
147
- */
148
- async function runEmbeddingCanary(db, config, signal, maxInputTokens) {
149
- const samples = sampleEmbeddedEntriesForCanary(db, CANARY_SAMPLE_SIZE);
150
- if (samples.length === 0) {
151
- return { outcome: "keep", verified: false, viaIdentityMatch: false };
152
- }
153
- let observedModel;
154
- const skips = [];
155
- let canaryVectors;
156
- try {
157
- canaryVectors = await embedBatch(
158
- // #955: the stored vector for each sample was produced from
159
- // capEmbeddingText(searchText, maxInputTokens) — the main pass below
160
- // caps every document before embedding it. The canary must re-embed
161
- // the SAME capped text, or an entry over the cap compares a fresh
162
- // vector of a different input against a stored vector of the capped
163
- // one, and a genuine model match can read as a rebuild-worthy
164
- // mismatch for reasons unrelated to the model.
165
- samples.map((sample) => capEmbeddingText(sample.searchText, maxInputTokens).text), config.embedding, signal, (skip) => skips.push(skip), (_indices, _embeddings, model) => {
166
- if (model)
167
- observedModel = model;
168
- });
169
- }
170
- catch (error) {
171
- const message = error instanceof Error ? error.message : String(error);
172
- return {
173
- outcome: "unverifiable",
174
- message: `could not verify embedding compatibility (${message}); keeping existing vectors — rerun akm index when the endpoint is reachable`,
175
- };
176
- }
177
- const observedVectorLen = canaryVectors.find((vector) => vector !== undefined)?.length;
178
- const observedIdentity = deriveObservedEmbeddingIdentity(config.embedding, observedModel, observedVectorLen);
179
- const storedIdentity = getMeta(db, "embeddingIdentity");
180
- if (storedIdentity && observedIdentity && storedIdentity === observedIdentity) {
181
- // The server reports the same model identity as last time — no need to
182
- // even look at the cosines; the config string alone was misleading.
183
- return { outcome: "keep", verified: true, identity: observedIdentity, viaIdentityMatch: true };
184
- }
185
- const pairs = samples.map((sample, i) => ({ stored: sample.vector, fresh: canaryVectors[i] }));
186
- const decision = decideEmbeddingCompatibility(pairs);
187
- if (decision.outcome === "unverifiable") {
188
- // Covers both a total provider failure (RemoteEmbedder skips a failing
189
- // request rather than throwing, #874, so an unreachable endpoint
190
- // surfaces here as an all-`undefined` canary result, not a caught
191
- // exception) and a partial one thin enough that neither verdict can be
192
- // trusted (#955) — same message path either way.
193
- const message = skips[0]?.message ?? "embedding provider returned no vectors for the canary sample";
194
- return {
195
- outcome: "unverifiable",
196
- message: `could not verify embedding compatibility (${message}); keeping existing vectors — rerun akm index when the endpoint is reachable`,
197
- };
198
- }
199
- if (decision.outcome === "keep") {
200
- return {
201
- outcome: "keep",
202
- verified: true,
203
- identity: observedIdentity,
204
- viaIdentityMatch: false,
205
- medianSimilarity: decision.medianSimilarity,
206
- };
207
- }
208
- return {
209
- outcome: "rebuild",
210
- identity: observedIdentity,
211
- reason: `vectors differ (median similarity ${decision.medianSimilarity?.toFixed(3)})`,
212
- };
213
- }
214
58
  function throwIfAborted(signal) {
215
59
  if (signal?.aborted) {
216
60
  throw signal.reason instanceof Error ? signal.reason : new Error("index interrupted");
@@ -223,10 +67,9 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
223
67
  // unobservable SAVEPOINT instead, so an interruption (competing-process
224
68
  // collision, SIGKILL) could lose the whole pass rather than only the batch
225
69
  // in flight. This is an internal contract error (a caller bug), not a
226
- // user-facing failure class: callers with their own transaction (e.g. `akm
227
- // bundle update`'s unified update transaction) must run the embedding
228
- // phase on a separate connection AFTER their own transaction commits — see
229
- // `runEmbeddingPass` in `src/indexer/indexer.ts`.
70
+ // user-facing failure class: a caller with its own transaction must run the
71
+ // embedding phase on a separate connection AFTER that transaction commits —
72
+ // see `runEmbeddingPass` in `src/indexer/indexer.ts`.
230
73
  if (db.inTransaction) {
231
74
  throw new Error("generateEmbeddingsForDb was called with an ambient transaction already open on `db`: per-batch commits " +
232
75
  "would become SAVEPOINTs inside it, losing the crash-durability contract per-batch commit exists for. " +
@@ -234,11 +77,6 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
234
77
  }
235
78
  throwIfAborted(signal);
236
79
  if (config.semanticSearchMode === "off") {
237
- // #955: salvage is self-emptying only if every path that skips reuse
238
- // also drains it — otherwise a full rebuild performed with semantic
239
- // search disabled leaves permanent orphaned rows behind (nothing will
240
- // ever consume them, since this path never reaches the reuse step).
241
- purgeEmbeddingSalvage(db);
242
80
  onProgress({ phase: "embeddings", message: "Semantic search disabled; skipping embeddings." });
243
81
  return { success: false, message: "Semantic search is disabled." };
244
82
  }
@@ -246,8 +84,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
246
84
  // fix (every RemoteEmbedder path already resolves secret:// through one
247
85
  // boundary — a keyless request can only mean embedding.apiKey was absent
248
86
  // from the config THIS run loaded). One default-level line, before the
249
- // first provider request of the phase (the canary probe or the main
250
- // pass, whichever runs first below), naming the endpoint/model/credential
87
+ // first provider request of the phase, naming the endpoint/model/credential
251
88
  // SOURCE — never the credential value.
252
89
  if (hasRemoteEndpoint(config.embedding ?? {})) {
253
90
  const endpoint = normalizeEmbeddingEndpoint(config.embedding?.endpoint ?? "");
@@ -258,167 +95,56 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
258
95
  message: `[embed] endpoint ${endpoint}, model ${config.embedding?.model ?? "unknown"}; credential: ${credential}${configFileSuffix}`,
259
96
  });
260
97
  }
261
- // A targeted call starts from an already-published generation. Preserve its
262
- // trust decision in O(1): successful writes for the changed IDs keep a
263
- // healthy fast path healthy, but can never promote a generation already
264
- // marked degraded. Global runs can afford to verify the entire derived set.
265
- const vecFastPathWasReady = isVecFastPathReady(db);
266
- const currentFingerprint = deriveSemanticProviderFingerprint(config.embedding);
267
- const storedFingerprint = getMeta(db, "embeddingFingerprint");
98
+ const model = deriveSemanticProviderFingerprint(config.embedding);
99
+ const storedModel = getMeta(db, "embeddingFingerprint");
268
100
  let targetEntryIds = entryIds;
269
- /** Set only on an actual rebuild, so the up-front "Re-embedding N entries" line names why. */
101
+ /** Set only when every entry is (re)embedded, so the up-front "Re-embedding N entries" line names why. */
270
102
  let rebuildReason;
271
- // Resolved once and reused by both the canary (below) and the main pass's
272
- // cap loop (further down) — the same cap must apply to both, or the canary
273
- // compares a differently-capped text against the stored vector (#955).
274
103
  const maxInputTokens = config.embedding?.maxInputTokens ?? DEFAULT_MAX_INPUT_TOKENS;
275
104
  if (opts?.forceReembed) {
276
- // `akm index --reembed`: an explicit operator override, skips the canary
277
- // entirely. The new fingerprint (and identity, now stale/unknown until
278
- // the next successful pass observes it) is written in the SAME
279
- // transaction as the purge, before any embedding request — a restart
280
- // then sees a matching fingerprint and only heals what is still missing
281
- // instead of purging again from zero (#955/#956).
105
+ // The explicit operator override: purge and re-embed everything. The model
106
+ // is recorded in the SAME transaction as the purge, before any embedding
107
+ // request, so a restart heals only what is still missing instead of
108
+ // purging again from zero (#955/#956).
282
109
  db.transaction(() => {
283
- purgeEmbeddings(db, { dropVecTable: true });
284
- // #955: an explicit forced rebuild must re-embed everything, not
285
- // quietly satisfy some of it from stale salvage.
286
- purgeEmbeddingSalvage(db);
287
- deleteMeta(db, "embeddingDim");
288
- setMeta(db, "embeddingFingerprint", currentFingerprint);
289
- deleteMeta(db, "embeddingIdentity");
110
+ purgeEmbeddings(db);
111
+ setMeta(db, "embeddingFingerprint", model);
290
112
  })();
291
113
  targetEntryIds = undefined;
292
114
  rebuildReason = "forced by --reembed";
293
115
  }
294
- else if (storedFingerprint && storedFingerprint !== currentFingerprint) {
295
- const decision = await runEmbeddingCanary(db, config, signal, maxInputTokens);
296
- if (decision.outcome === "unverifiable") {
297
- // Destroying a good index because the server happens to be down right
298
- // now is worse than leaving a rename unverified until the next run —
299
- // keep the vectors AND the old fingerprint so the next `akm index`
300
- // retries the canary instead of silently treating this as resolved.
301
- warn(`[embed] ${decision.message}`);
302
- onProgress({ phase: "embeddings", message: decision.message });
303
- return { success: false, message: decision.message };
304
- }
305
- if (decision.outcome === "rebuild") {
306
- db.transaction(() => {
307
- purgeEmbeddings(db, { dropVecTable: true });
308
- // #955: the stored vectors AND any leftover salvage both belong to
309
- // a different model now — neither is reusable, so both go.
310
- purgeEmbeddingSalvage(db);
311
- deleteMeta(db, "embeddingDim");
312
- setMeta(db, "embeddingFingerprint", currentFingerprint);
313
- if (decision.identity)
314
- setMeta(db, "embeddingIdentity", decision.identity);
315
- else
316
- deleteMeta(db, "embeddingIdentity");
317
- })();
318
- targetEntryIds = undefined;
319
- rebuildReason = decision.reason;
320
- }
321
- else {
322
- // Keep: adopt the new fingerprint (and identity, when observed)
323
- // immediately rather than deferring to end-of-run — nothing was
324
- // purged, so there is nothing an interruption could lose, and an
325
- // immediate write means a crash right after this decision does not
326
- // re-run the canary needlessly on the next attempt.
327
- setMeta(db, "embeddingFingerprint", currentFingerprint);
328
- // #955: the model did not actually change, only the fingerprint
329
- // STRING did (e.g. a gateway rename) — any leftover salvage rows
330
- // tagged with the OLD string are still valid vectors. Relabel them so
331
- // the reuse step below (and any later pass) can still find them.
332
- relabelEmbeddingSalvageFingerprint(db, storedFingerprint, currentFingerprint);
333
- if (decision.identity)
334
- setMeta(db, "embeddingIdentity", decision.identity);
335
- if (decision.verified) {
336
- const keptCount = getEmbeddingCount(db);
337
- const detail = decision.viaIdentityMatch
338
- ? "server-reported model unchanged"
339
- : `stored vectors are compatible (median similarity ${decision.medianSimilarity?.toFixed(3)})`;
340
- const message = `[embed] embedding model renamed (${storedFingerprint} → ${currentFingerprint}); ${detail}, keeping ${keptCount} embedding${keptCount === 1 ? "" : "s"}.`;
341
- warn(message);
342
- onProgress({ phase: "embeddings", message });
343
- }
344
- // Empty-sample case (decision.verified === false): nothing stored to
345
- // lose or verify against — adopt the label silently, no purge line.
346
- }
116
+ else if (storedModel && storedModel !== model) {
117
+ // The configured model changed. Nothing is purged: each stored row keeps
118
+ // the model that produced it until the pass below replaces it, so an
119
+ // interrupted pass resumes with only the rows still on the old model.
120
+ // Readers serve the current model's rows only, so nothing mixes vectors
121
+ // from two models.
122
+ setMeta(db, "embeddingFingerprint", model);
123
+ targetEntryIds = undefined;
124
+ rebuildReason = `the embedding model changed (${storedModel} → ${model}); stored vectors are kept until each entry is re-embedded`;
347
125
  }
348
126
  else {
349
- // No rename to verify (either this is the very first
350
- // pass ever for this db, or the fingerprint already matches the last
351
- // successful one) — still record it NOW rather than deferring to a
352
- // fully successful pass, mirroring the rebuild/keep branches above
353
- // (#955/#956). Without this, an interrupted FIRST-EVER pass left
354
- // `embeddingFingerprint` unset despite a per-batch commit below (#954)
355
- // already having durably written real vectors — a later `akm index
356
- // --full`'s salvage-before-discard step tags rows by this meta
357
- // (`salvageEmbeddingsBeforeDiscard`) and treats an unset fingerprint as
358
- // "nothing was ever verified", silently turning genuinely-embedded
359
- // vectors into a full re-embed instead of a salvage-and-reuse.
360
- setMeta(db, "embeddingFingerprint", currentFingerprint);
127
+ // First pass ever for this index, or the model already matches — record
128
+ // it NOW rather than after a fully successful pass (#955/#956), so an
129
+ // interrupted first pass still labels the vectors it durably committed.
130
+ setMeta(db, "embeddingFingerprint", model);
361
131
  }
362
132
  try {
363
133
  throwIfAborted(signal);
364
- if (entryIds === undefined && (!vecFastPathWasReady || !isVecFastPathComplete(db))) {
365
- const storedDim = Number(getMeta(db, "embeddingDim"));
366
- const expectedDim = Number.isInteger(storedDim) && storedDim > 0 ? storedDim : (config.embedding?.dimension ?? EMBEDDING_DIM);
367
- const repair = repairVecFastPath(db, expectedDim);
368
- if (repair.available &&
369
- (repair.repaired > 0 || repair.removedOrphans > 0 || repair.rejected > 0 || repair.error !== undefined)) {
370
- const detail = repair.error ? `; repair stopped: ${repair.error}` : "";
371
- onProgress({
372
- phase: "embeddings",
373
- message: `[embed] Repaired ${repair.repaired} missing sqlite-vec row${repair.repaired === 1 ? "" : "s"}; removed ${repair.removedOrphans} orphan${repair.removedOrphans === 1 ? "" : "s"}; ${repair.rejected} rejected${detail}.`,
374
- });
375
- }
376
- }
377
- const allEntries = getAllEntriesForEmbedding(db, targetEntryIds);
378
- let vecFailedCount = 0;
379
- let vecUnavailableCount = 0;
380
- // #955: before any provider call, hand back vectors salvaged from a
381
- // full rebuild or a generation bump for entries whose search_text is
382
- // byte-identical to what was salvaged under the SAME fingerprint — a
383
- // fingerprint mismatch or a single-byte content change both correctly
384
- // fall through to the provider below instead.
385
- const { reusedCount, remaining: candidateEntries } = reuseSalvagedEmbeddings(db, allEntries, currentFingerprint, (entry, embedding) => {
386
- const result = upsertEmbedding(db, entry.id, embedding);
387
- if (result.vec === "failed")
388
- vecFailedCount++;
389
- if (result.vec === "unavailable")
390
- vecUnavailableCount++;
391
- return result.stored;
392
- });
393
- if (reusedCount > 0) {
394
- onProgress({
395
- phase: "embeddings",
396
- message: `Reused ${reusedCount} embedding${reusedCount === 1 ? "" : "s"} from the previous generation; embedding ${candidateEntries.length} new.`,
397
- });
398
- }
134
+ const candidateEntries = getAllEntriesForEmbedding(db, targetEntryIds, model);
399
135
  if (candidateEntries.length === 0) {
400
136
  onProgress({ phase: "embeddings", message: "Embeddings already up to date." });
401
- setMeta(db, "embeddingFingerprint", currentFingerprint);
402
- if (reusedCount > 0) {
403
- const vecGenerationComplete = targetEntryIds === undefined ? isVecFastPathComplete(db) : vecFastPathWasReady;
404
- setVecFastPathReady(db, vecFailedCount === 0 && vecUnavailableCount === 0 && vecGenerationComplete);
405
- }
406
- // A pass that completes (even one that did nothing but reuse) purges
407
- // whatever is left — salvage is consumed by the NEXT pass, never kept
408
- // around as a second cache.
409
- purgeEmbeddingSalvage(db);
410
- return reusedCount > 0 ? { success: true, vecInsertFailures: vecFailedCount } : { success: true };
137
+ return { success: true };
411
138
  }
412
- // Cap each document's embedded text at
413
- // embedding.maxInputTokens (default DEFAULT_MAX_INPUT_TOKENS, resolved
414
- // once above so the canary uses the identical cap) instead of ever
415
- // failing a whole batch over one oversized entry — truncation keeps the
416
- // head of the text, unicode-safe. A document is skipped only when its
417
- // head is empty (the impossible case: nothing left to embed), never
418
- // merely for being long.
139
+ // Cap each document's embedded text at embedding.maxInputTokens (default
140
+ // DEFAULT_MAX_INPUT_TOKENS) instead of ever failing a whole batch over one
141
+ // oversized entry — truncation keeps the head of the text, unicode-safe. A
142
+ // document is skipped only when its head is empty (the impossible case:
143
+ // nothing left to embed), never merely for being long.
419
144
  let truncatedCount = 0;
420
145
  const texts = [];
421
146
  const pendingEntries = [];
147
+ const { documentTemplate } = resolveEmbeddingProfile(config.embedding);
422
148
  for (const entry of candidateEntries) {
423
149
  const capped = capEmbeddingText(entry.searchText, maxInputTokens);
424
150
  if (capped.text.length === 0)
@@ -426,7 +152,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
426
152
  if (capped.truncated)
427
153
  truncatedCount++;
428
154
  pendingEntries.push(entry);
429
- texts.push(capped.text);
155
+ texts.push(applyEmbeddingTemplate(documentTemplate, capped.text));
430
156
  }
431
157
  if (truncatedCount > 0) {
432
158
  // Through onProgress ONLY, not warn() too — onProgress already reaches
@@ -446,38 +172,11 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
446
172
  phase: "embeddings",
447
173
  message: `Generating embeddings for ${pendingEntries.length} entr${pendingEntries.length === 1 ? "y" : "ies"}.`,
448
174
  });
175
+ // Per-document sizes; the provider's actual request grouping is reported
176
+ // per batch below (and each skipped document is listed at the end).
449
177
  if (isVerbose()) {
450
- // Mirror RemoteEmbedder's actual token-bounded batching (#874) so this
451
- // log reflects the real request grouping rather than a fixed count of
452
- // 100 that no longer matches what gets sent over the wire. Local runs
453
- // don't batch by size at all (LocalEmbedder chunks by a fixed count
454
- // for inference throughput only, never fails/skips), so there's
455
- // nothing meaningful to report per-batch for them.
456
- if (hasRemoteEndpoint(config.embedding ?? {})) {
457
- // Mirrors RemoteEmbedder.embedBatch's own tokenBudget resolution
458
- // (#956: contextLength no longer feeds this).
459
- const tokenBudget = config.embedding?.maxTokens ?? DEFAULT_TOKEN_BUDGET;
460
- const maxCount = config.embedding?.batchSize ?? DEFAULT_REMOTE_BATCH_SIZE;
461
- const batches = buildTokenBoundedBatches(texts, tokenBudget, maxCount);
462
- const batchNumberByIndex = new Map();
463
- batches.forEach((batch, batchIdx) => {
464
- for (const i of batch.indices)
465
- batchNumberByIndex.set(i, batchIdx + 1);
466
- });
467
- for (const [i, entry] of pendingEntries.entries()) {
468
- const chars = entry.searchText.length;
469
- const tokens = estimateTokenCount(entry.searchText);
470
- const batch = batches[batchNumberByIndex.get(i) - 1];
471
- const label = batch?.oversized
472
- ? "oversized (skipped)"
473
- : `batch ${batchNumberByIndex.get(i)}/${batches.length}`;
474
- warnVerbose(`[embed] ${entry.itemRef} (${chars} chars, est. ${tokens} tokens) → ${label}`);
475
- }
476
- }
477
- else {
478
- for (const entry of pendingEntries) {
479
- warnVerbose(`[embed] ${entry.itemRef} (${entry.searchText.length} chars, est. ${estimateTokenCount(entry.searchText)} tokens)`);
480
- }
178
+ for (const entry of pendingEntries) {
179
+ warnVerbose(`[embed] ${entry.itemRef} (${entry.searchText.length} chars, est. ${estimateTokenCount(entry.searchText)} tokens)`);
481
180
  }
482
181
  }
483
182
  let heartbeatTimer;
@@ -538,17 +237,11 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
538
237
  // the very end (#954) — a competing-process lock error or any other
539
238
  // interruption partway through now keeps whatever already committed
540
239
  // instead of losing the entire pass.
541
- // Tracks what this run actually observed, so a successful pass can
542
- // record `embeddingIdentity` from real data rather than the config
543
- // string alone (#955) — only the first non-empty batch's vector width
544
- // is kept; every batch from one run shares the same provider/model.
545
- let observedModel;
546
- let observedVectorLen;
547
240
  // Whether the remote provider's endpoint/model/token language is
548
241
  // meaningful for this run — the per-batch diagnostic line below is
549
242
  // remote-only, same gate the credential diagnostic (#953) above uses.
550
243
  const reportPerBatchLine = hasRemoteEndpoint(config.embedding ?? {});
551
- const onBatch = (indices, batchEmbeddings, model, outcome) => {
244
+ const onBatch = (indices, batchEmbeddings, outcome) => {
552
245
  // #954 field-report follow-up: a "retrying" event carries nothing to
553
246
  // commit — the request hasn't settled yet — only the notice that a
554
247
  // back-off is about to be waited out, default-level so a run is
@@ -579,8 +272,6 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
579
272
  }
580
273
  return;
581
274
  }
582
- if (model)
583
- observedModel = model;
584
275
  // A batch that delivered at least one real embedding proves the
585
276
  // provider is currently answering — reset both circuit-breaker
586
277
  // streaks. (A wholly failed batch's `batchEmbeddings` are all
@@ -601,10 +292,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
601
292
  embedFailedCount++;
602
293
  continue;
603
294
  }
604
- if (observedVectorLen === undefined)
605
- observedVectorLen = embedding.length;
606
- const result = upsertEmbedding(db, entry.id, embedding);
607
- if (result.stored) {
295
+ if (upsertEmbedding(db, entry.id, embedding, model)) {
608
296
  storedCount++;
609
297
  // #954: sum the estimate of the text actually sent —
610
298
  // `texts[index]` is the capped string `embedBatch` was handed,
@@ -617,10 +305,6 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
617
305
  else {
618
306
  skippedCount++;
619
307
  }
620
- if (result.vec === "failed")
621
- vecFailedCount++;
622
- if (result.vec === "unavailable")
623
- vecUnavailableCount++;
624
308
  }
625
309
  })();
626
310
  // Default level, one line per provider batch (#954, field-report
@@ -653,16 +337,8 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
653
337
  if (skippedCount > 0) {
654
338
  warn(`[embed] ${skippedCount} embedding${skippedCount === 1 ? "" : "s"} skipped (entry deleted between queue and write)`);
655
339
  }
656
- const vecGenerationComplete = targetEntryIds === undefined ? isVecFastPathComplete(db) : vecFastPathWasReady;
657
- setVecFastPathReady(db, vecFailedCount === 0 && vecUnavailableCount === 0 && vecGenerationComplete);
658
- if (vecFailedCount > 0) {
659
- warn(`[embed] ${vecFailedCount} sqlite-vec fast-path insert${vecFailedCount === 1 ? "" : "s"} failed — ` +
660
- "semantic search will use the slower JS-cosine fallback over stored embeddings. " +
661
- "Rebuild with 'akm index --full' after resolving the vec table (often a vector-dimension mismatch).");
662
- }
663
340
  const entriesPerSec = storedCount / elapsedSeconds;
664
341
  const tokensPerSec = storedTokens / elapsedSeconds;
665
- const totalStored = storedCount + reusedCount;
666
342
  // #954, field-report follow-up: the final line
667
343
  // reports every outcome, not just what was stored — counts come from
668
344
  // the same collected `skips` the circuit breaker already uses,
@@ -675,16 +351,11 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
675
351
  const oversizedSkips = skips.filter((skip) => skip.reason === "context-window-exceeded");
676
352
  const timedOutSkips = skips.filter((skip) => skip.reason === "batch-request-failed" && skip.failureKind === "timeout");
677
353
  const failedSkips = skips.filter((skip) => skip.reason === "batch-request-failed" && skip.failureKind !== "timeout");
678
- const throughputLine = reusedCount > 0
679
- ? // #955: report reused and newly-embedded counts separately — the
680
- // rate figures below are provider throughput only (reuse is a
681
- // plain DB write, not provider work) and would be misleadingly
682
- // inflated if reused entries were folded into them.
683
- `Stored ${totalStored} embedding${totalStored === 1 ? "" : "s"} (${reusedCount} reused, ${storedCount} newly embedded) in ${elapsedSeconds.toFixed(1)}s (${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s)`
684
- : `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"} in ${elapsedSeconds.toFixed(1)}s (${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s)`;
685
354
  onProgress({
686
355
  phase: "embeddings",
687
- message: `${throughputLine}; ${oversizedSkips.length} oversized skipped, ${timedOutSkips.length} timed out, ${failedSkips.length} failed.`,
356
+ message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"} in ${elapsedSeconds.toFixed(1)}s ` +
357
+ `(${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s); ` +
358
+ `${oversizedSkips.length} oversized skipped, ${timedOutSkips.length} timed out, ${failedSkips.length} failed.`,
688
359
  });
689
360
  // Bounded itemRef-level detail for every skip category, not just
690
361
  // oversized — the aggregate counts above say HOW MANY documents timed
@@ -707,10 +378,6 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
707
378
  printSkipList("oversized documents", oversizedSkips);
708
379
  printSkipList("timed-out documents", timedOutSkips);
709
380
  printSkipList("failed documents", failedSkips);
710
- setMeta(db, "embeddingFingerprint", currentFingerprint);
711
- const observedIdentity = deriveObservedEmbeddingIdentity(config.embedding, observedModel, observedVectorLen);
712
- if (observedIdentity)
713
- setMeta(db, "embeddingIdentity", observedIdentity);
714
381
  // Circuit breaker tripped (#954): committed batches are
715
382
  // kept (nothing above discards them), but the pass is not a success —
716
383
  // the provider looks dead, not just occasionally flaky.
@@ -720,7 +387,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
720
387
  "were stored — rerun akm index when the endpoint is healthy";
721
388
  warn(`[embed] ${message}`);
722
389
  onProgress({ phase: "embeddings", message });
723
- return { success: false, message, vecInsertFailures: vecFailedCount };
390
+ return { success: false, message };
724
391
  }
725
392
  // Only a total failure (nothing at all embedded, despite having entries
726
393
  // to embed) turns into a phase failure. Any partial success — the vast
@@ -735,11 +402,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
735
402
  message: `All ${embedFailedCount} embedding batch(es) failed: ${firstMessage}`,
736
403
  };
737
404
  }
738
- // A pass that completes without abort or circuit-break purges
739
- // whatever salvage is left — consumed by this pass's reuse step
740
- // above, or superseded by what it just embedded.
741
- purgeEmbeddingSalvage(db);
742
- return { success: true, vecInsertFailures: vecFailedCount };
405
+ return { success: true };
743
406
  }
744
407
  finally {
745
408
  if (heartbeatTimer)
@@ -779,7 +442,7 @@ export function publishTargetedEmbeddingMeta(db, config) {
779
442
  return;
780
443
  }
781
444
  const entryCount = getEmbeddableEntryCount(db);
782
- const embeddingCount = getEmbeddingCount(db);
445
+ const embeddingCount = getEmbeddingCount(db, deriveSemanticProviderFingerprint(config.embedding));
783
446
  const ready = entryCount > 0 && embeddingCount >= entryCount;
784
447
  setMeta(db, "hasEmbeddings", ready ? "1" : "0");
785
448
  }