okengine 0.18.4 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,508 @@
1
+ ---
2
+ title: "Schedules"
3
+ description: "Cron helpers, every-intervals, app timezones, per-tenant rows, leader locks, catch-up one, and Console overrides."
4
+ icon: "Calendar"
5
+ source: "docs/spec/unified-theory.md"
6
+ ---
7
+
8
+ Named schedules trigger Flows on a calendar cron **or** a fixed duration. Declare with
9
+ `clock` / helpers, set the zone once on the app, then bind with `on(clockDecl, flow)`.
10
+
11
+ For developers running daily digests, health pings, and per-tenant billing loops on okengine.
12
+
13
+ <Callout title="The one rule">
14
+ Calendar work: prefer `oke({ clock: { timezone } })` (or `defineConfig`) and omit
15
+ `timezone` on each schedule. Intervals: durations are `ms|s|m|h|d` only — no weeks.
16
+ Both shapes share leader locks, catch-up `"one"`, and the Store.
17
+ </Callout>
18
+
19
+ <ClockSchedules />
20
+
21
+ ## Smallest Example
22
+
23
+ <Steps>
24
+
25
+ <Step>
26
+ ### Declare a schedule and bind a Flow
27
+
28
+ ```typescript title="src/flows/reports/daily.ts"
29
+ import { on, flow, clock } from "okengine";
30
+
31
+ export const dailyReportClock = clock.daily("reports.daily", { at: "06:00" });
32
+
33
+ export const runDailyReport = on(
34
+ dailyReportClock,
35
+ flow("reports.runDaily", {
36
+ plane: "operator",
37
+ do: async (_, fx) => {
38
+ await fx.call(buildDailyReport, { at: fx.clock.now() });
39
+ },
40
+ }),
41
+ );
42
+ ```
43
+
44
+ Set the zone once so each schedule stays short:
45
+
46
+ ```typescript title="src/app.ts"
47
+ import { oke } from "okengine";
48
+
49
+ export const app = oke({
50
+ name: "notes",
51
+ clock: { timezone: "Asia/Riyadh" },
52
+ });
53
+ ```
54
+
55
+ Or `defineConfig({ clock: { timezone: "Asia/Riyadh" } })`. `oke({ clock })` wins over
56
+ config; a per-clock `timezone` wins over both.
57
+
58
+ </Step>
59
+
60
+ <Step>
61
+ ### What fires
62
+
63
+ Every morning at 06:00 in the declared zone, the leader instance runs `reports.runDaily`.
64
+ `do` receives no payload — use `fx.clock.now()` when you need the fire instant.
65
+
66
+ With `oke dev`, the scheduler reconciles `reports.daily` into the Store and leader-elects
67
+ before each tick. Under `drivers.clock.test = "frozen"`, advance time in tests instead of
68
+ waiting for dawn.
69
+
70
+ </Step>
71
+
72
+ </Steps>
73
+
74
+ <Callout title="Jobs are Flows">
75
+ `on(clockDecl, flow)` is the same species as HTTP handlers and Signal consumers. There is no
76
+ separate job runner — see [Consumers · Clock Jobs](/docs/elements/flow/consumers#clock-jobs).
77
+ </Callout>
78
+
79
+ ## Progressive Patterns
80
+
81
+ From helpers and intervals to per-tenant rows and Console-tunable schedules:
82
+
83
+ <Tabs items={["Helpers", "Interval", "Per-tenant", "Complex fields", "Overridable"]}>
84
+
85
+ <Tab value="Helpers">
86
+
87
+ Named helpers compile to the same `ClockDecl` (a five-field `cron` or `every` string):
88
+
89
+ ```typescript title="src/clocks/reports.ts"
90
+ import { clock } from "okengine";
91
+
92
+ export const daily = clock.daily("reports.daily", { at: "06:00" });
93
+ export const rollup = clock.hourly("metrics.rollup", { minute: 0 });
94
+ export const digest = clock.weekly("notes.digest", {
95
+ on: ["mon", "fri"],
96
+ at: "09:00",
97
+ });
98
+ export const close = clock.monthly("billing.close", { on: [1, 15], at: "00:00" });
99
+ export const ping = clock.every("health.ping", "30s");
100
+ ```
101
+
102
+ Raw strings still work: `clock("x", { cron: "0 6 * * *" })`, Bun nicknames (`@hourly`),
103
+ and `clock("x", { every: "30s" })`.
104
+
105
+ **Consequence:** Manifest / Store still see `cron: "0 6 * * *"` — helpers are declare-time sugar.
106
+
107
+ </Tab>
108
+
109
+ <Tab value="Interval">
110
+
111
+ Fixed duration loops — `"200ms"` · `"30s"` · `"5m"` · `"1h"` · `"7d"`:
112
+
113
+ ```typescript title="src/flows/health/ping.ts"
114
+ import { on, flow, clock } from "okengine";
115
+
116
+ export const pingClock = clock.every("health.pingExternal", "30s");
117
+
118
+ export const pingExternal = on(
119
+ pingClock,
120
+ flow("health.pingExternal", {
121
+ plane: "operator",
122
+ do: async (_, fx) => {
123
+ await fx.call(pingUpstream);
124
+ },
125
+ }),
126
+ );
127
+ ```
128
+
129
+ Combine with injectable offsets inside `do`:
130
+
131
+ ```typescript
132
+ await fx.call(dropExpired, { before: fx.clock.ago("1h") });
133
+ ```
134
+
135
+ Unknown duration strings parse as `0` ms and never become due. A `"d"` is exactly
136
+ 86_400_000 ms — not a calendar day across DST. No week unit; no jitter on `clock()`
137
+ (retry jitter lives on Flow `retry`).
138
+
139
+ </Tab>
140
+
141
+ <Tab value="Per-tenant">
142
+
143
+ `clock.perTenant` expands one Store row per tenant (`{name}#{tenantId}`). The bare
144
+ template name is never ticked:
145
+
146
+ ```typescript title="src/clocks/invoices.ts"
147
+ import { on, flow, clock } from "okengine";
148
+
149
+ export const invoicesClock = clock.perTenant("invoices", { every: "1h" });
150
+
151
+ export const runInvoices = on(
152
+ invoicesClock,
153
+ flow("billing.invoices", {
154
+ plane: "operator",
155
+ do: async (_, fx) => {
156
+ await fx.call(closeOpenInvoices);
157
+ },
158
+ }),
159
+ );
160
+ ```
161
+
162
+ Equivalent: `clock("invoices", { every: "1h", perTenant: true })`.
163
+
164
+ **Consequence:** ten tenants → ten leader-elected rows. Catch-up still fires **once per
165
+ row** after downtime — not a burst of missed hours per tenant.
166
+
167
+ </Tab>
168
+
169
+ <Tab value="Complex fields">
170
+
171
+ `clock.cron` accepts a string **or** a field bag (lists, ranges, steps, weekday names):
172
+
173
+ ```typescript title="src/clocks/ops.ts"
174
+ import { clock } from "okengine";
175
+
176
+ export const sweep = clock.cron("ops.sweep", {
177
+ minute: "*/15",
178
+ hour: [9, 12, 17],
179
+ dayOfWeek: "1-5",
180
+ });
181
+
182
+ export const digest = clock.cron("ops.digest", {
183
+ at: "08:00",
184
+ dayOfWeek: ["mon", "wed", "fri"],
185
+ });
186
+ ```
187
+
188
+ | Field | Examples |
189
+ | ------------------------------------------ | --------------------------------------------------- |
190
+ | `minute` / `hour` / `dayOfMonth` / `month` | `0` · `[9, 12, 17]` · `"1-5"` · step strings |
191
+ | `dayOfWeek` | `1` · `"mon"` · `["mon", "fri"]` · `"1-5"` |
192
+ | `at` | `"06:00"` — sets minute + hour when those are unset |
193
+
194
+ Only standard five-field cron (what Bun parses). No “last day of month” / “nth weekday”.
195
+
196
+ You may set both `cron` and `every` on one declaration; extract records the cron as the
197
+ Manifest trigger when both are present. Prefer one primary shape unless you want both.
198
+
199
+ </Tab>
200
+
201
+ <Tab value="Overridable">
202
+
203
+ `overridable: true` lets Console edit the effective cron/every in the Store:
204
+
205
+ ```typescript title="src/clocks/digest.ts"
206
+ import { clock } from "okengine";
207
+
208
+ export const digestClock = clock.daily("notes.digest", {
209
+ at: "08:00",
210
+ overridable: true,
211
+ });
212
+ ```
213
+
214
+ Without it, a Console edit fails with `ScheduleNotOverridableError`
215
+ (`clock "{name}" is not overridable`).
216
+
217
+ </Tab>
218
+
219
+ </Tabs>
220
+
221
+ ## Helper Reference
222
+
223
+ | Helper / form | Signature | Default / notes | Compiles to |
224
+ | ----------------- | ----------------------------------------- | ------------------------------------------------------------------ | --------------------- |
225
+ | `clock` | `clock(name, { cron? \| every?, … })` | Need `cron` and/or `every` | same |
226
+ | `clock.daily` | `clock.daily(name, { at?, … })` | `at` default `"00:00"`; invalid `at` → `cron at: expected "HH:MM"` | five-field cron |
227
+ | `clock.hourly` | `clock.hourly(name, { minute?, … })` | `minute` default `0` | five-field cron |
228
+ | `clock.weekly` | `clock.weekly(name, { on, at?, … })` | `on` required (`sun`…`sat` / `0–6`); `at` default `"00:00"` | five-field cron |
229
+ | `clock.monthly` | `clock.monthly(name, { on, at?, … })` | `on` required (day of month); no “last day” token | five-field cron |
230
+ | `clock.cron` | `clock.cron(name, expr \| fields, opts?)` | Empty field bag throws; invalid string → `Bun.cron.parse` | five-field / nickname |
231
+ | `clock.every` | `clock.every(name, duration, opts?)` | Same duration grammar as `fx.clock.ago` / sleep | `every` string |
232
+ | `clock.perTenant` | `clock.perTenant(name, opts)` | Expands `{name}#{tenantId}` rows | same + `perTenant` |
233
+
234
+ At least one of `cron` or `every` is required on the callable form. Empty options throw
235
+ `clock("name"): require cron or every`. Invalid cron throws at declare (`invalid cron …`).
236
+
237
+ An empty `clock.cron` field bag throws
238
+ `cron fields: require at least one of at, minute, hour, dayOfMonth, month, dayOfWeek`.
239
+
240
+ ## Timezone Resolution
241
+
242
+ Cron math needs an IANA zone. Resolve it once; override only when a schedule must differ.
243
+
244
+ | Source | Wins when | Example |
245
+ | --------------------------------------- | ------------------- | ---------------------------------------------------------- |
246
+ | Per-clock `timezone` | Always | `clock.daily("x", { at: "06:00", timezone: "UTC" })` |
247
+ | `oke({ clock: { timezone } })` | No per-clock zone | `oke({ name: "app", clock: { timezone: "Asia/Riyadh" } })` |
248
+ | `defineConfig({ clock: { timezone } })` | No `oke({ clock })` | config default |
249
+ | Built-in default | Nothing else set | `"UTC"` |
250
+
251
+ ```typescript title="src/app.ts"
252
+ import { oke } from "okengine";
253
+ import { clock } from "okengine";
254
+
255
+ export const app = oke({
256
+ name: "notes",
257
+ clock: { timezone: "Asia/Riyadh" },
258
+ });
259
+
260
+ // Inherits Asia/Riyadh
261
+ export const digest = clock.daily("notes.digest", { at: "08:00" });
262
+
263
+ // Explicit UTC wins over the app default
264
+ export const utcRollup = clock.hourly("metrics.utc", {
265
+ minute: 0,
266
+ timezone: "UTC",
267
+ });
268
+ ```
269
+
270
+ `{ tz: "…" }` is not an option — it is ignored. Intervals (`every`) are duration-based and
271
+ do not consult the zone for tick spacing; the zone still lands on the Store row.
272
+
273
+ ## Binding & Input
274
+
275
+ Bind the returned handle with `on(clockDecl, flow)`. The scheduler invokes `do` with **no
276
+ payload** — destructure `_` and read time through `fx.clock`:
277
+
278
+ ```typescript title="src/flows/metrics/cleanup.ts"
279
+ import { on, flow, clock } from "okengine";
280
+ import { lt } from "drizzle-orm";
281
+ import { db, metricLogs } from "@/schema";
282
+
283
+ export const cleanupClock = clock.every("metrics.cleanup", "1h");
284
+
285
+ export const cleanup = on(
286
+ cleanupClock,
287
+ flow("metrics.cleanup", {
288
+ plane: "operator",
289
+ do: async (_, fx) => {
290
+ await fx
291
+ .store(db)
292
+ .delete(metricLogs)
293
+ .where(lt(metricLogs.timestamp, fx.clock.ago("7d")));
294
+ return { at: new Date(fx.clock.now()).toISOString() };
295
+ },
296
+ }),
297
+ );
298
+ ```
299
+
300
+ | Concern | Rule |
301
+ | ------- | ------------------------------------------------------------------ |
302
+ | Trigger | `on(clockDecl, flow)` — same Flow species as HTTP / Signal |
303
+ | Input | none — use `_` |
304
+ | Time | `fx.clock.now` / `ago` / `fromNow` / `duration` only |
305
+ | Plane | Prefer `plane: "operator"` for background work |
306
+ | Import | The `clock(…)` module must load at boot so reconcile sees the decl |
307
+
308
+ ## Options Reference
309
+
310
+ Shared options on `clock(name, options)` and helpers:
311
+
312
+ | Option / helper | Type | Default | Meaning |
313
+ | --------------------------------------- | ----------- | --------------------- | ------------------------------------------ |
314
+ | `cron` | `string` | — | Five-field cron or Bun nickname |
315
+ | `every` | `string` | — | Interval (`"30s"`, `"1h"`, `"7d"`, …) |
316
+ | `timezone` | IANA string | app default / `"UTC"` | Zone for cron math |
317
+ | `overridable` | `boolean` | `false` | Console may override schedule in the Store |
318
+ | `perTenant` | `boolean` | `false` | Expand `{name}#{tenantId}` rows |
319
+ | `description` | `string` | the name | Console / docs blurb |
320
+ | `oke({ clock: { timezone } })` | app option | — | Default zone when `timezone` omitted |
321
+ | `defineConfig({ clock: { timezone } })` | config | — | Same; overridden by `oke({ clock })` |
322
+
323
+ ### Duration units (`every`)
324
+
325
+ | Unit | Example | Milliseconds |
326
+ | ---- | --------- | ------------ |
327
+ | `ms` | `"200ms"` | 200 |
328
+ | `s` | `"30s"` | 30_000 |
329
+ | `m` | `"5m"` | 300_000 |
330
+ | `h` | `"1h"` | 3_600_000 |
331
+ | `d` | `"7d"` | 604_800_000 |
332
+
333
+ ## Per-tenant Schedules
334
+
335
+ `clock.perTenant` expands one Store row per tenant (`{name}#{tenantId}`). The bare
336
+ template name never ticks — `runNow("invoices")` returns `false`.
337
+
338
+ Tenant ids come from reconcile (`tenantIds`). New tenants get rows; deleted tenants mark
339
+ those rows `orphaned`.
340
+
341
+ | Shape | Example | Fires? |
342
+ | ------------------------ | --------------- | -------------------- |
343
+ | Template (declared name) | `invoices` | No — never ticked |
344
+ | Expanded row | `invoices#acme` | Yes — leader-elected |
345
+
346
+ **Consequence:** ten tenants → ten leases. Catch-up is `"one"` **per row**, so downtime
347
+ does not replay a burst of missed hours for each tenant.
348
+
349
+ ## Leader Lock
350
+
351
+ Multi-instance deploys need a shared clock driver — otherwise every pod fires the same
352
+ tick. Dev/prod default is **postgres** (test is **frozen**). A short lease (default
353
+ **30s**) means only one instance runs each fire.
354
+
355
+ **Consequence:** three pods calling `runNow` still execute the Flow once. After the lease
356
+ expires, another instance may take the next tick.
357
+
358
+ | Driver | Cross-instance lock | Best for |
359
+ | ---------- | ----------------------------------------- | --------------------------------- |
360
+ | `postgres` | Yes — shared `oke_crons` | Dev/prod multi-instance (default) |
361
+ | `file` | Yes — on one host (`.oke/crons.json`) | Single-host multi-process |
362
+ | `memory` | No — single process only | Local single process |
363
+ | `frozen` | Test harness — you drive `tick` / advance | Tests |
364
+
365
+ ## Catch-up Policy
366
+
367
+ <ClockCatchUp />
368
+
369
+ Catch-up is `"one"`: after 5 hours down on an hourly clock, the next tick fires **once**.
370
+ Missed slots are visible as `missedRuns` — they are not replayed as a burst.
371
+
372
+ **Consequence:** a digest that missed the night still runs once at boot, not 24 times.
373
+
374
+ Console health on each row exposes `driftMs`, `overdue`, `missedRuns`, and `catchUp: "one"`.
375
+ Flow `retry` is separate — it retries a failed fire, not missed calendar slots.
376
+
377
+ ## Store Lifecycle & Console
378
+
379
+ Named clocks reconcile into the Store at boot. The scheduler reads **effective** state from
380
+ the Store, never the source declaration directly.
381
+
382
+ ```text
383
+ Declared (Manifest / code) ← truth for names and defaults
384
+ Override (Store, overridable) ← operational drift
385
+ Effective = declared + override ← what actually runs
386
+ ```
387
+
388
+ | Status | Meaning | Fires? |
389
+ | ---------- | ----------------------------- | ---------------------------- |
390
+ | `active` | Reconciled and enabled | Yes (when due + lease) |
391
+ | `paused` | Operator paused via Console | No — until `active` again |
392
+ | `orphaned` | Decl removed (or tenant gone) | No — row kept, never deleted |
393
+
394
+ Reconcile upserts still-declared clocks as `active` (preserving lease / `lastRunAt` /
395
+ overrides when `overridable`). Removed declarations become `orphaned`.
396
+
397
+ | Console action | Requirement | Error when missing |
398
+ | --------------- | ------------------- | ------------------------------------------------------ |
399
+ | Run now | Active row + lease | `ClockResourceNotFoundError` — `cron "{id}" not found` |
400
+ | Pause | Existing row | same |
401
+ | Edit cron/every | `overridable: true` | `ScheduleNotOverridableError` |
402
+
403
+ **Consequence:** without `overridable: true`, change the schedule in source and redeploy —
404
+ Console edits are refused. Redeploying a still-overridable clock **preserves** overrides.
405
+
406
+ DST gap / fall-back overlap attaches a Store warning (`gap` / `overlap`); UTC never
407
+ warns; the scheduler still ticks. Detection covers simple `M H * * *` / `M H * * DOW`
408
+ forms; on overlap days crontab fires **once** (first occurrence).
409
+
410
+ | Prefer | When |
411
+ | ----------------------------------------------- | ------------------------------------ |
412
+ | `timezone: "UTC"` | No DST warnings |
413
+ | Non-ambiguous local hour (`08:00`, not `02:00`) | Digests that stay silent on DST days |
414
+
415
+ ## Troubleshooting
416
+
417
+ <Accordions>
418
+
419
+ <Accordion title='TypeError: clock("name"): require cron or every'>
420
+ Pass `{cron}` and/or `{every}`, or use a helper (`clock.daily`, `clock.every`, …). Declaration
421
+ throws before `on()`.
422
+ </Accordion>
423
+
424
+ <Accordion title="invalid cron at declare">
425
+ Cause: the expression failed `Bun.cron.parse`. Fix the five-field string / nickname, or fix
426
+ structured fields (`at` must be `"HH:MM"`). Error shape: `clock("…"): invalid cron "…" (…)`.
427
+ </Accordion>
428
+
429
+ <Accordion title='cron at: expected "HH:MM"'>
430
+ `at` on helpers / field bags must be `"HH:MM"` or `"H:MM"` with hour `0–23` and minute `0–59`.
431
+ `"6am"` and `"06:00:00"` fail.
432
+ </Accordion>
433
+
434
+ <Accordion title="Interval never fires">
435
+ Unknown duration strings parse as `0` and never become due. Use `ms|s|m|h|d` only. Confirm the
436
+ Flow is bound with `on(clockDecl, flow)` and the clock module is imported at boot.
437
+ </Accordion>
438
+
439
+ <Accordion title="tz is not a clock option">
440
+ Use `timezone: "Asia/Riyadh"` on the declaration, or set the app default with
441
+ `oke({ clock: { timezone: "Asia/Riyadh" } })` /
442
+ `defineConfig({ clock: { timezone: "Asia/Riyadh" } })`. `{ tz: "…" }` is ignored.
443
+ </Accordion>
444
+
445
+ <Accordion title="Cron / interval fired many times after downtime">
446
+ Catch-up is `"one"` per Store row. A burst usually means several clocks or `clock.perTenant` rows
447
+ (one per tenant), not a replay of missed slots.
448
+ </Accordion>
449
+
450
+ <Accordion title="Two pods ran the same job">
451
+ Need shared `drivers.clock` postgres (or `file` on one machine). `memory` does not elect a leader
452
+ across processes.
453
+ </Accordion>
454
+
455
+ <Accordion title="Bare template name never ticks">
456
+ Expected for `perTenant: true` — only `{name}#{tenantId}` rows fire. Ensure tenant ids are
457
+ available at reconcile.
458
+ </Accordion>
459
+
460
+ <Accordion title="ScheduleNotOverridableError from Console">
461
+ Cause: `clock "{name}" is not overridable`. Add `overridable: true` and redeploy, or change the
462
+ schedule in source.
463
+ </Accordion>
464
+
465
+ <Accordion title="ClockResourceNotFoundError">
466
+ Cause: `cron "{id}" not found` (or `run "{id}" not found`). The Console action targeted a name
467
+ that is not in the reconciled Store — check spelling against your `clock()` declarations.
468
+ </Accordion>
469
+
470
+ <Accordion title="DST gap / overlap warning on the row">
471
+ Informational. Pick a UTC cron, a non-ambiguous local hour, or accept the warning. The job still
472
+ schedules.
473
+ </Accordion>
474
+
475
+ <Accordion title="Paused schedule never fires">
476
+ Console pause sets `status: "paused"`. The scheduler skips it. A later reconcile of a
477
+ still-declared clock restores `active` (and keeps overrides when `overridable`).
478
+ </Accordion>
479
+
480
+ </Accordions>
481
+
482
+ ## Learn more
483
+
484
+ - [Clock overview](/docs/elements/clock) — `fx.clock` helpers and drivers
485
+ - [Durable Sleep](/docs/elements/clock/sleep) — `fx.clock.sleep(label, duration)`
486
+ - [Consumers · Clock Jobs](/docs/elements/flow/consumers#clock-jobs) — bind with `on(clockDecl, flow)`
487
+ - [fx](/docs/reference/fx) — `fx.clock.now` / `ago` / `fromNow` / `duration`
488
+ - [Errors](/docs/reference/errors) — `ScheduleNotOverridableError` · `ClockResourceNotFoundError`
489
+
490
+ ## Next
491
+
492
+ <Cards>
493
+ <Card
494
+ title="Durable Sleep"
495
+ description="Journaled pauses with fx.clock.sleep(label, duration)."
496
+ href="/docs/elements/clock/sleep"
497
+ />
498
+ <Card
499
+ title="Clock Overview"
500
+ description="Return to the Clock element overview."
501
+ href="/docs/elements/clock"
502
+ />
503
+ <Card
504
+ title="Consumers"
505
+ description="Signal workers, named Clock jobs, and SQL CDC."
506
+ href="/docs/elements/flow/consumers"
507
+ />
508
+ </Cards>