okengine 0.18.5 → 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 (510) 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/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. 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
+ }