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,256 @@
1
+ ---
2
+ title: "Prompts"
3
+ description: "Versioned prompt artifacts on ai.model — out schemas, via recovery, budgets, timeouts, and fx.ask."
4
+ icon: "FileCode"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ A prompt is a **versioned artifact** minted on a model binding (`smart.prompt(...)`). It names
9
+ the ask, optional `in` / `out` shapes, a recovery chain, and a cost cap — then Flows call it
10
+ with `fx.ask`.
11
+
12
+ For developers who need typed triage JSON and a fallback model — declare once, ask everywhere.
13
+
14
+ <Callout title="The one rule">
15
+ Mint prompts with `model.prompt(name, options)` — there is no top-level `ai.prompt`. Input is
16
+ JSON-serialized (no `template` / `{{var}}` API). When `out` is set, the reply must match or
17
+ the ask throws.
18
+ </Callout>
19
+
20
+ ## Smallest Example
21
+
22
+ <Steps>
23
+
24
+ <Step>
25
+ ### Mint a prompt on a model
26
+
27
+ ```typescript title="src/core/ai.ts"
28
+ import { ai } from "okengine";
29
+ import { z } from "zod";
30
+
31
+ export const smart = ai.model("smart", {
32
+ provider: "openrouter",
33
+ model: "openrouter/free",
34
+ apiKey: process.env.OPENROUTER_API_KEY,
35
+ });
36
+
37
+ export const classifyTicket = smart.prompt("support.classify", {
38
+ version: 1,
39
+ budget: { maxCostPerCall: 0.01 },
40
+ in: z.object({ message: z.string() }),
41
+ out: z.object({
42
+ category: z.enum(["billing", "technical", "feature_request"]),
43
+ urgency: z.enum(["low", "medium", "high"]),
44
+ }),
45
+ });
46
+ ```
47
+
48
+ </Step>
49
+
50
+ <Step>
51
+ ### Ask from a Flow
52
+
53
+ ```typescript title="src/flows/support/classify.ts"
54
+ import { on, flow, http } from "okengine";
55
+ import { z } from "zod";
56
+ import { classifyTicket } from "@/core/ai";
57
+
58
+ export const classify = on(
59
+ http.post(),
60
+ flow({
61
+ in: z.object({ message: z.string().min(1) }),
62
+ do: async ({ message }, fx) => {
63
+ return await fx.ask(classifyTicket, { message });
64
+ },
65
+ }),
66
+ );
67
+ ```
68
+
69
+ Manifest lists `asks: ["support.classify"]`. Pin a version at call time with
70
+ `fx.ask("support.classify@1", input)`.
71
+
72
+ </Step>
73
+
74
+ <Step>
75
+ ### See structured output
76
+
77
+ With `out` set, the runtime appends a schema instruction and validates the JSON reply. Invalid
78
+ shape → `AiSchemaValidationError` (`code: "AiSchemaInvalid"`) — **no** automatic correction
79
+ retry.
80
+
81
+ </Step>
82
+
83
+ </Steps>
84
+
85
+ ## Progressive Patterns
86
+
87
+ <Tabs items={["Minimal", "via", "Budget", "Tools"]}>
88
+
89
+ <Tab value="Minimal">
90
+
91
+ Name + optional version — model is the parent binding:
92
+
93
+ ```typescript
94
+ export const summarize = smart.prompt("docs.summarize", { version: 1 });
95
+
96
+ await fx.ask(summarize, { text: "…" });
97
+ ```
98
+
99
+ </Tab>
100
+
101
+ <Tab value="via">
102
+
103
+ Ordered logical model names for recovery. Resolution:
104
+ `ask.via ?? prompt.via ?? [prompt.model]`:
105
+
106
+ ```typescript
107
+ export const triage = smart.prompt("ticket-triage", {
108
+ version: 1,
109
+ via: ["smart", "local"],
110
+ out: z.object({ category: z.string() }),
111
+ });
112
+
113
+ await fx.ask(triage, input, { via: ["local"] }); // call override
114
+ ```
115
+
116
+ **Consequence:** each via step uses that binding’s own `apiKey` / `baseUrl`.
117
+
118
+ </Tab>
119
+
120
+ <Tab value="Budget">
121
+
122
+ Per-call cost cap — exceeded throws `AiBudgetExceededError`:
123
+
124
+ ```typescript
125
+ smart.prompt("ticket-triage", {
126
+ budget: { maxCostPerCall: 0.02 },
127
+ });
128
+ ```
129
+
130
+ `maxCostPerRun` on a prompt budget is reserved for multi-step asks; agents use
131
+ `budget.maxCostPerRun` on `ai.agent`.
132
+
133
+ </Tab>
134
+
135
+ <Tab value="Tools">
136
+
137
+ Offer Flows for one ask without declaring an agent:
138
+
139
+ ```typescript
140
+ await fx.ask(
141
+ triage,
142
+ { message },
143
+ {
144
+ tools: ["docs.search", "tickets.create"],
145
+ maxSteps: 4,
146
+ },
147
+ );
148
+ ```
149
+
150
+ Default `maxSteps` is `6`. Tool names also stamp `effects.calls`.
151
+
152
+ </Tab>
153
+
154
+ </Tabs>
155
+
156
+ ## Options
157
+
158
+ | Option | Type | Default | Meaning |
159
+ | --------- | ------------------------------------- | ---------------- | --------------------------------------------------------------- |
160
+ | `version` | `number` | — | Pin with `fx.ask("name@N")` |
161
+ | `evals` | `string` | — | Path for `oke eval` JSONL |
162
+ | `budget` | `{ maxCostPerCall?, maxCostPerRun? }` | — | Cost contracts |
163
+ | `via` | `string[]` | `[parent model]` | Recovery chain of logical names |
164
+ | `timeout` | `"30s"` \| ms | — | Ask deadline (not a cost budget) |
165
+ | `in` | Schema | — | Declared / Manifest / doctor — **not** runtime-validated on ask |
166
+ | `out` | Schema | — | Runtime-validated (Zod / JSON Schema / field shorthand) |
167
+
168
+ ## How input reaches the model
169
+
170
+ There is **no** template string API. The runtime JSON-stringifies the ask input (redacting
171
+ secrets), and when `out` is set appends:
172
+
173
+ ```text
174
+ Reply with JSON only matching this schema: …
175
+ ```
176
+
177
+ Pass the fields you need as the second argument to `fx.ask` — do not invent `template` /
178
+ `{{message}}` options.
179
+
180
+ ## `fx.ask` options
181
+
182
+ | Option | Type | Default | Meaning |
183
+ | ---------- | --------- | ---------------- | ------------------------------- |
184
+ | `via` | refs | prompt chain | Override recovery for this call |
185
+ | `timeout` | duration | prompt `timeout` | Per-call deadline |
186
+ | `tools` | Flow refs | — | Tool loop via `fx.call` |
187
+ | `maxSteps` | `number` | `6` | Cap on tool rounds |
188
+
189
+ ## Embeds (related)
190
+
191
+ `ai.embed` is a separate declaration for vector pipelines into `store.index`:
192
+
193
+ ```typescript
194
+ export const kb = ai.embed("kb", {
195
+ model: "smart",
196
+ into: "knowledge",
197
+ });
198
+
199
+ const vector = await fx.embed(smart, "install guide");
200
+ const hits = await fx.search(kb, "how to install", { topK: 5 });
201
+ ```
202
+
203
+ `fx.embed` records `embeds` (not `asks`). Missing `into` or a non-vector index fails at
204
+ runtime with `ai: embed "…" has no into` / vector-index errors.
205
+
206
+ ## Troubleshooting
207
+
208
+ <Accordions>
209
+
210
+ <Accordion title='ai: unknown prompt "…"'>
211
+ The name was never registered with `model.prompt`, or the AI module was not imported before boot.
212
+ </Accordion>
213
+
214
+ <Accordion title="OKE1005 — UNDECLARED_ASK">
215
+ Cause: `Flow "{flow}" asks "{resource}" without declaring it.` Use the prompt handle in `do`
216
+ or list `effects: { asks: ["support.classify"] }`.
217
+ </Accordion>
218
+
219
+ <Accordion title='ai: schema validation failed for prompt "…"@N'>
220
+ `AiSchemaValidationError` — the model reply failed `out`. Message includes missing / extra / type
221
+ issues. There is no retry-with-corrections loop.
222
+ </Accordion>
223
+
224
+ <Accordion title='ai: prompt "…" exceeded maxCostPerCall'>
225
+ `AiBudgetExceededError` — raise `budget.maxCostPerCall` or reduce tokens / tool rounds.
226
+ </Accordion>
227
+
228
+ <Accordion title='ai: all models failed for prompt "…"'>
229
+ Every entry in the `via` chain failed (network, auth, or provider error). Check each binding’s
230
+ `apiKey` / `baseUrl` and driver health.
231
+ </Accordion>
232
+
233
+ </Accordions>
234
+
235
+ ## Learn more
236
+
237
+ - [Models](/docs/elements/ai/models) — bindings and providers
238
+ - [Agents](/docs/elements/ai/agents) — multi-step `fx.run`
239
+ - [AI](/docs/elements/ai) — guardrails and PII egress
240
+ - [OpenRouter](/docs/recipes/openrouter) — cloud ask recipe
241
+
242
+ ## Next
243
+
244
+ <Cards>
245
+ <Card
246
+ title="Agents"
247
+ description="Bounded agents whose tools are Flows."
248
+ href="/docs/elements/ai/agents"
249
+ />
250
+ <Card
251
+ title="Models"
252
+ description="Provider registry and drivers."
253
+ href="/docs/elements/ai/models"
254
+ />
255
+ <Card title="AI" description="Element overview." href="/docs/elements/ai" />
256
+ </Cards>
@@ -0,0 +1,266 @@
1
+ ---
2
+ title: "Email"
3
+ description: "Transactional email — channel.email templates, {{field}} catalogs, SMTP/Mailpit, Resend, SNDR, and Taqnyat Mail."
4
+ icon: "Mail"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Email (`channel.email`) is the default Channel medium. Declare a binder with a From address,
9
+ register templates, put bodies in the catalog, and send with `fx.send`.
10
+
11
+ For developers who need local catchers and production HTTP MTAs — same `fx.send` path, swap the
12
+ driver.
13
+
14
+ <Callout title="The one rule">
15
+ Use `channel.email({ from }).template(name, { schema, locales })` — never
16
+ `channel.email("name", { subject, body })`. Subject and body live in the catalog.
17
+ </Callout>
18
+
19
+ ## Smallest Example
20
+
21
+ <Steps>
22
+
23
+ <Step>
24
+ ### Declare binder + template
25
+
26
+ ```typescript title="src/core/channel.ts"
27
+ import { channel } from "okengine";
28
+ import { z } from "zod";
29
+
30
+ const mail = channel.email({ from: "App <noreply@example.com>" });
31
+
32
+ export const passwordReset = mail.template("auth.resetPassword", {
33
+ description: "Password reset link",
34
+ locales: ["en"],
35
+ schema: z.object({ resetLink: z.string().url() }),
36
+ });
37
+ ```
38
+
39
+ </Step>
40
+
41
+ <Step>
42
+ ### Catalog + send
43
+
44
+ ```typescript title="src/app.ts"
45
+ import { oke } from "okengine";
46
+
47
+ oke({
48
+ name: "app",
49
+ channel: {
50
+ catalog: {
51
+ "auth.resetPassword": {
52
+ en: {
53
+ subject: "Reset your password",
54
+ text: "Open {{resetLink}} to choose a new password.",
55
+ html: '<p><a href="{{resetLink}}">Reset your password</a></p>',
56
+ },
57
+ },
58
+ },
59
+ },
60
+ });
61
+ ```
62
+
63
+ ```typescript title="src/flows/auth/reset.ts"
64
+ import { on, flow, http } from "okengine";
65
+ import { z } from "zod";
66
+ import { passwordReset } from "@/core/channel";
67
+
68
+ export const reset = on(
69
+ http.post().public(),
70
+ flow({
71
+ in: z.object({ email: z.string().email() }),
72
+ do: async ({ email }, fx) => {
73
+ await fx.send(passwordReset, {
74
+ to: email,
75
+ data: { resetLink: `https://example.com/reset?t=${fx.id()}` },
76
+ });
77
+ return { ok: true };
78
+ },
79
+ }),
80
+ );
81
+ ```
82
+
83
+ </Step>
84
+
85
+ <Step>
86
+ ### Inspect locally
87
+
88
+ With Mailpit (`images.channel.email` + `SMTP_URL`), open `MAILPIT_UI_URL`. Manifest lists
89
+ `sends: ["auth.resetPassword"]` on the Flow.
90
+
91
+ </Step>
92
+
93
+ </Steps>
94
+
95
+ ## Progressive Patterns
96
+
97
+ <Tabs items={["Minimal", "Catalog", "Failover", "Plugin catalog"]}>
98
+
99
+ <Tab value="Minimal">
100
+
101
+ No catalog — runtime uses `subject: templateName` and `text: JSON.stringify(data)`:
102
+
103
+ ```typescript
104
+ await fx.send(passwordReset, {
105
+ to: "alice@example.com",
106
+ data: { resetLink: "https://example.com/r/1" },
107
+ });
108
+ ```
109
+
110
+ </Tab>
111
+
112
+ <Tab value="Catalog">
113
+
114
+ Per-locale `subject` / `text` / `html` with `{{field}}` interpolation from `data`:
115
+
116
+ ```typescript
117
+ catalog: {
118
+ "auth.resetPassword": {
119
+ en: { subject: "Reset", text: "{{resetLink}}", html: "<a href=\"{{resetLink}}\">Reset</a>" },
120
+ ar: { subject: "إعادة التعيين", text: "{{resetLink}}" },
121
+ },
122
+ }
123
+ ```
124
+
125
+ </Tab>
126
+
127
+ <Tab value="Failover">
128
+
129
+ Pin multiple email drivers in `oke({ channel: { drivers } })` or rely on the boot chain, then
130
+ order them per send:
131
+
132
+ ```typescript
133
+ await fx.send(passwordReset, {
134
+ to: "alice@example.com",
135
+ data: { resetLink: "…" },
136
+ via: ["smtp", "resend"],
137
+ });
138
+ ```
139
+
140
+ Receipt status `fallback` means an earlier attempt failed and a later one succeeded.
141
+
142
+ </Tab>
143
+
144
+ <Tab value="Plugin catalog">
145
+
146
+ Official plugins contribute catalogs with `.channelCatalog(…)` (e.g. OTP, magic link). App
147
+ `channel.catalog` merges with plugin contributions at boot.
148
+
149
+ </Tab>
150
+
151
+ </Tabs>
152
+
153
+ ## Options Reference
154
+
155
+ ### `channel.email(options?)`
156
+
157
+ | Option | Type | Meaning |
158
+ | -------- | -------- | --------------------- |
159
+ | `from` | `string` | Default From / sender |
160
+ | `sender` | `string` | Alias for `from` |
161
+
162
+ ### `.template(name, options?)`
163
+
164
+ | Option | Type | Meaning |
165
+ | ------------- | ---------- | --------------------------- |
166
+ | `description` | `string` | Console label |
167
+ | `locales` | `string[]` | Declared locales |
168
+ | `schema` | Schema | Payload contract for `data` |
169
+
170
+ Default From when neither binder nor template sets one: `"oke@localhost.test"`.
171
+
172
+ ## Drivers
173
+
174
+ | Driver id | Opens at boot | Env / keys |
175
+ | -------------- | ------------- | ----------------------------------------------------------------------------- |
176
+ | `smtp` | yes (default) | `SMTP_URL` or `OKE_CHANNEL_EMAIL_URL`; optional `SMTP_USER` / `SMTP_PASSWORD` |
177
+ | `console` | yes (test) | In-process inbox — no network |
178
+ | `resend` | yes | `RESEND_API_KEY` |
179
+ | `sndr` | yes | `SNDR_API_KEY`; optional `SNDR_BASE_URL` |
180
+ | `taqnyat-mail` | yes | `TAQNYAT_MAIL_TOKEN` + `TAQNYAT_CAMPAIGN` |
181
+
182
+ ```typescript title="oke.config.ts"
183
+ drivers: {
184
+ channel: {
185
+ email: { dev: "smtp", test: "console", prod: "resend" },
186
+ },
187
+ },
188
+ images: {
189
+ channel: { email: "axllent/mailpit:v1.31.1" },
190
+ },
191
+ ```
192
+
193
+ **Consequence:** `dev` and `prod` both speak SMTP protocol by default — Mailpit locally, your
194
+ relay in production. Swap `prod` to `resend` / `sndr` / `taqnyat-mail` when you want HTTP APIs.
195
+
196
+ Unknown id → `oke boot: unknown email channel driver "…"`. Missing SMTP URL →
197
+ `oke boot: smtp driver needs SMTP_URL`.
198
+
199
+ ## Locale & consent
200
+
201
+ Locale chain and suppression run before the driver (see [Overview](/docs/elements/channel) and
202
+ [Receipts](/docs/elements/channel/receipts)). Opted-out or prior hard-bounce addresses never
203
+ reach SMTP / Resend.
204
+
205
+ Channel catalogs are **not** ICU — do not use `fx.t` for email bodies ([i18n](/docs/reference/i18n)).
206
+
207
+ ## Troubleshooting
208
+
209
+ <Accordions>
210
+
211
+ <Accordion title="oke boot: smtp driver needs SMTP_URL">
212
+ Pin Mailpit / set `SMTP_URL=smtp://…`. URL must use the `smtp://` scheme (`oke boot: SMTP_URL must
213
+ use smtp://`).
214
+ </Accordion>
215
+
216
+ <Accordion title="oke boot: resend channel needs RESEND_API_KEY">
217
+ Email driver is `resend` but the key is missing. Export `RESEND_API_KEY` for that env.
218
+ </Accordion>
219
+
220
+ <Accordion title="oke boot: sndr channel needs SNDR_API_KEY">
221
+ Same pattern for `sndr` — set `SNDR_API_KEY` (and `SNDR_BASE_URL` if non-default).
222
+ </Accordion>
223
+
224
+ <Accordion title="oke boot: taqnyat-mail channel needs TAQNYAT_MAIL_TOKEN / TAQNYAT_CAMPAIGN">
225
+ Taqnyat Email requires both the mail token and a campaign name.
226
+ </Accordion>
227
+
228
+ <Accordion title="Message in Mailpit has JSON body / template name as subject">
229
+ No catalog entry for that template + locale. Add `channel.catalog` (or a plugin catalog) with
230
+ `subject` / `text` / `html`.
231
+ </Accordion>
232
+
233
+ <Accordion title="suppressed/opted-out or prior-bounce — no provider call">
234
+ Consent or hard-bounce suppression blocked the send. Check the receipt ledger; see
235
+ [Receipts](/docs/elements/channel/receipts).
236
+ </Accordion>
237
+
238
+ </Accordions>
239
+
240
+ ## Learn more
241
+
242
+ - [Channel overview](/docs/elements/channel) — declare → send → drivers
243
+ - [Mailpit](/docs/recipes/mailpit) — local SMTP catcher
244
+ - [Receipts](/docs/elements/channel/receipts) — delivery ledger
245
+ - [Environment variables](/docs/reference/environment-variables) — email boot binder
246
+ - [OTP plugin](/docs/plugins/otp) — `auth-otp-email` catalog
247
+
248
+ ## Next
249
+
250
+ <Cards>
251
+ <Card
252
+ title="SMS"
253
+ description="SMS templates and provider OTP."
254
+ href="/docs/elements/channel/sms"
255
+ />
256
+ <Card
257
+ title="Receipts"
258
+ description="Ledger, outcomes, and suppression."
259
+ href="/docs/elements/channel/receipts"
260
+ />
261
+ <Card
262
+ title="Channel Overview"
263
+ description="Physics, fx surface, and driver table."
264
+ href="/docs/elements/channel"
265
+ />
266
+ </Cards>