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,591 @@
1
+ ---
2
+ title: "Live"
3
+ description: "Retained live event tapes streamed to clients over HTTP SSE with Last-Event-ID resume."
4
+ icon: "Activity"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ `signal.live` keeps a retained event tape and exposes it over Server-Sent Events. Emit with
9
+ `fx.emit`, and mount the feed with `http.live` (or a gated GET).
10
+
11
+ For developers shipping status feeds and progress UIs — declare the Signal, expose SSE, subscribe
12
+ from the typed client.
13
+
14
+ <Callout title="The one rule">
15
+ `signal.live` is an HTTP SSE tape — not a competing worker. Bind with
16
+ [`http.live`](/docs/elements/flow/http#live-streams). Do not use `on(liveSignal, flow)` as a
17
+ queue consumer. Prefer `{ optional: true }` so emit succeeds when no client is connected yet.
18
+ </Callout>
19
+
20
+ <SignalLiveReplay />
21
+
22
+ ## Smallest Example
23
+
24
+ <Steps>
25
+
26
+ <Step>
27
+ ### Declare the live signal
28
+
29
+ ```typescript title="src/signals/orders.ts"
30
+ import { signal } from "okengine";
31
+ import { z } from "zod";
32
+
33
+ export const orderStatus = signal.live("order-status", {
34
+ optional: true,
35
+ schema: z.object({
36
+ orderId: z.string(),
37
+ status: z.enum(["placed", "fulfilling", "shipped"]),
38
+ }),
39
+ });
40
+ ```
41
+
42
+ </Step>
43
+
44
+ <Step>
45
+ ### Expose SSE
46
+
47
+ ```typescript title="src/flows/orders/firehose.ts"
48
+ import { on, http } from "okengine";
49
+ import { member } from "@/core/gate";
50
+ import { orderStatus } from "@/signals/orders";
51
+
52
+ export const firehose = on(http.live(orderStatus).gate(member));
53
+ ```
54
+
55
+ </Step>
56
+
57
+ <Step>
58
+ ### Emit and subscribe
59
+
60
+ ```typescript
61
+ // Inside any Flow:
62
+ await fx.emit(orderStatus, { orderId: "ord_1", status: "shipped" });
63
+ ```
64
+
65
+ ```bash
66
+ curl -N http://localhost:6530/_oke/live/order-status \
67
+ -H "accept: text/event-stream" \
68
+ -H "authorization: Bearer …"
69
+ ```
70
+
71
+ Response `Content-Type` is `text/event-stream`. Frames are JSON `data:` lines
72
+ (optional `id:` for resume), then `data: [DONE]`.
73
+
74
+ </Step>
75
+
76
+ </Steps>
77
+
78
+ <Callout title="Pathless firehose">
79
+ `on(http.live(signal))` always mounts `GET /_oke/live/{name}` — there is no pathless file-tree
80
+ stamp for live. Custom paths use `http.get(path).live(signal)`. See [Exposure](#exposure).
81
+ </Callout>
82
+
83
+ ## Progressive Patterns
84
+
85
+ From a default firehose to filtered paths, retention, and the typed client:
86
+
87
+ <Tabs items={["Firehose", "Filtered", "Retention", "Client"]}>
88
+
89
+ <Tab value="Firehose">
90
+
91
+ `on(http.live(signal))` mounts `GET /_oke/live/{name}`. Chain `.gate(...)` like any GET:
92
+
93
+ ```typescript title="src/flows/orders/firehose.ts"
94
+ import { on, http } from "okengine";
95
+ import { member } from "@/core/gate";
96
+ import { orderStatus } from "@/signals/orders";
97
+
98
+ export const firehose = on(http.live(orderStatus).gate(member));
99
+ ```
100
+
101
+ Signal names in the path are `encodeURIComponent`'d (`chat.message` stays readable).
102
+
103
+ </Tab>
104
+
105
+ <Tab value="Filtered">
106
+
107
+ Path params become a filter: an event forwards when each `:param` that **exists on the payload**
108
+ equals the request value. Params missing from the payload are skipped:
109
+
110
+ ```typescript title="src/flows/orders/events.ts"
111
+ import { on, http } from "okengine";
112
+ import { member } from "@/core/gate";
113
+ import { orderStatus } from "@/signals/orders";
114
+
115
+ export const events = on(http.get("/orders/:orderId/events").gate(member).live(orderStatus));
116
+ ```
117
+
118
+ `GET /orders/ord_1/events` receives `{ orderId: "ord_1", status: "shipped" }` and drops other
119
+ orders.
120
+
121
+ </Tab>
122
+
123
+ <Tab value="Retention">
124
+
125
+ Cap the tape with `retention` (live-only). Omit for unbounded history:
126
+
127
+ ```typescript title="src/signals/chat.ts"
128
+ import { signal } from "okengine";
129
+ import { z } from "zod";
130
+
131
+ export const chatMessage = signal.live("chat.message", {
132
+ optional: true,
133
+ retention: { maxAge: "7d", maxCount: 10_000 },
134
+ schema: z.object({
135
+ room: z.string(),
136
+ text: z.string(),
137
+ author: z.string(),
138
+ }),
139
+ });
140
+ ```
141
+
142
+ Invalid `maxAge` / `maxCount` throw at declare — see [Retention](#retention).
143
+
144
+ </Tab>
145
+
146
+ <Tab value="Client">
147
+
148
+ Browsers use the typed client callback — not raw `EventSource` on an invented path:
149
+
150
+ ```typescript
151
+ const stop = api.live(
152
+ orderStatus,
153
+ { orderId: "ord_1" },
154
+ {
155
+ onEvent: (event) => {
156
+ /* { orderId, status } */
157
+ },
158
+ onError: (err) => {
159
+ /* 4xx, envelope, or drop */
160
+ },
161
+ autoResubscribe: false,
162
+ },
163
+ );
164
+ stop();
165
+ ```
166
+
167
+ Reconnects send `Last-Event-ID` from the last `id:` received. See
168
+ [Client subscription](#client-subscription).
169
+
170
+ </Tab>
171
+
172
+ </Tabs>
173
+
174
+ ## Options Reference
175
+
176
+ Optional second argument to `signal.live(name, options?)`. Delivery is the helper name — not an
177
+ option.
178
+
179
+ | Option | Type | Default | Meaning |
180
+ | ------------- | ------------------------ | --------- | -------------------------------------------------------- |
181
+ | `schema` | Standard Schema | omitted | **Emit** contract — validated at `fx.emit` (**OKE1250**) |
182
+ | `optional` | `boolean` | `false` | Allow emit with zero SSE clients / subscribers |
183
+ | `retention` | `{ maxAge?, maxCount? }` | unbounded | Prune the tape (live-only) |
184
+ | `description` | `string` | the name | Console / docs blurb |
185
+ | `retries` | `number` | `3` | Declared; live uses the tape, not once DLQ |
186
+ | `deadLetter` | `boolean` | `true` | Declared; live does not use once DLQ |
187
+
188
+ **Consequence:** `retention` on `signal.once` / `signal.broadcast` is a type error — switch to
189
+ `signal.live` or drop the option.
190
+
191
+ ## Exposure
192
+
193
+ <Callout title="Detailed section">
194
+ If you only need the default firehose, jump to the example below. `.live(…)` is GET-only —
195
+ `on(http.post("/x").live(signal))` throws `on(http.*.live(signal)): live exposure must be GET`.
196
+ </Callout>
197
+
198
+ `http.live(signal)` is one-arg `on()` — the engine synthesizes the stream Flow
199
+ (`fx.live` + `effects.reads: ["signal:<name>"]`). Chain `.gate(...)` like any GET.
200
+
201
+ ```typescript title="src/flows/orders/firehose.ts"
202
+ import { on, http, signal } from "okengine";
203
+ import { z } from "zod";
204
+ import { member } from "@/core/gate";
205
+
206
+ export const orderStatus = signal.live("order-status", {
207
+ optional: true,
208
+ schema: z.object({
209
+ orderId: z.string(),
210
+ status: z.enum(["placed", "fulfilling", "shipped"]),
211
+ }),
212
+ });
213
+
214
+ export const firehose = on(http.live(orderStatus).gate(member));
215
+ ```
216
+
217
+ ```bash
218
+ curl -N http://localhost:6530/_oke/live/order-status \
219
+ -H "accept: text/event-stream" \
220
+ -H "authorization: Bearer …"
221
+ ```
222
+
223
+ Three GET shapes expose a live SSE body. Pick the physics first, then the path.
224
+
225
+ | Declaration | Path | Physics |
226
+ | -------------------------------------------------- | ----------------------- | ------------------------------------- |
227
+ | `on(http.live(signal))` | `GET /_oke/live/{name}` | Signal tape — every event |
228
+ | `on(http.get(path).live(signal))` | Your path | Signal tape — auto-match on `:params` |
229
+ | `on(http.get(path).live(table), flow)` | Your path | Live **query** — classified CDC rows |
230
+ | `store.resource({ live: true })` + `http.resource` | `GET <path>/live` | Same live-query physics |
231
+
232
+ Signal firehoses and resource live queries are different physics — see Live Queries below.
233
+
234
+ <Accordions>
235
+
236
+ <Accordion title="Filtered Paths">
237
+ Path params become a filter: an event is forwarded when each `:param` that
238
+ **exists on the payload** equals the request value. Params missing from the
239
+ payload are skipped (the event still flows). No params = firehose.
240
+
241
+ ```typescript title="src/flows/orders/events.ts"
242
+ import { on, http } from "okengine";
243
+ import { member } from "@/core/gate";
244
+ import { orderStatus } from "@/signals/orders";
245
+
246
+ export const events = on(http.get("/orders/:orderId/events").gate(member).live(orderStatus));
247
+ ```
248
+
249
+ `GET /orders/ord_1/events` receives `{ orderId: "ord_1", status: "shipped" }`
250
+ and drops events for other orders.
251
+
252
+ </Accordion>
253
+
254
+ <Accordion title="Custom Match">
255
+ Pass your own Flow as the second argument to `on()` when auto-match is not
256
+ enough. Return `fx.live(signal, { match })` from `do` — do not wrap it with
257
+ `fx.json.stream`.
258
+
259
+ ```typescript title="src/flows/orders/vip-feed.ts"
260
+ import { on, flow, http } from "okengine";
261
+ import { member } from "@/core/gate";
262
+ import { orderStatus } from "@/signals/orders";
263
+
264
+ export const vipFeed = on(
265
+ http.get("/orders/vip/events").gate(member).live(orderStatus),
266
+ flow({
267
+ do: (_input, fx) =>
268
+ fx.live(orderStatus, {
269
+ match: (payload) => payload.status === "shipped",
270
+ }),
271
+ }),
272
+ );
273
+ ```
274
+
275
+ **Consequence:** a custom Flow stamps a distinct match key, so it can coexist
276
+ with the auto-match route for the same signal (different path). Two synthesized
277
+ firehoses that share signal **and** gates fail uniqueness — see Uniqueness.
278
+
279
+ </Accordion>
280
+
281
+ <Accordion title="Live Queries">
282
+ Resource / table live is **not** a signal tape. Each subscriber gets classified
283
+ row events (RLS + list filters). Prefer `http.resource` + `{ live: true }` when
284
+ you already mount the five CRUD ops.
285
+
286
+ | `live` on the resource | Result |
287
+ | ---------------------- | ---------------------------------------------- |
288
+ | `{ live: true }` | Mount `GET <path>/live` now |
289
+ | omitted | Mount only if `oke({ store: { live: true } })` |
290
+ | `{ live: false }` | Never mount live for this resource |
291
+
292
+ Wire events (consumed with `useLiveQuery` on the [typed client](/docs/client/react)):
293
+
294
+ | `kind` | Meaning |
295
+ | --------- | ----------------------------------------------------------- |
296
+ | `upsert` | Row visible under stamp + query — merge by primary key |
297
+ | `revoked` | Row left visibility (`reason: "rls"` or `"query"`) — remove |
298
+ | `delete` | Row deleted — remove |
299
+
300
+ For a handwritten list, bind the table on GET and open the window with
301
+ `liveQuery` — full detail under [HTTP · Live Streams](/docs/elements/flow/http#live-streams).
302
+
303
+ </Accordion>
304
+
305
+ <Accordion title="Uniqueness">
306
+ Boot keys each live HTTP route as `(signal, gates, match)`. Match is the
307
+ sorted path-param names, or `custom:<flow>` when you passed a Flow, or
308
+ `(firehose)` when there are no params.
309
+
310
+ | Pair | Boots? |
311
+ | --------------------------------------- | ----------------------------------------- |
312
+ | Member `:orderId` + admin firehose | Yes — gates and match differ |
313
+ | Same params, different gates | Yes — the client disambiguates with `via` |
314
+ | Two member firehoses on different paths | No — **OKE1050** |
315
+ | Same method + path twice | No — **OKE1041** first |
316
+
317
+ **OKE1050** cause: `Live signal "{signal}" is exposed twice with the same gates ({gates}) and match ({match}).`
318
+ Fix: a different gate, a path-param filter, or drop the extra route.
319
+
320
+ </Accordion>
321
+
322
+ </Accordions>
323
+
324
+ ## Emit through fx
325
+
326
+ <Callout title="Detailed section">
327
+ If you only need `fx.emit(signal, payload)`, jump to the table. Emit appends to the retained tape
328
+ when the call resolves. The producer run id is stamped as `parentRunId` for Console trace chains.
329
+ </Callout>
330
+
331
+ | Call | Records | Use |
332
+ | ----------------------------- | ----------------------- | ------------------------------- |
333
+ | `fx.emit(signal, payload?)` | `emits` | Append to the live tape |
334
+ | `fx.live(signal, { match? })` | `reads` `signal:<name>` | Server SSE body for a live tape |
335
+
336
+ Invalid `schema` payloads fail at emit with **OKE1250** (`"{resource}": {detail}`) before any
337
+ client receives the frame. Cross-signal `fx.live` without a declared read throws **OKE1001**.
338
+
339
+ ```typescript title="src/flows/orders/[id]/ship.ts"
340
+ import { on, flow, http } from "okengine";
341
+ import { z } from "zod";
342
+ import { orderStatus } from "@/signals/orders";
343
+
344
+ export const ship = on(
345
+ http.post({
346
+ in: z.object({ id: z.string() }),
347
+ }),
348
+ flow({
349
+ do: async ({ id }, fx) => {
350
+ await fx.emit(orderStatus, { orderId: id, status: "shipped" });
351
+ return { id, status: "shipped" };
352
+ },
353
+ }),
354
+ );
355
+ ```
356
+
357
+ Zero SSE clients + `optional: false` → **OKE1240**. Cause:
358
+ `Flow "{flow}" emits signal "{resource}" with no subscriber.`
359
+ Fix: mount `http.live` (or a path `.live`) before emitting, or set `{ optional: true }`.
360
+
361
+ ## Retention
362
+
363
+ <Callout title="Detailed section">
364
+ If you only need an unbounded tape, skip this section. `retention` is live-only — omit both fields
365
+ (or omit `retention`) for unlimited history.
366
+ </Callout>
367
+
368
+ | Field | Type | Meaning |
369
+ | ---------- | ------------------------------------- | ----------------------------- |
370
+ | `maxAge` | duration string (`"24h"`, `"30s"`, …) | Drop events older than this |
371
+ | `maxCount` | integer ≥ 1 | Keep only the newest N events |
372
+
373
+ ```typescript title="src/signals/chat.ts"
374
+ import { signal } from "okengine";
375
+ import { z } from "zod";
376
+
377
+ export const chatMessage = signal.live("chat.message", {
378
+ optional: true,
379
+ retention: { maxAge: "7d", maxCount: 10_000 },
380
+ schema: z.object({
381
+ room: z.string(),
382
+ text: z.string(),
383
+ author: z.string(),
384
+ }),
385
+ });
386
+ ```
387
+
388
+ Invalid values throw at declare:
389
+
390
+ ```text
391
+ signal.live("…"): retention.maxAge must be a duration like "24h" or "30s"
392
+ signal.live("…"): retention.maxCount must be an integer ≥ 1
393
+ ```
394
+
395
+ **Consequence:** a pruned `id:` becomes a resume gap — reconnects that still send that
396
+ `Last-Event-ID` hit **OKE1210** / 410 `LiveResumeGap`. Prefer `autoResubscribe: true` on flaky
397
+ networks, or raise `maxCount` / `maxAge` if clients need longer catch-up.
398
+
399
+ ## Resume and gaps
400
+
401
+ <Callout title="Detailed section">
402
+ Resume is exclusive: replay events **after** `Last-Event-ID`, then continue live. Unknown or
403
+ pruned ids throw **OKE1210** before the SSE body — HTTP maps that to **410** `LiveResumeGap`.
404
+ </Callout>
405
+
406
+ | Symptom | Meaning | Fix |
407
+ | ----------------------------------- | ------------------------------- | -------------------------------------- |
408
+ | **OKE1210** / 410 `LiveResumeGap` | Cursor gone from the tape | Drop `Last-Event-ID`; replay remaining |
409
+ | `autoResubscribe: true` | Client clears gap after backoff | Prefer for flaky networks |
410
+ | Custom `fx.live(signal, { match })` | Server-side filter | Do not wrap with `fx.json.stream` |
411
+
412
+ ```text
413
+ Cursor "{afterId}" missing on "{signal}".
414
+ ```
415
+
416
+ SSE frames carry optional `id:` lines. Clients that reconnect send
417
+ `Last-Event-ID` from the last `id:` they actually received. A **410** means that cursor is
418
+ gone — drop it and replay the remaining tape.
419
+
420
+ ## Client subscription
421
+
422
+ <Callout title="Detailed section">
423
+ `signal.live` is HTTP SSE. `for await` stays on the server; the browser uses a callback. Prefer
424
+ `api.live` or `useLive` — not raw `EventSource` on an invented path.
425
+ </Callout>
426
+
427
+ The client picks the unique exposure whose `matchKey` fields are a subset of the input,
428
+ preferring the largest match (`{ orderId }` beats firehose). A tie needs `via: "unit.flow"`.
429
+
430
+ ```typescript
431
+ const stop = api.live(
432
+ orderStatus,
433
+ { orderId: "ord_1" },
434
+ {
435
+ onEvent: (event) => {
436
+ /* { orderId, status } */
437
+ },
438
+ onError: (err) => {
439
+ /* 4xx, envelope, or drop */
440
+ },
441
+ onOpen: () => {
442
+ /* HTTP 200, including reconnects */
443
+ },
444
+ autoResubscribe: false,
445
+ },
446
+ );
447
+ stop();
448
+ ```
449
+
450
+ `api.orders.events({ orderId }, { onEvent })` is the same shape on the exposing Flow.
451
+ Reconnects send `Last-Event-ID` from the last `id:` received.
452
+
453
+ | Option | Default | Meaning |
454
+ | ----------------- | ------------ | --------------------------------------------------- |
455
+ | `onEvent` | _(required)_ | Each JSON frame from the tape |
456
+ | `onError` | omitted | 4xx, envelope errors, or network drop |
457
+ | `onOpen` | omitted | After a successful SSE open (incl. reconnects) |
458
+ | `autoResubscribe` | `false` | Re-open after a drop (500ms…30s backoff) |
459
+ | `via` | omitted | Disambiguate when two exposures share a match shape |
460
+ | `signal` | omitted | `AbortSignal` to cancel the subscribe |
461
+
462
+ React:
463
+
464
+ ```typescript
465
+ import { useLive } from "okengine/client-react";
466
+
467
+ const { events, latest, error, isConnected } = useLive(
468
+ api,
469
+ orderStatus,
470
+ { orderId: "ord_1" },
471
+ { autoResubscribe: true },
472
+ );
473
+ ```
474
+
475
+ Resource live queries use `useLiveQuery` (snapshot + classified events), not `api.live`.
476
+ See [Client · Live](/docs/client/live).
477
+
478
+ ## What live is not
479
+
480
+ <Callout title="Detailed section">
481
+ Pick physics from the guarantee you need. Live is the wrong tool when work must be claimed once,
482
+ or when every in-process listener should react with no retained history.
483
+ </Callout>
484
+
485
+ | Need | Use instead |
486
+ | -------------------------------------- | --------------------------------------------------------------------------- |
487
+ | Exactly one worker processes the job | [`once`](/docs/elements/signal/once) |
488
+ | Every active Flow gets a copy, no tape | [`broadcast`](/docs/elements/signal/broadcast) |
489
+ | Classified CDC rows for a list window | [`http.resource` live](/docs/elements/flow/http#resources) / `useLiveQuery` |
490
+ | Durable multi-step work with journal | [Durable Workflows](/docs/elements/flow/workflows) |
491
+
492
+ Live does **not** use the `once` visibility lease or dead-letter queue. `retries` /
493
+ `deadLetter` may appear on the declare options bag, but the live path is the retained tape +
494
+ SSE resume — not competing-consumer physics.
495
+
496
+ ## Troubleshooting
497
+
498
+ <Accordions>
499
+
500
+ <Accordion title="404 on /_oke/live/…">
501
+ Confirm `on(http.live(signal))` (or a path `.live(signal)`) is adopted. Names in the default path
502
+ are URI-encoded (`encodeURIComponent`). A bare `404` with body `Not Found` means the router found
503
+ no match.
504
+ </Accordion>
505
+
506
+ <Accordion title="TypeError: live exposure must be GET">
507
+ `.live(signal)` only attaches to `http.get` / `http.live`. Other verbs reject live synthesis:
508
+ `on(http.*.live(signal)): live exposure must be GET`.
509
+ </Accordion>
510
+
511
+ <Accordion title="OKE1050 — live signal exposed twice">
512
+ Cause: `Live signal "{signal}" is exposed twice with the same gates ({gates}) and match ({match}
513
+ ).` Two firehoses (`http.live` or param-less `.live`) that share the signal and gates cannot boot.
514
+ Change the gate, add a path-param filter, or remove a route.
515
+ </Accordion>
516
+
517
+ <Accordion title="OKE1041 — method + path bound twice">
518
+ Cause: `{method} {path} is bound twice (flow "{flow}").` Two mounts collide on the same method +
519
+ path (for example two `http.live` firehoses that resolve to the same URL). Drop one binding.
520
+ </Accordion>
521
+
522
+ <Accordion title="OKE1210 — 410 LiveResumeGap">
523
+ Cause: `Cursor "{afterId}" missing on "{signal}".` That `Last-Event-ID` was pruned or never
524
+ existed. Reconnect without it; remaining events replay. `autoResubscribe: true` does this after
525
+ backoff.
526
+ </Accordion>
527
+
528
+ <Accordion title="OKE1240 when emitting live">
529
+ No subscriber / exposure counted. Set `{ optional: true }` (usual for firehoses) or mount
530
+ `http.live` before emitting in tests.
531
+ </Accordion>
532
+
533
+ <Accordion title="OKE1250 on emit">
534
+ Cause: `"{resource}": {detail}` from the Standard Schema issues. Align the payload with `schema` —
535
+ no client receives a frame.
536
+ </Accordion>
537
+
538
+ <Accordion title="Bound on(liveSignal, flow) as a worker">
539
+ Live is not competing-consumer physics. Use `signal.once` for workers, or expose SSE with
540
+ `http.live`.
541
+ </Accordion>
542
+
543
+ <Accordion title="Raw EventSource on /api/signals/…">
544
+ That path is not the engine firehose. Use `GET /_oke/live/{name}`, a gated `.live` route, or
545
+ `api.live` / `useLive` from the typed client.
546
+ </Accordion>
547
+
548
+ <Accordion title="Multiple live exposures (client)">
549
+ Two routes share the same match shape. Pass `via: "unit.flow"` or call the exposing Flow
550
+ (`api.orders.events({orderId}, {onEvent})`) instead of root `api.live`.
551
+ </Accordion>
552
+
553
+ <Accordion title="TypeError: retention is only valid with signal.live">
554
+ `retention: { maxAge, maxCount }` is live-only. Drop it on queue / pub-sub Signals, or switch
555
+ to `signal.live`.
556
+ </Accordion>
557
+
558
+ <Accordion title="live query requires a primary key / RLS driver">
559
+ Extract: `live: true on table "…" requires a primary key column`. Runtime needs an RLS-capable SQL
560
+ driver (`postgres` / `pglite`) and a gated identity. That path is resource live — not
561
+ `signal.live`. Attach `.gate(...)` and declare a PK.
562
+ </Accordion>
563
+
564
+ </Accordions>
565
+
566
+ ## Learn more
567
+
568
+ - [HTTP · Live Streams](/docs/elements/flow/http#live-streams) — exposure, uniqueness, match
569
+ - [Signal Overview](/docs/elements/signal) — delivery matrix and drivers
570
+ - [Once](/docs/elements/signal/once) — when you need competing workers instead
571
+ - [Broadcast](/docs/elements/signal/broadcast) — ephemeral fan-out without a tape
572
+ - [Client](/docs/client/live) — `api.live`, `useLive`, `useLiveQuery`
573
+ - [fx](/docs/reference/fx) — `fx.emit`, `fx.live`
574
+ - [Gate](/docs/elements/gate) — `.gate(...)` / `.public()` on triggers
575
+ - [Errors](/docs/reference/errors) — OKE1050 · OKE1210 · OKE1240 · OKE1250
576
+
577
+ ## Next
578
+
579
+ <Cards>
580
+ <Card
581
+ title="HTTP · Live Streams"
582
+ description="Firehose paths, filters, uniqueness, and client subscribe."
583
+ href="/docs/elements/flow/http#live-streams"
584
+ />
585
+ <Card title="Client" description="api.live and useLive for browsers." href="/docs/client/live" />
586
+ <Card
587
+ title="Signal Overview"
588
+ description="once / broadcast / live in one place."
589
+ href="/docs/elements/signal"
590
+ />
591
+ </Cards>
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Signal",
3
+ "icon": "Radio",
4
+ "pages": ["index", "once", "broadcast", "live"]
5
+ }