okengine 0.18.4 → 0.19.0

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/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -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 +50 -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 +1 -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 -132
  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 -162
  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-De7Lc2JC.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-RGy7VEA_.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-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.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-4rHOePuE.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-DISPgxLM.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,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
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Random-hyperplane LSH — K fixed planes generated once per field lifetime.
3
+ */
4
+
5
+ import { createHash } from "node:crypto";
6
+ import { LSH_DEFAULT_K } from "./search-errors.ts";
7
+
8
+ /**
9
+ * Stable seed string for a field's hyperplanes.
10
+ *
11
+ * @param table - SQL table
12
+ * @param column - SQL column
13
+ * @param dims - Embedding dimensionality
14
+ * @param k - Number of planes
15
+ */
16
+ export function hyperplaneSeed(table: string, column: string, dims: number, k: number): string {
17
+ return `${table}|${column}|${dims}|${k}`;
18
+ }
19
+
20
+ /**
21
+ * Generate K unit-ish random hyperplanes in `dims` dimensions from a seed.
22
+ * Deterministic across hosts for the same seed.
23
+ *
24
+ * @param seed - Stable seed string
25
+ * @param dims - Vector dimensionality
26
+ * @param k - Plane count (default {@link LSH_DEFAULT_K})
27
+ */
28
+ export function generateHyperplanes(
29
+ seed: string,
30
+ dims: number,
31
+ k: number = LSH_DEFAULT_K,
32
+ ): Float32Array[] {
33
+ const planes: Float32Array[] = [];
34
+ let counter = 0;
35
+ for (let i = 0; i < k; i++) {
36
+ const plane = new Float32Array(dims);
37
+ let filled = 0;
38
+ while (filled < dims) {
39
+ const h = createHash("sha256").update(`${seed}\0${counter++}`).digest();
40
+ for (let b = 0; b + 3 < h.length && filled < dims; b += 4) {
41
+ const u = h.readUInt32BE(b) / 0xffff_ffff;
42
+ plane[filled++] = u * 2 - 1;
43
+ }
44
+ }
45
+ // L2 normalize
46
+ let norm = 0;
47
+ for (let j = 0; j < dims; j++) norm += plane[j]! * plane[j]!;
48
+ norm = Math.sqrt(norm) || 1;
49
+ for (let j = 0; j < dims; j++) plane[j]! /= norm;
50
+ planes.push(plane);
51
+ }
52
+ return planes;
53
+ }
54
+
55
+ /**
56
+ * Pack LSH bucket bits into a bigint (K ≤ 64).
57
+ *
58
+ * @param vector - Embedding
59
+ * @param planes - Fixed hyperplanes
60
+ */
61
+ export function lshBucket(vector: readonly number[], planes: readonly Float32Array[]): bigint {
62
+ if (planes.length > 64) {
63
+ throw new Error(`lshBucket: K=${planes.length} exceeds bigint packing (max 64)`);
64
+ }
65
+ let bits = 0n;
66
+ for (let i = 0; i < planes.length; i++) {
67
+ const plane = planes[i]!;
68
+ if (vector.length !== plane.length) {
69
+ throw new Error(`lshBucket: vector length ${vector.length} !== plane dims ${plane.length}`);
70
+ }
71
+ let dot = 0;
72
+ for (let j = 0; j < plane.length; j++) {
73
+ dot += vector[j]! * plane[j]!;
74
+ }
75
+ if (dot >= 0) bits |= 1n << BigInt(i);
76
+ }
77
+ return bits;
78
+ }
79
+
80
+ /**
81
+ * Encode an LSH bucket for PostgreSQL `bigint` (signed int64).
82
+ * K=64 bit patterns with the high bit set exceed unsigned range when passed
83
+ * as a decimal string — reinterpret as two's-complement signed.
84
+ *
85
+ * @param bucket - Unsigned bit pack from {@link lshBucket}
86
+ */
87
+ export function lshBucketToSql(bucket: bigint): string {
88
+ const masked = bucket & 0xffff_ffff_ffff_ffffn;
89
+ if (masked >= 0x8000_0000_0000_0000n) {
90
+ return (masked - 0x1_0000_0000_0000_0000n).toString();
91
+ }
92
+ return masked.toString();
93
+ }
94
+
95
+ /**
96
+ * Decode a PostgreSQL `bigint` LSH value back to an unsigned bit pack.
97
+ *
98
+ * @param value - Driver value (string | number | bigint)
99
+ */
100
+ export function lshBucketFromSql(value: unknown): bigint {
101
+ const n = typeof value === "bigint" ? value : BigInt(String(value));
102
+ return n < 0n ? n + 0x1_0000_0000_0000_0000n : n;
103
+ }
104
+
105
+ /**
106
+ * Cosine similarity in [−1, 1].
107
+ *
108
+ * @param a - Vector a
109
+ * @param b - Vector b
110
+ */
111
+ export function cosineSimilarity(a: readonly number[], b: readonly number[]): number {
112
+ if (a.length !== b.length) {
113
+ throw new Error(`cosineSimilarity: length mismatch ${a.length} vs ${b.length}`);
114
+ }
115
+ let dot = 0;
116
+ let na = 0;
117
+ let nb = 0;
118
+ for (let i = 0; i < a.length; i++) {
119
+ dot += a[i]! * b[i]!;
120
+ na += a[i]! * a[i]!;
121
+ nb += b[i]! * b[i]!;
122
+ }
123
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
124
+ return denom === 0 ? 0 : dot / denom;
125
+ }
126
+
127
+ /**
128
+ * Candidate buckets: exact match plus Hamming distance 1 flips.
129
+ *
130
+ * @param bucket - Query bucket
131
+ * @param k - Bit width
132
+ */
133
+ export function neighborBuckets(bucket: bigint, k: number): bigint[] {
134
+ const out: bigint[] = [bucket];
135
+ for (let i = 0; i < k; i++) {
136
+ out.push(bucket ^ (1n << BigInt(i)));
137
+ }
138
+ return out;
139
+ }
140
+
141
+ /**
142
+ * Serialize planes for `oke_search_planes.planes` (Float32 little-endian).
143
+ *
144
+ * @param planes - Hyperplanes
145
+ */
146
+ export function serializePlanes(planes: readonly Float32Array[]): Buffer {
147
+ const dims = planes[0]?.length ?? 0;
148
+ const buf = Buffer.allocUnsafe(4 + planes.length * dims * 4);
149
+ buf.writeUInt32LE(dims, 0);
150
+ let offset = 4;
151
+ for (const plane of planes) {
152
+ for (let i = 0; i < dims; i++) {
153
+ buf.writeFloatLE(plane[i]!, offset);
154
+ offset += 4;
155
+ }
156
+ }
157
+ return buf;
158
+ }
159
+
160
+ /**
161
+ * Deserialize planes from storage.
162
+ *
163
+ * @param buf - Stored bytes
164
+ * @param k - Expected plane count
165
+ */
166
+ export function deserializePlanes(buf: Buffer, k: number): Float32Array[] {
167
+ const dims = buf.readUInt32LE(0);
168
+ const planes: Float32Array[] = [];
169
+ let offset = 4;
170
+ for (let i = 0; i < k; i++) {
171
+ const plane = new Float32Array(dims);
172
+ for (let j = 0; j < dims; j++) {
173
+ plane[j] = buf.readFloatLE(offset);
174
+ offset += 4;
175
+ }
176
+ planes.push(plane);
177
+ }
178
+ return planes;
179
+ }