okengine 0.18.5 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. package/src/drivers/ollama.ts +0 -14
@@ -129,7 +129,7 @@ describe("fx — capability enforcement", () => {
129
129
  err = e;
130
130
  }
131
131
  expect(err).toBeInstanceOf(OkeError);
132
- expect((err as OkeError).code).toBe(1015);
132
+ expect((err as OkeError).code).toBe(1810);
133
133
  });
134
134
 
135
135
  test("index driverId is meilisearch before first I/O (not a function, not memory)", async () => {
@@ -485,9 +485,15 @@ describe("fx — wholesale swap", () => {
485
485
  async ask() {
486
486
  return {};
487
487
  },
488
+ async embed() {
489
+ return [];
490
+ },
488
491
  async search() {
489
492
  return [];
490
493
  },
494
+ async fetch() {
495
+ return new Response(null, { status: 204 });
496
+ },
491
497
  async run() {
492
498
  return {};
493
499
  },
@@ -679,31 +685,31 @@ describe("fx.json", () => {
679
685
  });
680
686
 
681
687
  describe("errors — registry", () => {
682
- test("OKE1042 is reserved with the §21 shape", () => {
688
+ test("OKE1240 is reserved with the §21 shape", () => {
683
689
  const def = OKE_ERRORS.ORPHAN_EMIT;
684
- expect(def.code).toBe(1042);
685
- expect(lookupOkeError(1042)).toEqual(def);
690
+ expect(def.code).toBe(1240);
691
+ expect(lookupOkeError(1240)).toEqual(def);
686
692
  const err = new OkeError(def, {
687
693
  flow: "bookings.create",
688
694
  resource: "order-placed",
689
695
  });
690
- expect(err.message).toContain("OKE1042");
696
+ expect(err.message).toContain("OKE1240");
691
697
  expect(err.message).toContain("no subscriber");
692
- expect(err.message).toContain("https://oke.omqkhafi.dev/e/1042");
698
+ expect(err.message).toContain("https://oke.omqkhafi.dev/e/1240");
693
699
  expect(err.message).toContain("→");
694
700
  });
695
701
 
696
- test("OKE1043 is reserved for signal schema emit failures", () => {
702
+ test("OKE1250 is reserved for signal schema emit failures", () => {
697
703
  const def = OKE_ERRORS.SIGNAL_SCHEMA;
698
- expect(def.code).toBe(1043);
699
- expect(lookupOkeError(1043)).toEqual(def);
704
+ expect(def.code).toBe(1250);
705
+ expect(lookupOkeError(1250)).toEqual(def);
700
706
  const err = new OkeError(def, {
701
707
  resource: "order-placed",
702
708
  detail: "total: Expected number, received string",
703
709
  });
704
- expect(err.message).toContain("OKE1043");
710
+ expect(err.message).toContain("OKE1250");
705
711
  expect(err.message).toContain("order-placed");
706
- expect(err.message).toContain("https://oke.omqkhafi.dev/e/1043");
712
+ expect(err.message).toContain("https://oke.omqkhafi.dev/e/1250");
707
713
  });
708
714
 
709
715
  test("fx.fail returns a value, not an exception", () => {
package/src/kernel/fx.ts CHANGED
@@ -44,7 +44,13 @@ import type { TenantStore } from "../auth/tenants.ts";
44
44
  import type { SessionCrypto, SessionStore } from "../auth/sessions.ts";
45
45
  import type { Manifest } from "../manifest/types.ts";
46
46
  import { createCapabilityToken, type CapabilityToken } from "./capability.ts";
47
- import { createEffectLedger, recordEffect, reversibilityOf, type EffectLedger } from "./effects.ts";
47
+ import {
48
+ createEffectLedger,
49
+ recordEffect,
50
+ reversibilityOf,
51
+ type EffectExternal,
52
+ type EffectLedger,
53
+ } from "./effects.ts";
48
54
  import { resolveDurationMs } from "./elapsed.ts";
49
55
  import {
50
56
  DryRunWriteIsolationError,
@@ -90,6 +96,11 @@ function loadFxLiveStream(): typeof import("./fx-live-stream.ts") {
90
96
  return lazyRequire(import.meta.dir, ["fx", "live", "stream"].join("-"));
91
97
  }
92
98
 
99
+ /** `fx.fetch` — keep host parsing + dry-run stub off cold edge / Store-only graphs. */
100
+ function loadFxFetch(): typeof import("./fx-fetch.ts") {
101
+ return lazyRequire(import.meta.dir, ["fx", "fetch"].join("-"));
102
+ }
103
+
93
104
  /** Resource ref Flows declare to read the Runs store via {@link Fx.runs}. */
94
105
  export const RUNS_RESOURCE = "runs";
95
106
 
@@ -386,7 +397,7 @@ export interface JsonStreamResult {
386
397
  readonly kind: "stream";
387
398
  readonly status: 200;
388
399
  readonly chunks: AsyncIterable<unknown>;
389
- /** Awaited before the 200 SSE body; throws OKE1014 on a missing resume cursor. */
400
+ /** Awaited before the 200 SSE body; throws OKE1210 on a missing resume cursor. */
390
401
  ready?: () => Promise<void>;
391
402
  /** Set by the kernel to commit journal / Runs after the stream settles. */
392
403
  finalize?: () => Promise<void>;
@@ -693,7 +704,7 @@ export interface Fx {
693
704
  * Stream a `delivery: "live"` signal as SSE (records `read` on `signal:<name>`).
694
705
  *
695
706
  * HTTP `Last-Event-ID` is applied when `opts.afterId` is omitted. A missing
696
- * cursor throws OKE1014; `JsonStreamResult.ready` turns that into HTTP 410.
707
+ * cursor throws OKE1210; `JsonStreamResult.ready` turns that into HTTP 410.
697
708
  *
698
709
  * @param signal - Signal name or handle
699
710
  * @param opts - Payload filter and optional resume cursor
@@ -773,6 +784,14 @@ export interface Fx {
773
784
  * @param opts - Model routing opts
774
785
  */
775
786
  ask(prompt: NamedRef, input?: unknown, opts?: FxAskOptions): Promise<Record<string, unknown>>;
787
+ /**
788
+ * Embed text via a named model (records `embed` — distinct from `ask`).
789
+ * Returns the vector; does not write an index.
790
+ *
791
+ * @param model - Model name or handle
792
+ * @param text - Text to embed
793
+ */
794
+ embed(model: NamedRef, text: string): Promise<readonly number[]>;
776
795
  /**
777
796
  * Similarity search over an index/embed (records `read` on the embed ref).
778
797
  *
@@ -781,6 +800,17 @@ export interface Fx {
781
800
  * @param opts - Search options
782
801
  */
783
802
  search(embed: NamedRef, query: unknown, opts?: FxSearchOptions): Promise<unknown[]>;
803
+ /**
804
+ * Outbound HTTP to an arbitrary URL (records `fetch` on the hostname).
805
+ *
806
+ * Unconditionally external (`kind: "third-party"`). Declare the hostname in
807
+ * `effects.fetches`. Prefer wrapping inside {@link Fx.step}; use
808
+ * {@link Fx.retry} only when the remote API is safe to repeat.
809
+ *
810
+ * @param url - Absolute URL (string or URL)
811
+ * @param init - Standard `RequestInit`
812
+ */
813
+ fetch(url: string | URL, init?: RequestInit): Promise<Response>;
784
814
  /**
785
815
  * Run a bounded AI agent (records `ask`).
786
816
  *
@@ -1077,6 +1107,15 @@ function stampAskTelemetry(
1077
1107
  if (outputTokens > 0) telemetry.outputTokens += outputTokens;
1078
1108
  }
1079
1109
 
1110
+ /**
1111
+ * Hostname for `fx.fetch` capability / ledger resource.
1112
+ *
1113
+ * @param href - Absolute URL string
1114
+ */
1115
+ export function hostFromFetchUrl(href: string): string {
1116
+ return loadFxFetch().hostFromFetchUrl(href);
1117
+ }
1118
+
1080
1119
  /**
1081
1120
  * Create an in-memory `fx` context (v1 stubs, full surface).
1082
1121
  *
@@ -1162,9 +1201,12 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1162
1201
  kind: Parameters<CapabilityToken["assert"]>[0],
1163
1202
  resource: string,
1164
1203
  body: () => T | Promise<T>,
1204
+ externalOf?:
1205
+ | EffectExternal
1206
+ | ((result: T | undefined, error: unknown) => EffectExternal | undefined),
1165
1207
  ): Promise<T> {
1166
1208
  capability.assert(kind, resource);
1167
- const execute = () => recordEffect(ledger, kind, resource, now, body);
1209
+ const execute = () => recordEffect(ledger, kind, resource, now, body, externalOf);
1168
1210
  if (journal) {
1169
1211
  return journal.effect(kind, resource, execute);
1170
1212
  }
@@ -1265,14 +1307,15 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1265
1307
  table: unknown,
1266
1308
  body: () => T | Promise<T>,
1267
1309
  ): Promise<T> => {
1310
+ const externalOf = (): EffectExternal | undefined => cached?.external;
1268
1311
  const name = schemaTableName(table);
1269
1312
  if (name !== undefined) {
1270
1313
  const perTable = sqlTableRef(name);
1271
1314
  if (perTable !== ref && capability.allows(kind, perTable)) {
1272
- return gated(kind, perTable, body);
1315
+ return gated(kind, perTable, body, externalOf);
1273
1316
  }
1274
1317
  }
1275
- return gated(kind, ref, body);
1318
+ return gated(kind, ref, body, externalOf);
1276
1319
  };
1277
1320
 
1278
1321
  return {
@@ -1443,6 +1486,42 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1443
1486
  return h.page(table, pageOptions);
1444
1487
  });
1445
1488
  },
1489
+ search(table, searchOptions) {
1490
+ return gatedTable("read", table, async () => {
1491
+ const h = await ensure();
1492
+ const result = await h.search(table, searchOptions);
1493
+ // Optional rerank via fx.ask — only when explicitly requested.
1494
+ if (
1495
+ searchOptions.rerank &&
1496
+ typeof searchOptions.rerank === "object" &&
1497
+ searchOptions.rerank.model
1498
+ ) {
1499
+ const model = searchOptions.rerank.model;
1500
+ const pk = "id";
1501
+ const docs = result.data.map((row) => ({
1502
+ id: String(row[pk] ?? ""),
1503
+ text: Object.values(row)
1504
+ .filter((v) => typeof v === "string")
1505
+ .join("\n"),
1506
+ score: 0,
1507
+ }));
1508
+ const out = (await fx.ask(model, {
1509
+ query: searchOptions.query,
1510
+ docs,
1511
+ })) as { rankedIds?: string[] };
1512
+ if (out.rankedIds && out.rankedIds.length > 0) {
1513
+ const byId = new Map(result.data.map((r) => [String(r[pk] ?? ""), r]));
1514
+ return {
1515
+ ...result,
1516
+ data: out.rankedIds
1517
+ .map((id) => byId.get(id))
1518
+ .filter((r): r is NonNullable<typeof r> => r !== undefined),
1519
+ };
1520
+ }
1521
+ }
1522
+ return result;
1523
+ });
1524
+ },
1446
1525
  ensureTable(table) {
1447
1526
  return gatedTable("write", table, async () => {
1448
1527
  refuseDryRunWrite();
@@ -1555,17 +1634,28 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1555
1634
  }
1556
1635
  const isRead = prop === "get" || prop === "search" || prop === "list" || prop === "ttlMs";
1557
1636
  return (...args: unknown[]) =>
1558
- gated(isRead ? "read" : "write", baseRef, async () => {
1559
- if (!isRead && isDryRun()) {
1560
- throw new DryRunWriteIsolationError(
1561
- `Driver-backed store "${baseRef}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
1562
- );
1563
- }
1564
- const h = await open();
1565
- const fn = (h as unknown as Record<string | symbol, unknown>)[prop];
1566
- if (typeof fn !== "function") return undefined;
1567
- return (fn as (...a: unknown[]) => unknown).apply(h, args);
1568
- });
1637
+ gated(
1638
+ isRead ? "read" : "write",
1639
+ baseRef,
1640
+ async () => {
1641
+ if (!isRead && isDryRun()) {
1642
+ throw new DryRunWriteIsolationError(
1643
+ `Driver-backed store "${baseRef}" cannot isolate writes during dry-run; dry-run refused rather than risk a double-write.`,
1644
+ );
1645
+ }
1646
+ const h = await open();
1647
+ const fn = (h as unknown as Record<string | symbol, unknown>)[prop];
1648
+ if (typeof fn !== "function") return undefined;
1649
+ return (fn as (...a: unknown[]) => unknown).apply(h, args);
1650
+ },
1651
+ () => {
1652
+ const opened = cache.handle;
1653
+ if (opened && typeof opened === "object" && "external" in opened) {
1654
+ return (opened as { external?: EffectExternal }).external;
1655
+ }
1656
+ return undefined;
1657
+ },
1658
+ );
1569
1659
  },
1570
1660
  });
1571
1661
  }
@@ -1893,26 +1983,33 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1893
1983
  cache,
1894
1984
  send(template, opts) {
1895
1985
  const name = resolveName(template);
1896
- return gated("send", name, async () => {
1897
- // Dry-run: record "would have fired" — never contact a real channel
1898
- // (console §9.1 · §9.3 · §9.4).
1899
- if (isDryRun()) {
1900
- recordWouldHaveFired("send", name);
1986
+ let sendExternal: EffectExternal | undefined;
1987
+ return gated(
1988
+ "send",
1989
+ name,
1990
+ async () => {
1991
+ // Dry-run: record "would have fired" — never contact a real channel
1992
+ // (console §9.1 · §9.3 · §9.4).
1993
+ if (isDryRun()) {
1994
+ recordWouldHaveFired("send", name);
1995
+ return { ok: true as const };
1996
+ }
1997
+ if (options.channelRuntime) {
1998
+ const result = await options.channelRuntime.send(name, {
1999
+ to: opts?.to ?? "",
2000
+ data: opts?.data,
2001
+ via: opts?.via?.map(resolveName),
2002
+ locale: opts?.locale ?? locale,
2003
+ profileLocale: opts?.profileLocale,
2004
+ acceptLanguage: opts?.acceptLanguage,
2005
+ });
2006
+ sendExternal = result.external;
2007
+ return { ok: result.ok as true };
2008
+ }
1901
2009
  return { ok: true as const };
1902
- }
1903
- if (options.channelRuntime) {
1904
- const result = await options.channelRuntime.send(name, {
1905
- to: opts?.to ?? "",
1906
- data: opts?.data,
1907
- via: opts?.via?.map(resolveName),
1908
- locale: opts?.locale ?? locale,
1909
- profileLocale: opts?.profileLocale,
1910
- acceptLanguage: opts?.acceptLanguage,
1911
- });
1912
- return { ok: result.ok as true };
1913
- }
1914
- return { ok: true as const };
1915
- });
2010
+ },
2011
+ () => sendExternal,
2012
+ );
1916
2013
  },
1917
2014
  sendOtp(opts) {
1918
2015
  return gated("send", "sms-otp", async () => {
@@ -1982,28 +2079,51 @@ export function createFxContext(options: CreateFxOptions): FxContext {
1982
2079
  },
1983
2080
  ask(prompt, input, opts) {
1984
2081
  const name = resolveName(prompt);
1985
- return gated("ask", name, async () => {
1986
- // Dry-run: stub the model call the same way as send.
1987
- if (isDryRun()) {
1988
- recordWouldHaveFired("ask", name);
1989
- return {};
1990
- }
1991
- if (options.aiRuntime) {
1992
- try {
1993
- return await options.aiRuntime.ask(name, input, {
1994
- via: opts?.via?.map(resolveName),
1995
- ...(opts?.timeout !== undefined ? { timeout: opts.timeout } : {}),
1996
- tools: opts?.tools?.map(resolveName),
1997
- maxSteps: opts?.maxSteps,
1998
- // Host fx.call — same capability / ledger / Runs path as any call.
1999
- callTool: (tool, toolInput) => fx.call(tool, toolInput),
2000
- });
2001
- } finally {
2002
- stampAskTelemetry(telemetry, options.aiRuntime, name);
2082
+ return gated(
2083
+ "ask",
2084
+ name,
2085
+ async () => {
2086
+ // Dry-run: stub the model call the same way as send.
2087
+ if (isDryRun()) {
2088
+ recordWouldHaveFired("ask", name);
2089
+ return {};
2003
2090
  }
2004
- }
2005
- throw new Error(`fx.ask: AI runtime is not configured for prompt "${name}"`);
2006
- });
2091
+ if (options.aiRuntime) {
2092
+ try {
2093
+ return await options.aiRuntime.ask(name, input, {
2094
+ via: opts?.via?.map(resolveName),
2095
+ ...(opts?.timeout !== undefined ? { timeout: opts.timeout } : {}),
2096
+ tools: opts?.tools?.map(resolveName),
2097
+ maxSteps: opts?.maxSteps,
2098
+ // Host fx.call — same capability / ledger / Runs path as any call.
2099
+ callTool: (tool, toolInput) => fx.call(tool, toolInput),
2100
+ });
2101
+ } finally {
2102
+ stampAskTelemetry(telemetry, options.aiRuntime, name);
2103
+ }
2104
+ }
2105
+ throw new Error(`fx.ask: AI runtime is not configured for prompt "${name}"`);
2106
+ },
2107
+ () => options.aiRuntime?.lastExternal,
2108
+ );
2109
+ },
2110
+ embed(model, text) {
2111
+ const name = resolveName(model);
2112
+ return gated(
2113
+ "embed",
2114
+ name,
2115
+ async () => {
2116
+ if (isDryRun()) {
2117
+ recordWouldHaveFired("embed", name);
2118
+ return [];
2119
+ }
2120
+ if (options.aiRuntime && typeof options.aiRuntime.embedVector === "function") {
2121
+ return options.aiRuntime.embedVector(name, text);
2122
+ }
2123
+ throw new Error(`fx.embed: AI runtime is not configured for model "${name}"`);
2124
+ },
2125
+ () => options.aiRuntime?.lastExternal,
2126
+ );
2007
2127
  },
2008
2128
  search(embed, query, opts) {
2009
2129
  const name = resolveName(embed);
@@ -2029,6 +2149,9 @@ export function createFxContext(options: CreateFxOptions): FxContext {
2029
2149
  return [];
2030
2150
  });
2031
2151
  },
2152
+ fetch(url, init) {
2153
+ return loadFxFetch().runFxFetch(gated, url, init);
2154
+ },
2032
2155
  run(agent, input) {
2033
2156
  const name = resolveName(agent);
2034
2157
  return gated("ask", name, async () => {
@@ -104,7 +104,7 @@ const signalRt = createSignalRuntime({
104
104
  durablePath: signalPath,
105
105
  leaseMs: Number.isFinite(leaseMs) ? leaseMs : 300,
106
106
  });
107
- const job = signal(SIG, { delivery: "once", retries: 3, deadLetter: true, optional: true });
107
+ const job = signal.once(SIG, { retries: 3, deadLetter: true, optional: true });
108
108
  signalRt.register(job);
109
109
  const bus = await signalRt.start();
110
110
  await bus.subscribe(SIG, `consumer-${instanceId}`, async (msg) => {
@@ -19,16 +19,14 @@ describe("http.query — RFC 10008", () => {
19
19
  test("a QUERY flow receives the JSON body exactly as POST does", async () => {
20
20
  const schema = z.object({ n: z.number() });
21
21
  on(
22
- http.query("/q"),
22
+ http.query("/q", { in: schema }),
23
23
  flow("q.run", {
24
- in: schema,
25
24
  do: (input: { n: number }) => input,
26
25
  }),
27
26
  );
28
27
  on(
29
- http.post("/p"),
28
+ http.post("/p", { in: schema }),
30
29
  flow("p.run", {
31
- in: schema,
32
30
  do: (input: { n: number }) => input,
33
31
  }),
34
32
  );
@@ -70,9 +68,8 @@ describe("http.query — RFC 10008", () => {
70
68
  describe("http.query — RFC 10008 media type", () => {
71
69
  function queryApp(aot = true) {
72
70
  on(
73
- http.query("/q"),
71
+ http.query("/q", { in: z.object({ n: z.number() }) }),
74
72
  flow("q.run", {
75
- in: z.object({ n: z.number() }),
76
73
  do: (input: { n: number }) => input,
77
74
  }),
78
75
  );
@@ -91,6 +91,7 @@ export {
91
91
  recordEffect,
92
92
  reversibilityOf,
93
93
  type EffectEntry,
94
+ type EffectExternal,
94
95
  type EffectKind,
95
96
  type EffectLedger,
96
97
  type ReversibilityTier,
@@ -107,6 +108,7 @@ export {
107
108
  fail,
108
109
  formatOkeMessage,
109
110
  lookupOkeError,
111
+ OKE_ERROR_RANGES,
110
112
  OKE_ERRORS,
111
113
  OkeError,
112
114
  throwOke,
@@ -115,6 +117,7 @@ export {
115
117
  type FlowFailure,
116
118
  type OkeErrorCode,
117
119
  type OkeErrorDefinition,
120
+ type OkeErrorDomain,
118
121
  type OkeErrorParams,
119
122
  } from "./errors.ts";
120
123
 
@@ -133,6 +136,14 @@ export {
133
136
  type StandardSchemaV1,
134
137
  } from "./flow.ts";
135
138
 
139
+ export { call } from "./call.ts";
140
+ export type {
141
+ BoundaryContract,
142
+ InferBoundaryErrors,
143
+ InferBoundaryIn,
144
+ InferBoundaryOut,
145
+ } from "./boundary-contract.ts";
146
+
136
147
  export {
137
148
  createFx,
138
149
  createFxContext,
@@ -346,15 +357,15 @@ export {
346
357
 
347
358
  export {
348
359
  asSignalTrigger,
349
- every,
350
360
  http,
351
361
  internal,
362
+ isClockDecl,
352
363
  isResourceMount,
353
364
  isSignalTriggerSource,
354
365
  normalizeTrigger,
355
- table,
356
366
  type BoundTriggerOf,
357
367
  type CdcTrigger,
368
+ type ClockTrigger,
358
369
  type EveryTrigger,
359
370
  type GateRef,
360
371
  type HttpMethod,
@@ -365,7 +376,6 @@ export {
365
376
  type ResourceMount,
366
377
  type SignalAsTrigger,
367
378
  type SignalSource,
368
- type TableHandle,
369
379
  type Trigger,
370
380
  type TriggerKind,
371
381
  } from "./triggers.ts";
@@ -12,7 +12,7 @@ export const INSTANCE_ID_PREFIX = "inst-";
12
12
  * Mint a process instance id (`inst-<okid>`).
13
13
  */
14
14
  export function mintInstanceId(): string {
15
- return `${INSTANCE_ID_PREFIX}${okid()}`;
15
+ return okid({ prefix: INSTANCE_ID_PREFIX });
16
16
  }
17
17
 
18
18
  /**
@@ -11,7 +11,9 @@ import { INSTANCE_ID_PREFIX } from "./instance-id.ts";
11
11
  import {
12
12
  createInstanceRuntime,
13
13
  createMemoryInstanceStore,
14
+ isBenignSqlDisconnect,
14
15
  projectInstancesList,
16
+ type InstanceStore,
15
17
  } from "./instances.ts";
16
18
 
17
19
  describe("projectInstancesList", () => {
@@ -64,6 +66,44 @@ describe("instance heartbeat", () => {
64
66
  expect(second?.heartbeatAt).toBe(1_100);
65
67
  expect(second?.startedAt).toBe(first?.startedAt);
66
68
  });
69
+
70
+ test("heartbeat swallows paused / connection-closed SQL errors", async () => {
71
+ const store: InstanceStore = {
72
+ kind: "memory",
73
+ async upsert() {
74
+ const err = new Error("shared Postgres pools are paused (compose stopped)");
75
+ (err as { code?: string; name?: string }).code = "ERR_OKE_POSTGRES_PAUSED";
76
+ (err as { name?: string }).name = "SharedPostgresPausedError";
77
+ throw err;
78
+ },
79
+ async get() {
80
+ return undefined;
81
+ },
82
+ async list() {
83
+ return [];
84
+ },
85
+ async listAlive() {
86
+ return [];
87
+ },
88
+ async remove() {},
89
+ };
90
+ const rt = createInstanceRuntime({
91
+ instanceId: "inst-pause",
92
+ store,
93
+ env: "dev",
94
+ heartbeatMs: 1,
95
+ leaseMs: 300,
96
+ });
97
+ await expect(rt.heartbeat()).resolves.toBeUndefined();
98
+ });
99
+ });
100
+
101
+ describe("isBenignSqlDisconnect", () => {
102
+ test("matches pause and Bun connection codes", () => {
103
+ expect(isBenignSqlDisconnect({ code: "ERR_OKE_POSTGRES_PAUSED" })).toBe(true);
104
+ expect(isBenignSqlDisconnect({ code: "ERR_POSTGRES_CONNECTION_CLOSED" })).toBe(true);
105
+ expect(isBenignSqlDisconnect(new Error("relation missing"))).toBe(false);
106
+ });
67
107
  });
68
108
 
69
109
  describe("boot instanceId unification", () => {
@@ -188,6 +188,38 @@ export interface InstanceRuntime {
188
188
  close(): Promise<void>;
189
189
  }
190
190
 
191
+ /**
192
+ * SQL failures that are expected while `oke dev` pauses shared Postgres for
193
+ * compose stop (or the server is already gone). Scheduler ticks must not dump
194
+ * these as unhandled rejections.
195
+ *
196
+ * @param err - Rejection / throw
197
+ */
198
+ export function isBenignSqlDisconnect(err: unknown): boolean {
199
+ if (!err || typeof err !== "object") return false;
200
+ const code = (err as { code?: unknown }).code;
201
+ if (typeof code === "string") {
202
+ if (
203
+ code === "ERR_OKE_POSTGRES_PAUSED" ||
204
+ code === "ERR_POSTGRES_CONNECTION_CLOSED" ||
205
+ code === "ERR_POSTGRES_CONNECTION_REFUSED" ||
206
+ code === "ERR_POSTGRES_CONNECTION_TIMEOUT"
207
+ ) {
208
+ return true;
209
+ }
210
+ }
211
+ const name = (err as { name?: unknown }).name;
212
+ if (name === "SharedPostgresPausedError") return true;
213
+ const message = (err as { message?: unknown }).message;
214
+ if (typeof message !== "string") return false;
215
+ return (
216
+ message === "Connection closed" ||
217
+ /shared Postgres pools are paused/i.test(message) ||
218
+ /Failed to connect/i.test(message) ||
219
+ /ECONNREFUSED/i.test(message)
220
+ );
221
+ }
222
+
191
223
  /**
192
224
  * Create a heartbeat runtime for one process.
193
225
  *
@@ -214,15 +246,21 @@ export function createInstanceRuntime(options: CreateInstanceRuntimeOptions): In
214
246
  },
215
247
  async heartbeat(at) {
216
248
  const t = at ?? now();
217
- await options.store.upsert({
218
- id: options.instanceId,
219
- startedAt,
220
- heartbeatAt: t,
221
- leaseExpiresAt: t + leaseMs,
222
- env: options.env,
223
- pid,
224
- });
225
- lastHeartbeatAt = t;
249
+ try {
250
+ await options.store.upsert({
251
+ id: options.instanceId,
252
+ startedAt,
253
+ heartbeatAt: t,
254
+ leaseExpiresAt: t + leaseMs,
255
+ env: options.env,
256
+ pid,
257
+ });
258
+ lastHeartbeatAt = t;
259
+ } catch (err) {
260
+ // Compose-stop pause / torn-down Postgres — skip until pools resume.
261
+ if (isBenignSqlDisconnect(err)) return;
262
+ throw err;
263
+ }
226
264
  },
227
265
  async release() {
228
266
  await options.store.remove(options.instanceId);