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,590 @@
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
+ flow({
347
+ in: z.object({ id: z.string() }),
348
+ do: async ({ id }, fx) => {
349
+ await fx.emit(orderStatus, { orderId: id, status: "shipped" });
350
+ return { id, status: "shipped" };
351
+ },
352
+ }),
353
+ );
354
+ ```
355
+
356
+ Zero SSE clients + `optional: false` → **OKE1240**. Cause:
357
+ `Flow "{flow}" emits signal "{resource}" with no subscriber.`
358
+ Fix: mount `http.live` (or a path `.live`) before emitting, or set `{ optional: true }`.
359
+
360
+ ## Retention
361
+
362
+ <Callout title="Detailed section">
363
+ If you only need an unbounded tape, skip this section. `retention` is live-only — omit both fields
364
+ (or omit `retention`) for unlimited history.
365
+ </Callout>
366
+
367
+ | Field | Type | Meaning |
368
+ | ---------- | ------------------------------------- | ----------------------------- |
369
+ | `maxAge` | duration string (`"24h"`, `"30s"`, …) | Drop events older than this |
370
+ | `maxCount` | integer ≥ 1 | Keep only the newest N events |
371
+
372
+ ```typescript title="src/signals/chat.ts"
373
+ import { signal } from "okengine";
374
+ import { z } from "zod";
375
+
376
+ export const chatMessage = signal.live("chat.message", {
377
+ optional: true,
378
+ retention: { maxAge: "7d", maxCount: 10_000 },
379
+ schema: z.object({
380
+ room: z.string(),
381
+ text: z.string(),
382
+ author: z.string(),
383
+ }),
384
+ });
385
+ ```
386
+
387
+ Invalid values throw at declare:
388
+
389
+ ```text
390
+ signal.live("…"): retention.maxAge must be a duration like "24h" or "30s"
391
+ signal.live("…"): retention.maxCount must be an integer ≥ 1
392
+ ```
393
+
394
+ **Consequence:** a pruned `id:` becomes a resume gap — reconnects that still send that
395
+ `Last-Event-ID` hit **OKE1210** / 410 `LiveResumeGap`. Prefer `autoResubscribe: true` on flaky
396
+ networks, or raise `maxCount` / `maxAge` if clients need longer catch-up.
397
+
398
+ ## Resume and gaps
399
+
400
+ <Callout title="Detailed section">
401
+ Resume is exclusive: replay events **after** `Last-Event-ID`, then continue live. Unknown or
402
+ pruned ids throw **OKE1210** before the SSE body — HTTP maps that to **410** `LiveResumeGap`.
403
+ </Callout>
404
+
405
+ | Symptom | Meaning | Fix |
406
+ | ----------------------------------- | ------------------------------- | -------------------------------------- |
407
+ | **OKE1210** / 410 `LiveResumeGap` | Cursor gone from the tape | Drop `Last-Event-ID`; replay remaining |
408
+ | `autoResubscribe: true` | Client clears gap after backoff | Prefer for flaky networks |
409
+ | Custom `fx.live(signal, { match })` | Server-side filter | Do not wrap with `fx.json.stream` |
410
+
411
+ ```text
412
+ Cursor "{afterId}" missing on "{signal}".
413
+ ```
414
+
415
+ SSE frames carry optional `id:` lines. Clients that reconnect send
416
+ `Last-Event-ID` from the last `id:` they actually received. A **410** means that cursor is
417
+ gone — drop it and replay the remaining tape.
418
+
419
+ ## Client subscription
420
+
421
+ <Callout title="Detailed section">
422
+ `signal.live` is HTTP SSE. `for await` stays on the server; the browser uses a callback. Prefer
423
+ `api.live` or `useLive` — not raw `EventSource` on an invented path.
424
+ </Callout>
425
+
426
+ The client picks the unique exposure whose `matchKey` fields are a subset of the input,
427
+ preferring the largest match (`{ orderId }` beats firehose). A tie needs `via: "unit.flow"`.
428
+
429
+ ```typescript
430
+ const stop = api.live(
431
+ orderStatus,
432
+ { orderId: "ord_1" },
433
+ {
434
+ onEvent: (event) => {
435
+ /* { orderId, status } */
436
+ },
437
+ onError: (err) => {
438
+ /* 4xx, envelope, or drop */
439
+ },
440
+ onOpen: () => {
441
+ /* HTTP 200, including reconnects */
442
+ },
443
+ autoResubscribe: false,
444
+ },
445
+ );
446
+ stop();
447
+ ```
448
+
449
+ `api.orders.events({ orderId }, { onEvent })` is the same shape on the exposing Flow.
450
+ Reconnects send `Last-Event-ID` from the last `id:` received.
451
+
452
+ | Option | Default | Meaning |
453
+ | ----------------- | ------------ | --------------------------------------------------- |
454
+ | `onEvent` | _(required)_ | Each JSON frame from the tape |
455
+ | `onError` | omitted | 4xx, envelope errors, or network drop |
456
+ | `onOpen` | omitted | After a successful SSE open (incl. reconnects) |
457
+ | `autoResubscribe` | `false` | Re-open after a drop (500ms…30s backoff) |
458
+ | `via` | omitted | Disambiguate when two exposures share a match shape |
459
+ | `signal` | omitted | `AbortSignal` to cancel the subscribe |
460
+
461
+ React:
462
+
463
+ ```typescript
464
+ import { useLive } from "okengine/client-react";
465
+
466
+ const { events, latest, error, isConnected } = useLive(
467
+ api,
468
+ orderStatus,
469
+ { orderId: "ord_1" },
470
+ { autoResubscribe: true },
471
+ );
472
+ ```
473
+
474
+ Resource live queries use `useLiveQuery` (snapshot + classified events), not `api.live`.
475
+ See [Client · Live](/docs/client/live).
476
+
477
+ ## What live is not
478
+
479
+ <Callout title="Detailed section">
480
+ Pick physics from the guarantee you need. Live is the wrong tool when work must be claimed once,
481
+ or when every in-process listener should react with no retained history.
482
+ </Callout>
483
+
484
+ | Need | Use instead |
485
+ | -------------------------------------- | --------------------------------------------------------------------------- |
486
+ | Exactly one worker processes the job | [`once`](/docs/elements/signal/once) |
487
+ | Every active Flow gets a copy, no tape | [`broadcast`](/docs/elements/signal/broadcast) |
488
+ | Classified CDC rows for a list window | [`http.resource` live](/docs/elements/flow/http#resources) / `useLiveQuery` |
489
+ | Durable multi-step work with journal | [Durable Workflows](/docs/elements/flow/workflows) |
490
+
491
+ Live does **not** use the `once` visibility lease or dead-letter queue. `retries` /
492
+ `deadLetter` may appear on the declare options bag, but the live path is the retained tape +
493
+ SSE resume — not competing-consumer physics.
494
+
495
+ ## Troubleshooting
496
+
497
+ <Accordions>
498
+
499
+ <Accordion title="404 on /_oke/live/…">
500
+ Confirm `on(http.live(signal))` (or a path `.live(signal)`) is adopted. Names in the default path
501
+ are URI-encoded (`encodeURIComponent`). A bare `404` with body `Not Found` means the router found
502
+ no match.
503
+ </Accordion>
504
+
505
+ <Accordion title="TypeError: live exposure must be GET">
506
+ `.live(signal)` only attaches to `http.get` / `http.live`. Other verbs reject live synthesis:
507
+ `on(http.*.live(signal)): live exposure must be GET`.
508
+ </Accordion>
509
+
510
+ <Accordion title="OKE1050 — live signal exposed twice">
511
+ Cause: `Live signal "{signal}" is exposed twice with the same gates ({gates}) and match ({match}
512
+ ).` Two firehoses (`http.live` or param-less `.live`) that share the signal and gates cannot boot.
513
+ Change the gate, add a path-param filter, or remove a route.
514
+ </Accordion>
515
+
516
+ <Accordion title="OKE1041 — method + path bound twice">
517
+ Cause: `{method} {path} is bound twice (flow "{flow}").` Two mounts collide on the same method +
518
+ path (for example two `http.live` firehoses that resolve to the same URL). Drop one binding.
519
+ </Accordion>
520
+
521
+ <Accordion title="OKE1210 — 410 LiveResumeGap">
522
+ Cause: `Cursor "{afterId}" missing on "{signal}".` That `Last-Event-ID` was pruned or never
523
+ existed. Reconnect without it; remaining events replay. `autoResubscribe: true` does this after
524
+ backoff.
525
+ </Accordion>
526
+
527
+ <Accordion title="OKE1240 when emitting live">
528
+ No subscriber / exposure counted. Set `{ optional: true }` (usual for firehoses) or mount
529
+ `http.live` before emitting in tests.
530
+ </Accordion>
531
+
532
+ <Accordion title="OKE1250 on emit">
533
+ Cause: `"{resource}": {detail}` from the Standard Schema issues. Align the payload with `schema` —
534
+ no client receives a frame.
535
+ </Accordion>
536
+
537
+ <Accordion title="Bound on(liveSignal, flow) as a worker">
538
+ Live is not competing-consumer physics. Use `signal.once` for workers, or expose SSE with
539
+ `http.live`.
540
+ </Accordion>
541
+
542
+ <Accordion title="Raw EventSource on /api/signals/…">
543
+ That path is not the engine firehose. Use `GET /_oke/live/{name}`, a gated `.live` route, or
544
+ `api.live` / `useLive` from the typed client.
545
+ </Accordion>
546
+
547
+ <Accordion title="Multiple live exposures (client)">
548
+ Two routes share the same match shape. Pass `via: "unit.flow"` or call the exposing Flow
549
+ (`api.orders.events({orderId}, {onEvent})`) instead of root `api.live`.
550
+ </Accordion>
551
+
552
+ <Accordion title="TypeError: retention is only valid with signal.live">
553
+ `retention: { maxAge, maxCount }` is live-only. Drop it on queue / pub-sub Signals, or switch
554
+ to `signal.live`.
555
+ </Accordion>
556
+
557
+ <Accordion title="live query requires a primary key / RLS driver">
558
+ Extract: `live: true on table "…" requires a primary key column`. Runtime needs an RLS-capable SQL
559
+ driver (`postgres` / `pglite`) and a gated identity. That path is resource live — not
560
+ `signal.live`. Attach `.gate(...)` and declare a PK.
561
+ </Accordion>
562
+
563
+ </Accordions>
564
+
565
+ ## Learn more
566
+
567
+ - [HTTP · Live Streams](/docs/elements/flow/http#live-streams) — exposure, uniqueness, match
568
+ - [Signal Overview](/docs/elements/signal) — delivery matrix and drivers
569
+ - [Once](/docs/elements/signal/once) — when you need competing workers instead
570
+ - [Broadcast](/docs/elements/signal/broadcast) — ephemeral fan-out without a tape
571
+ - [Client](/docs/client/live) — `api.live`, `useLive`, `useLiveQuery`
572
+ - [fx](/docs/reference/fx) — `fx.emit`, `fx.live`
573
+ - [Gate](/docs/elements/gate) — `.gate(...)` / `.public()` on triggers
574
+ - [Errors](/docs/reference/errors) — OKE1050 · OKE1210 · OKE1240 · OKE1250
575
+
576
+ ## Next
577
+
578
+ <Cards>
579
+ <Card
580
+ title="HTTP · Live Streams"
581
+ description="Firehose paths, filters, uniqueness, and client subscribe."
582
+ href="/docs/elements/flow/http#live-streams"
583
+ />
584
+ <Card title="Client" description="api.live and useLive for browsers." href="/docs/client/live" />
585
+ <Card
586
+ title="Signal Overview"
587
+ description="once / broadcast / live in one place."
588
+ href="/docs/elements/signal"
589
+ />
590
+ </Cards>
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Signal",
3
+ "icon": "Radio",
4
+ "pages": ["index", "once", "broadcast", "live"]
5
+ }