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
@@ -4,27 +4,28 @@
4
4
 
5
5
  import { describe, expect, test } from "bun:test";
6
6
  import { mockAiDriver } from "../../drivers/ai-mock.ts";
7
- import { ollamaAiDriver } from "../../drivers/ai-ollama.ts";
8
- import { aiDriverFor, aiUrlFor, bindAi, openDefaultsFor, resolveAiDriverId } from "./ai.ts";
7
+ import { openaiCompatibleAiDriver } from "../../drivers/ai-openai-compatible.ts";
8
+ import { aiDriverFor, bindAi, openDefaultsFor, resolveAiDriverId } from "./ai.ts";
9
9
 
10
10
  describe("ai boot binder", () => {
11
11
  test("aiDriverFor maps protocol ids; unknown / reserved throw", () => {
12
12
  expect(aiDriverFor("mock").id).toBe("mock");
13
- expect(aiDriverFor("ollama")).toBe(ollamaAiDriver);
13
+ expect(aiDriverFor("openai-compatible")).toBe(openaiCompatibleAiDriver);
14
+ expect(() => aiDriverFor("ollama")).toThrow(/unknown AI driver/);
14
15
  expect(() => aiDriverFor("bedrock")).toThrow(/not implemented/);
15
16
  expect(() => aiDriverFor("nope")).toThrow(/unknown AI driver/);
16
17
  });
17
18
 
18
19
  test("resolveAiDriverId defaults to mock; honours config + OKE_AI_DRIVER in docker", () => {
19
20
  expect(resolveAiDriverId({}, "test")).toBe("mock");
20
- expect(resolveAiDriverId({ config: { drivers: { ai: { test: "ollama" } } } }, "test")).toBe(
21
- "ollama",
22
- );
21
+ expect(
22
+ resolveAiDriverId({ config: { drivers: { ai: { test: "openai-compatible" } } } }, "test"),
23
+ ).toBe("openai-compatible");
23
24
 
24
25
  const prev = process.env.OKE_AI_DRIVER;
25
- process.env.OKE_AI_DRIVER = "ollama";
26
+ process.env.OKE_AI_DRIVER = "openai-compatible";
26
27
  try {
27
- expect(resolveAiDriverId({}, "test", true)).toBe("ollama");
28
+ expect(resolveAiDriverId({}, "test", true)).toBe("openai-compatible");
28
29
  } finally {
29
30
  if (prev === undefined) delete process.env.OKE_AI_DRIVER;
30
31
  else process.env.OKE_AI_DRIVER = prev;
@@ -51,37 +52,39 @@ describe("ai boot binder", () => {
51
52
  }
52
53
  });
53
54
 
54
- test("openDefaultsFor openai-compatible fails loud in docker without OKE_AI_URL", () => {
55
+ test("openDefaultsFor openai-compatible allows docker without OKE_AI_URL (cloud registry)", () => {
55
56
  const prevUrl = process.env.OKE_AI_URL;
56
57
  const prevBase = process.env.OPENAI_BASE_URL;
57
58
  delete process.env.OKE_AI_URL;
58
59
  delete process.env.OPENAI_BASE_URL;
59
60
  try {
60
- expect(() => openDefaultsFor("openai-compatible", true)).toThrow(/OKE_AI_URL/);
61
+ expect(openDefaultsFor("openai-compatible", true)).toEqual({});
61
62
  } finally {
62
63
  if (prevUrl !== undefined) process.env.OKE_AI_URL = prevUrl;
63
64
  if (prevBase !== undefined) process.env.OPENAI_BASE_URL = prevBase;
64
65
  }
65
66
  });
66
67
 
67
- test("aiUrlFor fails loud in docker without OKE_AI_URL", () => {
68
+ test("openDefaultsFor openai-compatible keeps compose URL when present", () => {
68
69
  const prevUrl = process.env.OKE_AI_URL;
69
- const prevHost = process.env.OLLAMA_HOST;
70
- delete process.env.OKE_AI_URL;
71
- delete process.env.OLLAMA_HOST;
70
+ const prevBase = process.env.OPENAI_BASE_URL;
71
+ process.env.OKE_AI_URL = "http://127.0.0.1:8080/v1";
72
+ delete process.env.OPENAI_BASE_URL;
72
73
  try {
73
- expect(() => aiUrlFor(true)).toThrow(/OKE_AI_URL/);
74
- expect(aiUrlFor(false)).toBe("http://127.0.0.1:11434");
74
+ expect(openDefaultsFor("openai-compatible", true)).toEqual({
75
+ baseUrl: "http://127.0.0.1:8080/v1",
76
+ });
75
77
  } finally {
76
- if (prevUrl !== undefined) process.env.OKE_AI_URL = prevUrl;
77
- if (prevHost !== undefined) process.env.OLLAMA_HOST = prevHost;
78
+ if (prevUrl === undefined) delete process.env.OKE_AI_URL;
79
+ else process.env.OKE_AI_URL = prevUrl;
80
+ if (prevBase !== undefined) process.env.OPENAI_BASE_URL = prevBase;
78
81
  }
79
82
  });
80
83
 
81
84
  test("bindAi uses injected defaultDriver over config (tests stay on mock)", () => {
82
85
  const runtime = bindAi(
83
86
  {
84
- config: { drivers: { ai: { test: "ollama" } } },
87
+ config: { drivers: { ai: { test: "openai-compatible" } } },
85
88
  ai: { defaultDriver: mockAiDriver },
86
89
  },
87
90
  undefined,
@@ -9,11 +9,6 @@
9
9
  import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
10
10
  import { anthropicAiDriver } from "../../drivers/ai-anthropic.ts";
11
11
  import { mockAiDriver } from "../../drivers/ai-mock.ts";
12
- import {
13
- normalizeOllamaBaseUrl,
14
- ollamaAiDriver,
15
- OLLAMA_DEFAULT_BASE_URL,
16
- } from "../../drivers/ai-ollama.ts";
17
12
  import { openaiCompatibleAiDriver } from "../../drivers/ai-openai-compatible.ts";
18
13
  import type { AiDriver, AiOpenOptions } from "../../drivers/ai-types.ts";
19
14
  import { createAiRuntime, type AiRuntime } from "../../elements/ai.ts";
@@ -50,7 +45,6 @@ export function bindAi(
50
45
  mock: lazyDriver("mock", docker),
51
46
  anthropic: lazyDriver("anthropic", docker),
52
47
  "openai-compatible": lazyDriver("openai-compatible", docker),
53
- ollama: lazyDriver("ollama", docker),
54
48
  },
55
49
  gates: options.ai?.gates ?? gate,
56
50
  now,
@@ -77,7 +71,7 @@ export function resolveAiDriverId(options: BootOptions, env: ConfigEnv, docker =
77
71
  if (liveUrl && fromEnv && fromEnv !== "mock") return fromEnv;
78
72
  const resolved = resolveDriverId(options.config?.drivers?.ai, env);
79
73
  if (liveUrl && (resolved === undefined || resolved === "mock")) {
80
- return fromEnv === "ollama" ? "ollama" : "openai-compatible";
74
+ return fromEnv === "openai-compatible" || !fromEnv ? "openai-compatible" : fromEnv;
81
75
  }
82
76
  if (resolved) return resolved;
83
77
  return "mock";
@@ -97,8 +91,6 @@ export function aiDriverFor(id: string): AiDriver {
97
91
  return anthropicAiDriver;
98
92
  case "openai-compatible":
99
93
  return openaiCompatibleAiDriver;
100
- case "ollama":
101
- return ollamaAiDriver;
102
94
  case "bedrock":
103
95
  case "vertex":
104
96
  throw new Error(`oke boot: AI driver "${id}" is reserved but not implemented yet`);
@@ -113,13 +105,7 @@ export function aiDriverFor(id: string): AiDriver {
113
105
  * @param id - Driver id
114
106
  * @param docker - Docker mode
115
107
  */
116
- export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
117
- if (id === "ollama") {
118
- return {
119
- baseUrl: aiUrlFor(docker),
120
- ...(process.env.OKE_AI_MODEL?.trim() ? { model: process.env.OKE_AI_MODEL.trim() } : {}),
121
- };
122
- }
108
+ export function openDefaultsFor(id: string, _docker = false): AiOpenOptions {
123
109
  if (id === "anthropic") {
124
110
  const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
125
111
  return {
@@ -129,12 +115,10 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
129
115
  }
130
116
  if (id === "openai-compatible") {
131
117
  const apiKey = process.env.OPENAI_API_KEY?.trim();
118
+ // Prefer explicit proxy / local compose URL. Cloud registry models
119
+ // (`ai.model({ provider: "openrouter" })`) resolve their own baseUrl —
120
+ // do not require OKE_AI_URL just because `oke dev` uses Docker for Postgres.
132
121
  const baseUrl = process.env.OPENAI_BASE_URL?.trim() || process.env.OKE_AI_URL?.trim();
133
- if (docker && !baseUrl) {
134
- throw new Error(
135
- "oke boot: openai-compatible driver needs OKE_AI_URL (did `oke dev` write .env.local?)",
136
- );
137
- }
138
122
  return {
139
123
  ...(apiKey ? { apiKey } : {}),
140
124
  ...(baseUrl ? { baseUrl } : {}),
@@ -144,28 +128,6 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
144
128
  return {};
145
129
  }
146
130
 
147
- /**
148
- * Ollama base URL — docker fails loud when compose did not write one.
149
- *
150
- * @param docker - Docker mode
151
- */
152
- export function aiUrlFor(docker = false): string {
153
- const raw = process.env.OKE_AI_URL?.trim() || process.env.OLLAMA_HOST?.trim();
154
- if (raw) return normalizeOllamaBaseUrl(raw);
155
- if (docker) {
156
- throw new Error(
157
- "oke boot: ollama driver needs OKE_AI_URL (did `oke dev -d` write .env.local?)",
158
- );
159
- }
160
- return OLLAMA_DEFAULT_BASE_URL;
161
- }
162
-
163
- /**
164
- * Merge fixed open defaults under per-call options.
165
- *
166
- * @param driver - Base driver
167
- * @param defaults - Env-derived open options
168
- */
169
131
  /**
170
132
  * Open a protocol driver with env defaults only when a model actually uses it.
171
133
  *
@@ -5,6 +5,7 @@
5
5
  import { mkdirSync } from "node:fs";
6
6
  import { dirname, resolve } from "node:path";
7
7
  import {
8
+ applyClockTimezoneDefaults,
8
9
  clock as declareClock,
9
10
  createClockRuntime,
10
11
  createFileCronStore,
@@ -38,6 +39,15 @@ export function resolveClockDriverId(options: BootOptions, env: ConfigEnv): stri
38
39
  return resolveDriverId(options.config?.drivers?.clock, env, CLOCK_DEFAULTS)!;
39
40
  }
40
41
 
42
+ /**
43
+ * Effective default timezone: `oke({ clock })` wins over `defineConfig({ clock })`.
44
+ *
45
+ * @param options - Boot options
46
+ */
47
+ function resolveDefaultClockTimezone(options: BootOptions): string | undefined {
48
+ return options.clock?.timezone ?? options.config?.clock?.timezone;
49
+ }
50
+
41
51
  /**
42
52
  * Construct / adopt a Clock runtime, register decls, reconcile.
43
53
  *
@@ -102,19 +112,27 @@ export async function bindClock(
102
112
  );
103
113
  }
104
114
 
115
+ const defaultTimezone = resolveDefaultClockTimezone(options);
105
116
  const clockDecls = new Map<string, ClockDecl>();
106
- for (const c of options.clocks ?? []) {
117
+ for (const c of applyClockTimezoneDefaults(options.clocks ?? [], defaultTimezone)) {
107
118
  clockDecls.set(c.name, c);
108
119
  }
109
120
  const coveredEvery = new Set([...clockDecls.values()].flatMap((c) => (c.every ? [c.every] : [])));
110
121
  for (const b of options.bindings ?? []) {
122
+ if (b.trigger.kind === "clock" && b.trigger.clock && !clockDecls.has(b.trigger.name)) {
123
+ const [resolved] = applyClockTimezoneDefaults([b.trigger.clock], defaultTimezone);
124
+ if (resolved) clockDecls.set(b.trigger.name, resolved);
125
+ }
111
126
  if (b.trigger.kind !== "every") continue;
112
127
  if (clockDecls.has(b.trigger.interval) || coveredEvery.has(b.trigger.interval)) continue;
113
- clockDecls.set(
114
- b.trigger.interval,
115
- declareClock(b.trigger.interval, { every: b.trigger.interval }),
128
+ const [synthetic] = applyClockTimezoneDefaults(
129
+ [declareClock(b.trigger.interval, { every: b.trigger.interval })],
130
+ defaultTimezone,
116
131
  );
117
- coveredEvery.add(b.trigger.interval);
132
+ if (synthetic) {
133
+ clockDecls.set(b.trigger.interval, synthetic);
134
+ coveredEvery.add(b.trigger.interval);
135
+ }
118
136
  }
119
137
  for (const decl of clockDecls.values()) {
120
138
  clock.register(decl);
@@ -303,7 +303,7 @@ describe("boot binders honour drivers.* config", () => {
303
303
  test("signal: drivers.signal memory binds memory", async () => {
304
304
  const result = await bootApplication({
305
305
  env: "test",
306
- signals: [signal("ping", { delivery: "once" })],
306
+ signals: [signal.once("ping")],
307
307
  config: {
308
308
  drivers: {
309
309
  signal: { dev: "redis", test: "memory", prod: "redis" },
@@ -321,7 +321,7 @@ describe("boot binders honour drivers.* config", () => {
321
321
  const fake = createSignalRedisFake();
322
322
  const result = await bootApplication({
323
323
  env: "test",
324
- signals: [signal("ping", { delivery: "once" })],
324
+ signals: [signal.once("ping")],
325
325
  clients: { signalRedis: fake },
326
326
  config: {
327
327
  drivers: {
@@ -340,7 +340,7 @@ describe("boot binders honour drivers.* config", () => {
340
340
  await expect(
341
341
  bootApplication({
342
342
  env: "test",
343
- signals: [signal("ping", { delivery: "once" })],
343
+ signals: [signal.once("ping")],
344
344
  config: {
345
345
  drivers: {
346
346
  signal: { test: "postgres" },
@@ -421,7 +421,7 @@ describe("boot binders honour drivers.* config", () => {
421
421
  await expect(
422
422
  bootApplication({
423
423
  env: "test",
424
- signals: [signal("ping", { delivery: "once" })],
424
+ signals: [signal.once("ping")],
425
425
  config: {
426
426
  drivers: {
427
427
  signal: { test: "nats" },
@@ -16,7 +16,7 @@ import { store } from "../elements/store.ts";
16
16
  import { vault, VaultBootError } from "../elements/vault.ts";
17
17
  import { bootApplication, resolveElementNeeds } from "./boot.ts";
18
18
  import { flow, resetFlowSeq } from "./flow.ts";
19
- import { every, http } from "./triggers.ts";
19
+ import { http } from "./triggers.ts";
20
20
  import { oke } from "./app.ts";
21
21
  import { on, resetBindings } from "./on.ts";
22
22
 
@@ -79,7 +79,7 @@ describe("boot — lazy element needs", () => {
79
79
  expect(needs.signal).toBe(false);
80
80
  });
81
81
 
82
- test("oke() Store-only graph stays under the prior 50 kB baseline", async () => {
82
+ test("oke() Store-only graph stays under the prior 58 kB baseline", async () => {
83
83
  const dir = await mkdtemp(join(tmpdir(), "oke-store-only-"));
84
84
  const entry = join(dir, "entry.ts");
85
85
  const appPath = join(import.meta.dir, "app.ts");
@@ -118,6 +118,8 @@ describe("boot — lazy element needs", () => {
118
118
  }
119
119
  // Rebased after host API-key persist + allowlist on `oke()` and the
120
120
  // realtime bridge (CDC sink, LiveQuery runtime, openLiveStream).
121
+ // Browser JSON page (`json-code-block`) is lazy on `app.fetch` so this
122
+ // graph does not pin the traces-language HTML/CSS chunk.
121
123
  expect(total).toBeLessThan(58_000);
122
124
  } finally {
123
125
  await rm(dir, { recursive: true, force: true });
@@ -152,8 +154,9 @@ describe("boot — cron fires without manual dispatch", () => {
152
154
  resetFlowSeq();
153
155
 
154
156
  let ran = 0;
157
+ const expireStale = clock("expire-stale", { every: "1h" });
155
158
  on(
156
- every("1h"),
159
+ expireStale,
157
160
  flow("cleanup.expire", {
158
161
  do: () => {
159
162
  ran += 1;
@@ -163,14 +166,14 @@ describe("boot — cron fires without manual dispatch", () => {
163
166
 
164
167
  const app = oke({
165
168
  name: "cron-boot",
166
- clocks: [clock("expire-stale", { every: "1h" })],
169
+ clocks: [expireStale],
167
170
  env: "test",
168
171
  });
169
172
 
170
173
  await app.boot({
171
174
  env: "test",
172
175
  startScheduler: false,
173
- clocks: [clock("expire-stale", { every: "1h" })],
176
+ clocks: [expireStale],
174
177
  });
175
178
 
176
179
  const rt = app.bootResult!.clock!;
@@ -251,8 +254,9 @@ describe("boot — cron autostart via real timer loop", () => {
251
254
  jest.useFakeTimers({ now: 1_000_000 });
252
255
 
253
256
  let ran = 0;
257
+ const autostartClock = clock("cleanup.autostart", { every: "10m" });
254
258
  on(
255
- every("10m"),
259
+ autostartClock,
256
260
  flow("cleanup.autostart", {
257
261
  do: () => {
258
262
  ran += 1;
@@ -263,6 +267,7 @@ describe("boot — cron autostart via real timer loop", () => {
263
267
  // Non-test env → startScheduler defaults ON. No createTestApp / advance.
264
268
  const app = oke({
265
269
  name: "cron-autostart",
270
+ clocks: [autostartClock],
266
271
  env: "dev",
267
272
  schedulerIntervalMs: 1000,
268
273
  config: {
@@ -42,6 +42,7 @@ import type {
42
42
  } from "../elements/vault.ts";
43
43
  import type { JournalRuntime } from "./boot-bind/journal.ts";
44
44
  import type { InstanceRuntime, InstanceStore } from "./instances.ts";
45
+ import { isBenignSqlDisconnect } from "./instances.ts";
45
46
  import { resolveInstanceId } from "./instance-id.ts";
46
47
  import {
47
48
  resolveRunsConsoleBridge,
@@ -111,6 +112,14 @@ export interface BootOptions {
111
112
  readonly signals?: readonly SignalDecl[];
112
113
  /** Named clock declarations. */
113
114
  readonly clocks?: readonly ClockDecl[];
115
+ /**
116
+ * Clock element defaults — `{ timezone }` for declarations that omit
117
+ * `timezone`. Merged with `defineConfig({ clock: { timezone } })` at bind
118
+ * (`oke({ clock })` wins over config).
119
+ */
120
+ readonly clock?: {
121
+ readonly timezone?: string;
122
+ };
114
123
  /** Store facet declarations to register. */
115
124
  readonly stores?: readonly StoreDecl[];
116
125
  /** Channel runtime options. */
@@ -281,7 +290,7 @@ export function resolveElementNeeds(options: BootOptions): ElementNeeds {
281
290
  if ((e?.sends?.length ?? 0) > 0) channel = true;
282
291
  if ((e?.asks?.length ?? 0) > 0) ai = true;
283
292
  for (const t of f.triggers) {
284
- if (t.kind === "every") clock = true;
293
+ if (t.kind === "clock" || t.kind === "every") clock = true;
285
294
  if (t.kind === "signal") signal = true;
286
295
  if (t.kind === "cdc") store = true;
287
296
  if (t.kind === "http" && t.gates.length > 0) gate = true;
@@ -292,7 +301,7 @@ export function resolveElementNeeds(options: BootOptions): ElementNeeds {
292
301
  for (const b of options.bindings ?? []) {
293
302
  considerFlow(b.flow);
294
303
  const t = b.trigger;
295
- if (t.kind === "every") clock = true;
304
+ if (t.kind === "clock" || t.kind === "every") clock = true;
296
305
  if (t.kind === "signal") signal = true;
297
306
  if (t.kind === "cdc") store = true;
298
307
  if (t.kind === "http" && t.gates.length > 0) gate = true;
@@ -529,10 +538,16 @@ export async function bootApplication(input: BootOptions = {}): Promise<BootResu
529
538
  const clockRt = clock;
530
539
  const durableResume = options.onDurableResume;
531
540
  const fleet = instances;
541
+ /** Swallow pause / disconnect noise so compose-stop does not flood the TTY. */
542
+ const ignoreBenignSql = (err: unknown): void => {
543
+ if (isBenignSqlDisconnect(err)) return;
544
+ // Unexpected scheduler failures stay visible.
545
+ console.error(err);
546
+ };
532
547
  schedulerTimer = setInterval(() => {
533
- if (clockRt) void clockRt.tick();
534
- if (journal && durableResume) void durableResume();
535
- if (fleet) void fleet.maybeHeartbeat();
548
+ if (clockRt) void Promise.resolve(clockRt.tick()).catch(ignoreBenignSql);
549
+ if (journal && durableResume) void Promise.resolve(durableResume()).catch(ignoreBenignSql);
550
+ if (fleet) void fleet.maybeHeartbeat().catch(ignoreBenignSql);
536
551
  }, period);
537
552
  schedulerTimer.unref?.();
538
553
  }
@@ -635,6 +650,13 @@ export function resetNoEffectsWarnForTests(): void {
635
650
  noEffectsWarned = false;
636
651
  }
637
652
 
653
+ /** Result of a best-effort Manifest extract (never throws). */
654
+ type AutoExtractResult = {
655
+ readonly manifest?: Manifest;
656
+ /** Present when extract threw — surfaced before OKE1020 in strict boots. */
657
+ readonly error?: string;
658
+ };
659
+
638
660
  /**
639
661
  * Best-effort AoT extraction from `rootDir` — mirrors the CLI's own lazy,
640
662
  * defensive `extractManifest` use (`oke dev`'s `tryLoadProjectManifest`).
@@ -647,13 +669,14 @@ export function resetNoEffectsWarnForTests(): void {
647
669
  *
648
670
  * @param rootDir - Project root to extract from
649
671
  */
650
- async function tryAutoExtractManifest(rootDir: string): Promise<Manifest | undefined> {
672
+ async function tryAutoExtractManifest(rootDir: string): Promise<AutoExtractResult> {
651
673
  try {
652
674
  const url = new URL("../compiler/extract.ts", import.meta.url);
653
675
  const { extractManifest } = (await import(url.href)) as typeof import("../compiler/extract.ts");
654
- return await extractManifest({ rootDir });
655
- } catch {
656
- return undefined;
676
+ return { manifest: await extractManifest({ rootDir }) };
677
+ } catch (err) {
678
+ const message = err instanceof Error ? err.message : String(err);
679
+ return { error: message.length > 0 ? message : "unknown extract failure" };
657
680
  }
658
681
  }
659
682
 
@@ -663,9 +686,14 @@ async function tryAutoExtractManifest(rootDir: string): Promise<Manifest | undef
663
686
  * an explicit {@link BootOptions.manifest}, or a lazy AoT extract from
664
687
  * {@link BootOptions.rootDir} / `OKE_ROOT_DIR`.
665
688
  *
666
- * When neither is available: `test` stays open with a once-per-process
667
- * `oke boot:` warning; `dev` with compose infra and `prod` fail loud
668
- * (`OKE1008`)never a silent open door in a deploy-shaped environment.
689
+ * When a Manifest is available but a Flow is absent from it (framework /
690
+ * plugin Flows outside the app tree), stamp an empty least-privilege token
691
+ * automaticallydo not hand-declare `effects: {}` on those Flows.
692
+ *
693
+ * When neither Manifest nor declared effects are available: `test` stays
694
+ * open with a once-per-process `oke boot:` warning; `dev` with compose
695
+ * infra and `prod` fail loud (`OKE1020`) — never a silent open door in a
696
+ * deploy-shaped environment.
669
697
  *
670
698
  * @param flows - Adopted flows
671
699
  * @param env - Resolved {@link ConfigEnv}
@@ -683,10 +711,15 @@ export async function mintCapabilities(
683
711
  const map = new Map<string, CapabilityToken>();
684
712
 
685
713
  let manifest = options.manifest;
714
+ let extractError: string | undefined;
686
715
  const needsManifest = manifest === undefined && flows.some((f) => f.effects === undefined);
687
716
  if (needsManifest) {
688
717
  const rootDir = options.rootDir ?? process.env["OKE_ROOT_DIR"];
689
- if (rootDir) manifest = await tryAutoExtractManifest(rootDir);
718
+ if (rootDir) {
719
+ const extracted = await tryAutoExtractManifest(rootDir);
720
+ manifest = extracted.manifest;
721
+ extractError = extracted.error;
722
+ }
690
723
  }
691
724
 
692
725
  for (const f of flows) {
@@ -701,17 +734,34 @@ export async function mintCapabilities(
701
734
  continue;
702
735
  }
703
736
 
737
+ // Manifest extract succeeded (or an explicit Manifest was passed) but this
738
+ // flow is absent from it — typically a framework / plugin Flow that lives
739
+ // outside the app tree. Stamp an empty least-privilege token; do not hand
740
+ // declare `effects: {}` on those Flows. Real fx usage still needs inference
741
+ // (app source) or an explicit non-empty `effects` block.
742
+ if (manifest !== undefined) {
743
+ map.set(f.name, createCapabilityToken(f.name, {}));
744
+ continue;
745
+ }
746
+
704
747
  const strict = env === "prod" || (env === "dev" && options.docker === true);
705
748
  if (strict) {
749
+ if (extractError !== undefined) {
750
+ emitBootWarn(`oke boot: Manifest extract failed — ${extractError}`);
751
+ }
706
752
  throwOke("NO_EFFECTS_DECLARED", { flow: f.name });
707
753
  }
708
754
  if (!noEffectsWarned) {
709
755
  noEffectsWarned = true;
756
+ const extractHint =
757
+ extractError !== undefined ? ` Manifest extract failed (${extractError}).` : "";
710
758
  emitBootWarn(
711
759
  `oke boot: flow "${f.name}" (and possibly others) has no declared effects and no ` +
712
760
  "Manifest-derived effects — running with an OPEN capability token (every access " +
713
- "allowed, ledgered but not gated). Run `oke build`, or boot with `manifest` / " +
714
- "`rootDir`, before deploying — dev+compose/prod refuse to boot this way.",
761
+ "allowed, ledgered but not gated)." +
762
+ extractHint +
763
+ " Run `oke build`, or boot with `manifest` / `rootDir`, before deploying — " +
764
+ "dev+compose/prod refuse to boot this way.",
715
765
  );
716
766
  }
717
767
  map.set(f.name, createCapabilityToken(f.name, undefined));
@@ -757,7 +807,7 @@ async function tryListFlowsUnits(rootDir: string): Promise<readonly string[] | u
757
807
  * Opt-in only via {@link BootOptions.rootDir} / `OKE_ROOT_DIR` (same gate as
758
808
  * {@link mintCapabilities}'s Manifest fallback) — never a filesystem read
759
809
  * the kernel performs on its own. `test` warns once per process; `prod`
760
- * (and `dev` with compose) fail loud (`OKE1009`) — same class as
810
+ * (and `dev` with compose) fail loud (`OKE1030`) — same class as
761
811
  * `NO_EFFECTS_DECLARED`, never a silently-incomplete route table in a
762
812
  * deploy-shaped environment.
763
813
  *
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Shared invoke-boundary contract — authored on HTTP / call / MCP exposures,
3
+ * projected onto Manifest `flows.*.{in,out,errors,breaking}`.
4
+ *
5
+ * Distinct from Signal/Channel **emit schema** (payload in motion).
6
+ */
7
+
8
+ import type { InferSchemaOutput, SchemaInput } from "../validation/standard-schema.ts";
9
+ import type { FlowErrorMap } from "./flow.ts";
10
+
11
+ /** Invoke contract bag shared by `http.*`, `call`, and `mcp.tool`. */
12
+ export interface BoundaryContract<
13
+ ISchema = SchemaInput,
14
+ OSchema = SchemaInput,
15
+ E extends FlowErrorMap = FlowErrorMap,
16
+ > {
17
+ /** Request / args schema (Standard Schema). */
18
+ readonly in?: ISchema;
19
+ /** Success reply schema (Manifest + typed client; not runtime-validated). */
20
+ readonly out?: OSchema;
21
+ /** Typed domain errors (`fx.fail`). */
22
+ readonly errors?: E;
23
+ /**
24
+ * Acknowledge intentional Manifest contract breaks for the bound flow
25
+ * (`oke doctor --diff` / CI gate).
26
+ */
27
+ readonly breaking?: boolean;
28
+ }
29
+
30
+ /** Infer validated input from a {@link BoundaryContract} bag. */
31
+ export type InferBoundaryIn<Bag> = Bag extends { readonly in: infer S }
32
+ ? InferSchemaOutput<S>
33
+ : unknown;
34
+
35
+ /** Infer success output from a {@link BoundaryContract} bag. */
36
+ export type InferBoundaryOut<Bag> = Bag extends { readonly out: infer S }
37
+ ? InferSchemaOutput<S>
38
+ : unknown;
39
+
40
+ /** Infer error map from a {@link BoundaryContract} bag. */
41
+ export type InferBoundaryErrors<Bag> = Bag extends {
42
+ readonly errors: infer E extends FlowErrorMap;
43
+ }
44
+ ? E
45
+ : {};
46
+
47
+ /** Runtime fields stamped onto a Flow from an exposure bag. */
48
+ export interface StampedBoundaryContract {
49
+ readonly in: SchemaInput | undefined;
50
+ readonly out: SchemaInput | undefined;
51
+ readonly errors: FlowErrorMap | undefined;
52
+ readonly breaking: boolean;
53
+ }
54
+
55
+ /**
56
+ * Normalize an optional contract bag into stampable fields.
57
+ *
58
+ * @param bag - Exposure contract (may be undefined / partial)
59
+ */
60
+ export function stampBoundaryContract(
61
+ bag: BoundaryContract | undefined | null,
62
+ ): StampedBoundaryContract {
63
+ if (bag == null) {
64
+ return { in: undefined, out: undefined, errors: undefined, breaking: false };
65
+ }
66
+ return {
67
+ in: bag.in as SchemaInput | undefined,
68
+ out: bag.out as SchemaInput | undefined,
69
+ errors: bag.errors,
70
+ breaking: bag.breaking ?? false,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Apply a stamped contract onto a flow carrier (runtime mutation).
76
+ *
77
+ * @param flow - Flow definition object
78
+ * @param stamped - Normalized contract fields
79
+ */
80
+ export function applyBoundaryContract(
81
+ flow: {
82
+ in: SchemaInput | undefined;
83
+ out: SchemaInput | undefined;
84
+ errors: FlowErrorMap | undefined;
85
+ breaking: boolean;
86
+ },
87
+ stamped: StampedBoundaryContract,
88
+ ): void {
89
+ flow.in = stamped.in;
90
+ flow.out = stamped.out;
91
+ flow.errors = stamped.errors;
92
+ if (stamped.breaking) flow.breaking = true;
93
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Kernel edge profile budget — AGENTS.md / unified-theory §24.
3
- * Limit: < 16 kB gzipped (minified bundle).
3
+ * Limit: < 17 kB gzipped (minified bundle).
4
4
  */
5
5
 
6
6
  import { describe, expect, test } from "bun:test";