okengine 0.18.5 → 0.19.1

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 (512) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/skills.mdx +4 -1
  6. package/site/content/docs/client/auth.mdx +208 -0
  7. package/site/content/docs/client/calling.mdx +451 -0
  8. package/site/content/docs/client/index.mdx +208 -0
  9. package/site/content/docs/client/live.mdx +234 -0
  10. package/site/content/docs/client/meta.json +5 -0
  11. package/site/content/docs/client/react.mdx +249 -0
  12. package/site/content/docs/elements/ai/agents.mdx +235 -0
  13. package/site/content/docs/elements/ai/index.mdx +342 -0
  14. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  15. package/site/content/docs/elements/ai/meta.json +5 -0
  16. package/site/content/docs/elements/ai/models.mdx +283 -0
  17. package/site/content/docs/elements/ai/prompts.mdx +257 -0
  18. package/site/content/docs/elements/channel/email.mdx +269 -0
  19. package/site/content/docs/elements/channel/index.mdx +344 -0
  20. package/site/content/docs/elements/channel/meta.json +5 -0
  21. package/site/content/docs/elements/channel/push.mdx +218 -0
  22. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  23. package/site/content/docs/elements/channel/sms.mdx +264 -0
  24. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  25. package/site/content/docs/elements/clock/index.mdx +301 -0
  26. package/site/content/docs/elements/clock/meta.json +5 -0
  27. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  28. package/site/content/docs/elements/clock/sleep.mdx +448 -0
  29. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  30. package/site/content/docs/elements/flow/http.mdx +1080 -0
  31. package/site/content/docs/elements/flow/index.mdx +618 -0
  32. package/site/content/docs/elements/flow/meta.json +5 -0
  33. package/site/content/docs/elements/flow/routing.mdx +559 -0
  34. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  35. package/site/content/docs/elements/gate/auth.mdx +371 -0
  36. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  37. package/site/content/docs/elements/gate/index.mdx +418 -0
  38. package/site/content/docs/elements/gate/meta.json +5 -0
  39. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  40. package/site/content/docs/elements/gate/rls.mdx +303 -0
  41. package/site/content/docs/elements/gate/tenancy.mdx +375 -0
  42. package/site/content/docs/elements/index.mdx +20 -10
  43. package/site/content/docs/elements/signal/broadcast.mdx +519 -0
  44. package/site/content/docs/elements/signal/index.mdx +352 -0
  45. package/site/content/docs/elements/signal/live.mdx +591 -0
  46. package/site/content/docs/elements/signal/meta.json +5 -0
  47. package/site/content/docs/elements/signal/once.mdx +597 -0
  48. package/site/content/docs/elements/store/files.mdx +667 -0
  49. package/site/content/docs/elements/store/index.mdx +313 -0
  50. package/site/content/docs/elements/store/kv.mdx +637 -0
  51. package/site/content/docs/elements/store/meta.json +5 -0
  52. package/site/content/docs/elements/store/search.mdx +950 -0
  53. package/site/content/docs/elements/store/sql.mdx +941 -0
  54. package/site/content/docs/elements/vault/config.mdx +259 -0
  55. package/site/content/docs/elements/vault/index.mdx +356 -0
  56. package/site/content/docs/elements/vault/meta.json +5 -0
  57. package/site/content/docs/elements/vault/rotation.mdx +276 -0
  58. package/site/content/docs/elements/vault/secrets.mdx +288 -0
  59. package/site/content/docs/index.mdx +52 -40
  60. package/site/content/docs/meta.json +8 -7
  61. package/site/content/docs/plugins/cors.mdx +2 -1
  62. package/site/content/docs/plugins/csrf.mdx +4 -1
  63. package/site/content/docs/plugins/headers.mdx +2 -1
  64. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  65. package/site/content/docs/plugins/magic-link.mdx +6 -0
  66. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  67. package/site/content/docs/plugins/oauth.mdx +1 -1
  68. package/site/content/docs/plugins/otp.mdx +12 -1
  69. package/site/content/docs/plugins/passkey.mdx +39 -15
  70. package/site/content/docs/plugins/two-factor.mdx +91 -34
  71. package/site/content/docs/plugins/username.mdx +1 -1
  72. package/site/content/docs/recipes/caddy.mdx +3 -3
  73. package/site/content/docs/recipes/index.mdx +18 -28
  74. package/site/content/docs/recipes/mailpit.mdx +1 -1
  75. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  76. package/site/content/docs/recipes/meta.json +1 -4
  77. package/site/content/docs/recipes/nginx.mdx +2 -2
  78. package/site/content/docs/recipes/openrouter.mdx +283 -0
  79. package/site/content/docs/recipes/pgdog.mdx +3 -3
  80. package/site/content/docs/recipes/postgres.mdx +1 -2
  81. package/site/content/docs/recipes/rustfs.mdx +2 -2
  82. package/site/content/docs/recipes/traefik.mdx +3 -3
  83. package/site/content/docs/reference/cli.mdx +231 -0
  84. package/site/content/docs/reference/configuration.mdx +30 -10
  85. package/site/content/docs/reference/environment-variables.mdx +93 -20
  86. package/site/content/docs/reference/errors.mdx +132 -29
  87. package/site/content/docs/reference/fx.mdx +138 -73
  88. package/site/content/docs/reference/index.mdx +13 -21
  89. package/site/content/docs/reference/meta.json +4 -5
  90. package/site/content/docs/reference/okid.mdx +42 -17
  91. package/site/content/docs/reference/plugins.mdx +4 -3
  92. package/site/content/docs/reference/security.mdx +197 -0
  93. package/site/content/docs/understand/meta.json +5 -0
  94. package/site/content/docs/understand/the-architecture.mdx +264 -0
  95. package/site/content/docs/understand/try-it.mdx +58 -0
  96. package/src/auth/api-keys.ts +2 -1
  97. package/src/auth/bindings.ts +64 -16
  98. package/src/auth/gate-auth.test.ts +6 -1
  99. package/src/auth/identity.ts +152 -5
  100. package/src/auth/index.ts +29 -0
  101. package/src/auth/invites.ts +2 -1
  102. package/src/auth/method-context.ts +41 -0
  103. package/src/auth/oauth-as/crypto.ts +2 -1
  104. package/src/auth/oauth-as/stores.ts +2 -1
  105. package/src/auth/operator.ts +2 -1
  106. package/src/auth/sessions-jwt.test.ts +77 -0
  107. package/src/auth/sessions.ts +20 -4
  108. package/src/auth/tenants.ts +3 -2
  109. package/src/auth/two-factor-challenge.test.ts +61 -0
  110. package/src/auth/two-factor-challenge.ts +284 -0
  111. package/src/auth/verification.ts +5 -0
  112. package/src/bench/REPORT.md +80 -0
  113. package/src/bench/g03-signal-once.bench.ts +1 -1
  114. package/src/bench/g10-observability-contention.bench.ts +1 -1
  115. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  116. package/src/bench/load-app.ts +2 -10
  117. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  118. package/src/cli/ai-setup/apply.ts +294 -52
  119. package/src/cli/ai-setup/catalog.ts +238 -1342
  120. package/src/cli/ai-setup/index.ts +14 -99
  121. package/src/cli/ai-setup/prompts.ts +60 -443
  122. package/src/cli/ask-seed.test.ts +96 -0
  123. package/src/cli/ask-seed.ts +82 -0
  124. package/src/cli/ask-vault-gaps.test.ts +84 -0
  125. package/src/cli/ask-vault-gaps.ts +146 -0
  126. package/src/cli/build.ts +2 -2
  127. package/src/cli/client-add.test.ts +26 -1
  128. package/src/cli/client-add.ts +106 -27
  129. package/src/cli/db-seed.ts +2 -0
  130. package/src/cli/db.ts +150 -11
  131. package/src/cli/dev.ts +119 -211
  132. package/src/cli/docker-clean.ts +2 -2
  133. package/src/cli/docker-cli.test.ts +1 -1
  134. package/src/cli/doctor-diff.ts +4 -2
  135. package/src/cli/doctor-pii.test.ts +1 -1
  136. package/src/cli/doctor.ts +14 -1
  137. package/src/cli/hero-meta.test.ts +4 -2
  138. package/src/cli/load-config.images.test.ts +8 -10
  139. package/src/cli/load-config.ts +1 -1
  140. package/src/cli/registry.ts +22 -10
  141. package/src/cli/replay.ts +3 -1
  142. package/src/cli/start.ts +1 -1
  143. package/src/cli/tui/keys.ts +2 -3
  144. package/src/client/auth/cookies.ts +74 -0
  145. package/src/client/auth/create-auth-client.ts +636 -0
  146. package/src/client/auth/denials.ts +99 -0
  147. package/src/client/auth/session.ts +283 -0
  148. package/src/client/auth.test.ts +251 -0
  149. package/src/client/auth.ts +39 -114
  150. package/src/client/create-with-session.ts +245 -0
  151. package/src/client/create.ts +51 -5
  152. package/src/client/index.ts +11 -1
  153. package/src/client/live.ts +11 -79
  154. package/src/client/notes-contract.test.ts +5 -10
  155. package/src/client/sse.ts +137 -0
  156. package/src/client/stream.ts +147 -0
  157. package/src/client/transport.test.ts +32 -0
  158. package/src/client/transport.ts +123 -26
  159. package/src/client/types.ts +118 -14
  160. package/src/client-react/index.ts +185 -24
  161. package/src/client-react/use-live-query.ts +13 -3
  162. package/src/compiler/aot.test.ts +7 -4
  163. package/src/compiler/effects-embed.test.ts +56 -0
  164. package/src/compiler/effects-fetch.test.ts +43 -0
  165. package/src/compiler/effects-infer.ts +58 -2
  166. package/src/compiler/extract.test.ts +257 -43
  167. package/src/compiler/extract.ts +566 -48
  168. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  169. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  170. package/src/compiler/fixtures/skyport.expected.json +4 -4
  171. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  172. package/src/compiler/response.ts +2 -2
  173. package/src/compiler/search-writer-isolation.test.ts +40 -0
  174. package/src/config/index.ts +11 -0
  175. package/src/console/server/app.ts +17 -23
  176. package/src/console/server/bind.ts +4 -0
  177. package/src/console/server/console.test.ts +2 -2
  178. package/src/console/server/flows-invoke.test.ts +50 -34
  179. package/src/console/server/flows.ts +436 -255
  180. package/src/console/server/runs-ingest.test.ts +6 -3
  181. package/src/console/server/serve.ts +1 -1
  182. package/src/console/server/signals.test.ts +1 -5
  183. package/src/console/server/signals.ts +8 -6
  184. package/src/console/server/state.ts +6 -1
  185. package/src/console/server/store.ts +1 -1
  186. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  187. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  188. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  189. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  190. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  191. package/src/console/ui-next/dist/assets/access-page-C_qLDhTq.js +4 -0
  192. package/src/console/ui-next/dist/assets/agent-disclosure-BHVqr3TN.js +1 -0
  193. package/src/console/ui-next/dist/assets/cache-glyph-CKe92lRQ.js +1 -0
  194. package/src/console/ui-next/dist/assets/call-pii-button-DEwTl8ZX.js +1 -0
  195. package/src/console/ui-next/dist/assets/collapsible-BCBtDrCt.js +1 -0
  196. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  197. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  198. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  199. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  200. package/src/console/ui-next/dist/assets/duration-tone-JroqeuCp.js +9 -0
  201. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  202. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  203. package/src/console/ui-next/dist/assets/flows-page-CVHa0RTt.js +1 -0
  204. package/src/console/ui-next/dist/assets/highlighted-json-DjJW6hqe.js +154 -0
  205. package/src/console/ui-next/dist/assets/http-method-DC5HBdLU.js +1 -0
  206. package/src/console/ui-next/dist/assets/index-CYjiZ3WO.js +63 -0
  207. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  208. package/src/console/ui-next/dist/assets/observability-page-CAYMyKb3.js +4 -0
  209. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  210. package/src/console/ui-next/dist/assets/replica-lag-yAQYLv75.js +18 -0
  211. package/src/console/ui-next/dist/assets/request-meta-D0yusGxJ.js +1 -0
  212. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  213. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  214. package/src/console/ui-next/dist/assets/store-page-BTKJeJ02.js +41 -0
  215. package/src/console/ui-next/dist/assets/trace-detail-sheet-Bp-Yygs5.js +2 -0
  216. package/src/console/ui-next/dist/assets/tree-expand-toggle-DoaVDfAM.js +55 -0
  217. package/src/console/ui-next/dist/assets/units-page-BRz7xyYL.js +1 -0
  218. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  219. package/src/console/ui-next/dist/assets/vault-page-3jQt-bOJ.js +2 -0
  220. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  221. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  222. package/src/console/ui-next/dist/index.html +6 -13
  223. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  224. package/src/console/ui-next/src/client.ts +7 -2
  225. package/src/console/ui-next/src/features/flows/graph/element-map.test.ts +1 -1
  226. package/src/console/ui-next/src/features/flows/graph/element-map.ts +5 -3
  227. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  228. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  229. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  230. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +4 -5
  231. package/src/console/ui-next/src/features/flows/traces/trace-gates.ts +3 -6
  232. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  234. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  235. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  236. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  237. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  238. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  239. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  240. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  241. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  242. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  243. package/src/docker/ai-model-status.test.ts +1 -12
  244. package/src/docker/ai-model-status.ts +10 -38
  245. package/src/docker/compose-up.test.ts +100 -0
  246. package/src/docker/compose-up.ts +292 -0
  247. package/src/docker/compose.ts +4 -14
  248. package/src/docker/derive.ts +9 -34
  249. package/src/docker/docker.test.ts +16 -261
  250. package/src/docker/images-config.test.ts +13 -25
  251. package/src/docker/index.ts +1 -24
  252. package/src/docker/recipes/index.ts +0 -23
  253. package/src/docker/recipes/pgdog.ts +6 -4
  254. package/src/docker/stack-id.test.ts +2 -2
  255. package/src/docker/stack-id.ts +0 -1
  256. package/src/docker/types.ts +2 -2
  257. package/src/drivers/ai-anthropic.ts +10 -0
  258. package/src/drivers/ai-openai-compatible.ts +34 -7
  259. package/src/drivers/ai-providers.test.ts +0 -107
  260. package/src/drivers/ai-stream.test.ts +0 -34
  261. package/src/drivers/ai-types.ts +17 -8
  262. package/src/drivers/channel-fcm.ts +6 -1
  263. package/src/drivers/channel-msegat.ts +6 -1
  264. package/src/drivers/channel-resend.ts +5 -1
  265. package/src/drivers/channel-smtp.ts +5 -1
  266. package/src/drivers/channel-sndr.ts +10 -1
  267. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  268. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  269. package/src/drivers/channel-taqnyat.ts +6 -1
  270. package/src/drivers/channel-types.ts +10 -0
  271. package/src/drivers/channel-unifonic.ts +6 -1
  272. package/src/drivers/channel-wa-cloud.ts +6 -1
  273. package/src/drivers/channel-webpush.ts +6 -1
  274. package/src/drivers/external.ts +33 -0
  275. package/src/drivers/index.ts +0 -10
  276. package/src/drivers/meilisearch.ts +6 -0
  277. package/src/drivers/oauth-types.ts +4 -0
  278. package/src/drivers/postgres.test.ts +74 -3
  279. package/src/drivers/postgres.ts +175 -6
  280. package/src/drivers/signal-types.ts +5 -5
  281. package/src/drivers/types.ts +7 -0
  282. package/src/drivers/vault-types.ts +5 -0
  283. package/src/elements/ai/declare.ts +17 -4
  284. package/src/elements/ai/eval.ts +1 -1
  285. package/src/elements/ai/mcp-http.ts +1 -1
  286. package/src/elements/ai/pii.ts +1 -1
  287. package/src/elements/ai/providers.test.ts +289 -0
  288. package/src/elements/ai/providers.ts +169 -0
  289. package/src/elements/ai/runtime.ts +68 -5
  290. package/src/elements/ai/schema.ts +2 -2
  291. package/src/elements/ai.test.ts +7 -7
  292. package/src/elements/ai.ts +17 -1
  293. package/src/elements/channel/runtime.ts +105 -12
  294. package/src/elements/clock/cron-fields.test.ts +144 -0
  295. package/src/elements/clock/cron-fields.ts +185 -0
  296. package/src/elements/clock/declare.ts +247 -3
  297. package/src/elements/clock.ts +21 -1
  298. package/src/elements/gate/declare.ts +1 -1
  299. package/src/elements/gate.ts +1 -1
  300. package/src/elements/index.ts +15 -0
  301. package/src/elements/signal/chaos-child.ts +1 -2
  302. package/src/elements/signal/declare.ts +65 -37
  303. package/src/elements/signal/delivery-modes.test.ts +11 -31
  304. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  305. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  306. package/src/elements/signal/key-ordering.test.ts +6 -26
  307. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  308. package/src/elements/signal/optional-emit.test.ts +6 -9
  309. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  310. package/src/elements/signal/orphan-messages.test.ts +4 -15
  311. package/src/elements/signal/reconcile.test.ts +2 -2
  312. package/src/elements/signal/runtime.ts +1 -1
  313. package/src/elements/signal/schema-emit.test.ts +4 -8
  314. package/src/elements/signal.test.ts +24 -49
  315. package/src/elements/signal.ts +9 -2
  316. package/src/elements/store/domain-ddl.test.ts +1 -1
  317. package/src/elements/store/live-http.test.ts +9 -4
  318. package/src/elements/store/prepare-row.test.ts +123 -0
  319. package/src/elements/store/resource.ts +43 -12
  320. package/src/elements/store/schema-decl.ts +117 -0
  321. package/src/elements/store/search-backfill.ts +205 -0
  322. package/src/elements/store/search-bind.ts +66 -0
  323. package/src/elements/store/search-bm25.ts +78 -0
  324. package/src/elements/store/search-ddl.ts +156 -0
  325. package/src/elements/store/search-embed-flow.ts +141 -0
  326. package/src/elements/store/search-errors.ts +34 -0
  327. package/src/elements/store/search-fusion.ts +112 -0
  328. package/src/elements/store/search-lsh.ts +214 -0
  329. package/src/elements/store/search-runtime.pglite.test.ts +186 -0
  330. package/src/elements/store/search-runtime.ts +314 -0
  331. package/src/elements/store/search.test.ts +226 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +52 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +2 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -144
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -114
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  484. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,78 @@
1
+ /**
2
+ * BM25F scoring — Robertson–Zaragoza with field weights applied to TF
3
+ * **before** saturation (not post-hoc score summing).
4
+ */
5
+
6
+ import { BM25_B, BM25_K1 } from "./search-errors.ts";
7
+
8
+ /** One field's contribution to a document. */
9
+ export interface Bm25FieldTerms {
10
+ readonly weight: number;
11
+ /** term → raw term frequency in this field */
12
+ readonly tf: ReadonlyMap<string, number>;
13
+ }
14
+
15
+ /**
16
+ * BM25F score for one document given query terms and corpus stats.
17
+ *
18
+ * @param queryTerms - Tokenized query (unique terms scored once)
19
+ * @param fields - Per-field TF maps with BM25F weights
20
+ * @param docLen - Total document length (sum of field token counts)
21
+ * @param avgdl - Corpus average document length
22
+ * @param N - Corpus size
23
+ * @param df - Document frequency per term
24
+ */
25
+ export function bm25fScore(
26
+ queryTerms: readonly string[],
27
+ fields: readonly Bm25FieldTerms[],
28
+ docLen: number,
29
+ avgdl: number,
30
+ N: number,
31
+ df: ReadonlyMap<string, number>,
32
+ ): number {
33
+ if (N <= 0 || avgdl <= 0) return 0;
34
+ let score = 0;
35
+ const seen = new Set<string>();
36
+ for (const term of queryTerms) {
37
+ if (seen.has(term)) continue;
38
+ seen.add(term);
39
+ let weightedTf = 0;
40
+ for (const field of fields) {
41
+ const tf = field.tf.get(term) ?? 0;
42
+ if (tf > 0) weightedTf += field.weight * tf;
43
+ }
44
+ if (weightedTf <= 0) continue;
45
+ const n = df.get(term) ?? 0;
46
+ const idf = Math.log(1 + (N - n + 0.5) / (n + 0.5));
47
+ const denom = weightedTf + BM25_K1 * (1 - BM25_B + BM25_B * (docLen / avgdl));
48
+ score += idf * ((weightedTf * (BM25_K1 + 1)) / denom);
49
+ }
50
+ return score;
51
+ }
52
+
53
+ /**
54
+ * Simple whitespace + lower-case tokenizer for BM25 (English-oriented).
55
+ * Used for in-process scoring and tests; SQL path uses to_tsvector terms.
56
+ *
57
+ * @param text - Raw text
58
+ */
59
+ export function tokenize(text: string): string[] {
60
+ return text
61
+ .toLowerCase()
62
+ .replace(/[^\p{L}\p{N}\s]+/gu, " ")
63
+ .split(/\s+/)
64
+ .filter((t) => t.length > 0);
65
+ }
66
+
67
+ /**
68
+ * Build a TF map from tokens.
69
+ *
70
+ * @param tokens - Token list
71
+ */
72
+ export function termFrequencies(tokens: readonly string[]): Map<string, number> {
73
+ const tf = new Map<string, number>();
74
+ for (const t of tokens) {
75
+ tf.set(t, (tf.get(t) ?? 0) + 1);
76
+ }
77
+ return tf;
78
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Plain-SQL DDL for built-in hybrid search (PostgreSQL 15+, zero extensions).
3
+ */
4
+
5
+ import type { DeclaredColumn } from "../../manifest/types.ts";
6
+ import { LSH_DEFAULT_K } from "./search-errors.ts";
7
+ import { generateHyperplanes, hyperplaneSeed, serializePlanes } from "./search-lsh.ts";
8
+
9
+ /** Shadow / system column and index names. */
10
+ export const OKE_TSV_COL = "__oke_tsv";
11
+ export const OKE_SEARCH_STATS = "oke_search_stats";
12
+ export const OKE_SEARCH_DF = "oke_search_df";
13
+ export const OKE_SEARCH_PLANES = "oke_search_planes";
14
+
15
+ /**
16
+ * @param sqlName - Column SQL name
17
+ */
18
+ export function embColumn(sqlName: string): string {
19
+ return `__oke_emb_${sqlName}`;
20
+ }
21
+
22
+ /**
23
+ * @param sqlName - Column SQL name
24
+ */
25
+ export function lshColumn(sqlName: string): string {
26
+ return `__oke_lsh_${sqlName}`;
27
+ }
28
+
29
+ /**
30
+ * Emit CREATE TABLE / ALTER / INDEX statements for searchable columns.
31
+ *
32
+ * @param table - SQL table name
33
+ * @param columns - Manifest columns map
34
+ */
35
+ export function searchDdlForTable(
36
+ table: string,
37
+ columns: Record<string, DeclaredColumn>,
38
+ ): string[] {
39
+ const searchable: Array<{ key: string; sqlName: string; weight: number }> = [];
40
+ const embedCols: Array<{ key: string; sqlName: string; dims: number; model?: string }> = [];
41
+ for (const [key, col] of Object.entries(columns)) {
42
+ if (!col.searchable) continue;
43
+ const sqlName = col.sqlName ?? key;
44
+ searchable.push({ key, sqlName, weight: col.searchable.weight });
45
+ if (col.embed) {
46
+ embedCols.push({
47
+ key,
48
+ sqlName,
49
+ dims: col.embed.dims,
50
+ ...(col.embed.model ? { model: col.embed.model } : {}),
51
+ });
52
+ }
53
+ }
54
+ if (searchable.length === 0) return [];
55
+
56
+ const stmts: string[] = [
57
+ `CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_STATS} (
58
+ table_name text PRIMARY KEY,
59
+ n bigint NOT NULL DEFAULT 0,
60
+ avgdl double precision NOT NULL DEFAULT 0,
61
+ updated_at timestamptz NOT NULL DEFAULT now()
62
+ )`,
63
+ `CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_DF} (
64
+ table_name text NOT NULL,
65
+ term text NOT NULL,
66
+ df bigint NOT NULL DEFAULT 0,
67
+ PRIMARY KEY (table_name, term)
68
+ )`,
69
+ `CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_PLANES} (
70
+ table_name text NOT NULL,
71
+ column_name text NOT NULL,
72
+ dims integer NOT NULL,
73
+ k integer NOT NULL,
74
+ seed text NOT NULL,
75
+ planes bytea NOT NULL,
76
+ PRIMARY KEY (table_name, column_name)
77
+ )`,
78
+ ];
79
+
80
+ // Weighted tsvector expression for GIN candidate retrieval.
81
+ const tsvParts = searchable.map(
82
+ (c) => `coalesce(to_tsvector('english', ${quoteIdent(c.sqlName)}), '')`,
83
+ );
84
+ const tsvExpr = tsvParts.join(" || ");
85
+ stmts.push(
86
+ `ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${OKE_TSV_COL} tsvector GENERATED ALWAYS AS (${tsvExpr}) STORED`,
87
+ );
88
+ stmts.push(
89
+ `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_${OKE_TSV_COL}_gin`)} ON ${quoteIdent(table)} USING gin (${OKE_TSV_COL})`,
90
+ );
91
+
92
+ for (const c of embedCols) {
93
+ stmts.push(
94
+ `ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${quoteIdent(embColumn(c.sqlName))} real[]`,
95
+ );
96
+ stmts.push(
97
+ `ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${quoteIdent(lshColumn(c.sqlName))} bigint`,
98
+ );
99
+ stmts.push(
100
+ `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_${lshColumn(c.sqlName)}_btree`)} ON ${quoteIdent(table)} (${quoteIdent(lshColumn(c.sqlName))})`,
101
+ );
102
+ }
103
+
104
+ return stmts;
105
+ }
106
+
107
+ /**
108
+ * Insert-once hyperplane rows for embed columns (never regenerate).
109
+ *
110
+ * @param table - SQL table
111
+ * @param columns - Manifest columns
112
+ */
113
+ export function ensureHyperplaneInserts(
114
+ table: string,
115
+ columns: Record<string, DeclaredColumn>,
116
+ ): Array<{ sql: string; params: unknown[] }> {
117
+ const out: Array<{ sql: string; params: unknown[] }> = [];
118
+ for (const [key, col] of Object.entries(columns)) {
119
+ if (!col.embed) continue;
120
+ const sqlName = col.sqlName ?? key;
121
+ const dims = col.embed.dims;
122
+ const k = LSH_DEFAULT_K;
123
+ const seed = hyperplaneSeed(table, sqlName, dims, k);
124
+ const planes = generateHyperplanes(seed, dims, k);
125
+ const blob = serializePlanes(planes);
126
+ const hex = blob.toString("hex");
127
+ out.push({
128
+ sql: `INSERT INTO ${OKE_SEARCH_PLANES} (table_name, column_name, dims, k, seed, planes)
129
+ VALUES (?, ?, ?, ?, ?, decode(?, 'hex'))
130
+ ON CONFLICT (table_name, column_name) DO NOTHING`,
131
+ params: [table, sqlName, dims, k, seed, hex],
132
+ });
133
+ }
134
+ return out;
135
+ }
136
+
137
+ /**
138
+ * Minimum PostgreSQL major version for hybrid search DDL.
139
+ */
140
+ export const SEARCH_PG_MIN_MAJOR = 15;
141
+
142
+ /**
143
+ * @param versionString - `SELECT version()` / `server_version` text
144
+ */
145
+ export function parsePgMajor(versionString: string): number | undefined {
146
+ const m = /(\d+)\.\d+/.exec(versionString);
147
+ if (!m) return undefined;
148
+ return Number(m[1]);
149
+ }
150
+
151
+ function quoteIdent(name: string): string {
152
+ if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
153
+ return `"${name.replaceAll('"', '""')}"`;
154
+ }
155
+ return name;
156
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * System-owned durable CDC flow that embeds `.embed()` columns asynchronously.
3
+ * Writer flows never carry `effects.embeds` — only this flow does.
4
+ */
5
+
6
+ import type { DeclaredColumn, Manifest } from "../../manifest/types.ts";
7
+ import type { Fx } from "../../kernel/fx.ts";
8
+ import { LSH_DEFAULT_K, SearchConfigError } from "./search-errors.ts";
9
+ import { embColumn, lshColumn, OKE_SEARCH_PLANES } from "./search-ddl.ts";
10
+ import { deserializePlanes, lshBucket, lshBucketToSql } from "./search-lsh.ts";
11
+
12
+ /** CDC payload shape from bindRealtimeBridge / dispatchCdc. */
13
+ export interface SearchEmbedCdcPayload {
14
+ readonly before: Record<string, unknown> | null;
15
+ readonly after: Record<string, unknown> | null;
16
+ }
17
+
18
+ /**
19
+ * System flow name for a searchable-embed table.
20
+ *
21
+ * @param table - SQL table name
22
+ */
23
+ export function searchEmbedFlowName(table: string): string {
24
+ return `_oke_search_embed_${table}`;
25
+ }
26
+
27
+ /**
28
+ * Collect tables that need the system embed CDC flow.
29
+ *
30
+ * @param manifest - Project manifest
31
+ */
32
+ export function tablesNeedingSearchEmbed(manifest: Manifest): Array<{
33
+ readonly table: string;
34
+ readonly store: string;
35
+ readonly columns: Array<{ sqlName: string; dims: number; model?: string }>;
36
+ readonly pk: string;
37
+ }> {
38
+ const out: Array<{
39
+ readonly table: string;
40
+ readonly store: string;
41
+ readonly columns: Array<{ sqlName: string; dims: number; model?: string }>;
42
+ readonly pk: string;
43
+ }> = [];
44
+ for (const [storeName, store] of Object.entries(manifest.stores ?? {})) {
45
+ if (store.facet !== "sql" || !store.tables) continue;
46
+ for (const [tableName, table] of Object.entries(store.tables)) {
47
+ const embedCols: Array<{ sqlName: string; dims: number; model?: string }> = [];
48
+ let pk = "id";
49
+ for (const [key, col] of Object.entries(table.columns ?? {})) {
50
+ if (!col || typeof col !== "object") continue;
51
+ const c = col as DeclaredColumn;
52
+ if (c.primaryKey) pk = c.sqlName ?? key;
53
+ if (c.embed) {
54
+ embedCols.push({
55
+ sqlName: c.sqlName ?? key,
56
+ dims: c.embed.dims,
57
+ ...(c.embed.model ? { model: c.embed.model } : {}),
58
+ });
59
+ }
60
+ }
61
+ if (embedCols.length > 0) {
62
+ out.push({ table: tableName, store: storeName, columns: embedCols, pk });
63
+ }
64
+ }
65
+ }
66
+ return out;
67
+ }
68
+
69
+ /**
70
+ * Run one embed step for a CDC event (call inside `fx.step`).
71
+ *
72
+ * @param fx - Flow fx
73
+ * @param table - Table name
74
+ * @param pk - Primary key SQL name
75
+ * @param columns - Embed columns
76
+ * @param payload - CDC before/after
77
+ * @param sqlRef - Resource ref for raw SQL (`sql:app`)
78
+ */
79
+ export async function applySearchEmbedCdc(
80
+ fx: Fx,
81
+ table: string,
82
+ pk: string,
83
+ columns: Array<{ sqlName: string; dims: number; model?: string }>,
84
+ payload: SearchEmbedCdcPayload,
85
+ sqlRef: `sql:${string}`,
86
+ ): Promise<void> {
87
+ const store = fx.store({
88
+ kind: "sql",
89
+ name: sqlRef.slice(4),
90
+ ref: sqlRef,
91
+ facet: "sql",
92
+ } as never);
93
+
94
+ if (!payload.after) {
95
+ // Delete — embeddings cleared with the row; nothing to do.
96
+ return;
97
+ }
98
+
99
+ const id = String(payload.after[pk] ?? "");
100
+ if (!id) return;
101
+
102
+ for (const col of columns) {
103
+ const text = String(payload.after[col.sqlName] ?? "");
104
+ const beforeText =
105
+ payload.before && typeof payload.before[col.sqlName] === "string"
106
+ ? String(payload.before[col.sqlName])
107
+ : undefined;
108
+ if (beforeText !== undefined && beforeText === text) continue;
109
+
110
+ const model = col.model ?? "default";
111
+ const vector = await fx.embed(model, text);
112
+ if (vector.length !== col.dims) {
113
+ throw new SearchConfigError(
114
+ table,
115
+ col.sqlName,
116
+ `embedding length ${vector.length} !== declared dims ${col.dims}`,
117
+ );
118
+ }
119
+
120
+ const planesRows = await store.raw(
121
+ `SELECT k, planes FROM ${OKE_SEARCH_PLANES} WHERE table_name = ? AND column_name = ?`,
122
+ [table, col.sqlName],
123
+ );
124
+ const prow = planesRows[0];
125
+ if (!prow) {
126
+ throw new SearchConfigError(
127
+ table,
128
+ col.sqlName,
129
+ "missing hyperplanes — ensure search DDL / search-backfill ran",
130
+ );
131
+ }
132
+ const k = Number(prow["k"] ?? LSH_DEFAULT_K);
133
+ const planes = deserializePlanes(Buffer.from(prow["planes"] as Buffer), k);
134
+ const bucket = lshBucket(vector, planes);
135
+
136
+ await store.raw(
137
+ `UPDATE ${table} SET ${embColumn(col.sqlName)} = ?::real[], ${lshColumn(col.sqlName)} = ? WHERE ${pk} = ?`,
138
+ [`{${Array.from(vector).join(",")}}`, lshBucketToSql(bucket), id],
139
+ );
140
+ }
141
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Fail-loud errors for built-in hybrid SQL search.
3
+ */
4
+
5
+ /** Thrown when `.embed()` / search config is invalid or AI is missing. */
6
+ export class SearchConfigError extends Error {
7
+ override readonly name = "SearchConfigError";
8
+ readonly table: string;
9
+ readonly column: string;
10
+
11
+ /**
12
+ * @param table - SQL table name
13
+ * @param column - JS or SQL column key
14
+ * @param message - Human-readable fix
15
+ */
16
+ constructor(table: string, column: string, message: string) {
17
+ super(`SearchConfigError: ${table}.${column}: ${message}`);
18
+ this.table = table;
19
+ this.column = column;
20
+ }
21
+ }
22
+
23
+ /** Corpus size below which IDF / BM25 stats are not meaningful. */
24
+ export const SEARCH_LOW_CORPUS_WARN_N = 100;
25
+
26
+ /** Default RRF damping constant (Cormack, Clarke, Büttcher — SIGIR 2009). */
27
+ export const RRF_DEFAULT_K = 60;
28
+
29
+ /** Fixed LSH hyperplane count (fits bigint bit packing). */
30
+ export const LSH_DEFAULT_K = 64;
31
+
32
+ /** BM25 saturation / length-norm defaults (Robertson–Zaragoza). */
33
+ export const BM25_K1 = 1.2;
34
+ export const BM25_B = 0.75;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Rank fusion — RRF (default) and weighted combination (opt-in).
3
+ */
4
+
5
+ import { RRF_DEFAULT_K } from "./search-errors.ts";
6
+
7
+ /** One ranked list entry (1-based rank preferred for RRF). */
8
+ export interface RankedHit {
9
+ readonly id: string;
10
+ readonly score: number;
11
+ readonly rank: number;
12
+ }
13
+
14
+ export type FuseStrategy = "rrf" | "weighted";
15
+
16
+ export interface FuseOptions {
17
+ readonly strategy?: FuseStrategy;
18
+ /** RRF damping constant — default {@link RRF_DEFAULT_K} (Cormack et al. 2009). */
19
+ readonly k?: number;
20
+ /** Weighted fusion only. */
21
+ readonly weights?: { readonly bm25?: number; readonly vector?: number };
22
+ }
23
+
24
+ /**
25
+ * Reciprocal Rank Fusion: Σ 1/(k + rank).
26
+ *
27
+ * @param lists - Named ranked lists (e.g. bm25, vector)
28
+ * @param k - Damping constant
29
+ */
30
+ export function fuseRrf(
31
+ lists: ReadonlyMap<string, readonly RankedHit[]>,
32
+ k: number = RRF_DEFAULT_K,
33
+ ): RankedHit[] {
34
+ const scores = new Map<string, number>();
35
+ for (const list of lists.values()) {
36
+ for (const hit of list) {
37
+ const contrib = 1 / (k + hit.rank);
38
+ scores.set(hit.id, (scores.get(hit.id) ?? 0) + contrib);
39
+ }
40
+ }
41
+ return toRanked(scores);
42
+ }
43
+
44
+ /**
45
+ * Weighted linear combination of raw scores (min-max normalized per list).
46
+ *
47
+ * @param lists - Named scored lists
48
+ * @param weights - Per-engine weights
49
+ */
50
+ export function fuseWeighted(
51
+ lists: ReadonlyMap<string, readonly RankedHit[]>,
52
+ weights: { readonly bm25?: number; readonly vector?: number } = {},
53
+ ): RankedHit[] {
54
+ const wBm25 = weights.bm25 ?? 0.5;
55
+ const wVec = weights.vector ?? 0.5;
56
+ const norm = normalizeLists(lists);
57
+ const scores = new Map<string, number>();
58
+ for (const [name, list] of norm) {
59
+ const w = name === "vector" ? wVec : name === "bm25" ? wBm25 : 1;
60
+ for (const hit of list) {
61
+ scores.set(hit.id, (scores.get(hit.id) ?? 0) + w * hit.score);
62
+ }
63
+ }
64
+ return toRanked(scores);
65
+ }
66
+
67
+ /**
68
+ * Apply {@link FuseOptions} — omitted / rrf → RRF with k=60.
69
+ *
70
+ * @param lists - Ranked lists
71
+ * @param fuse - Options
72
+ */
73
+ export function fuseLists(
74
+ lists: ReadonlyMap<string, readonly RankedHit[]>,
75
+ fuse?: FuseOptions,
76
+ ): { hits: RankedHit[]; strategy: FuseStrategy; k?: number } {
77
+ const strategy = fuse?.strategy ?? "rrf";
78
+ if (strategy === "weighted") {
79
+ return { hits: fuseWeighted(lists, fuse?.weights), strategy };
80
+ }
81
+ const k = fuse?.k ?? RRF_DEFAULT_K;
82
+ return { hits: fuseRrf(lists, k), strategy, k };
83
+ }
84
+
85
+ function normalizeLists(
86
+ lists: ReadonlyMap<string, readonly RankedHit[]>,
87
+ ): Map<string, RankedHit[]> {
88
+ const out = new Map<string, RankedHit[]>();
89
+ for (const [name, list] of lists) {
90
+ let min = Infinity;
91
+ let max = -Infinity;
92
+ for (const h of list) {
93
+ if (h.score < min) min = h.score;
94
+ if (h.score > max) max = h.score;
95
+ }
96
+ const span = max - min || 1;
97
+ out.set(
98
+ name,
99
+ list.map((h) => ({
100
+ id: h.id,
101
+ rank: h.rank,
102
+ score: (h.score - min) / span,
103
+ })),
104
+ );
105
+ }
106
+ return out;
107
+ }
108
+
109
+ function toRanked(scores: Map<string, number>): RankedHit[] {
110
+ const sorted = [...scores.entries()].sort((a, b) => b[1]! - a[1]!);
111
+ return sorted.map(([id, score], i) => ({ id, score, rank: i + 1 }));
112
+ }