okengine 0.18.4 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Gate tests for EffectEntry.external + fx.fetch + driver-reported egress.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { openOpenaiCompatible } from "../drivers/ai-openai-compatible.ts";
7
+ import { openAnthropic } from "../drivers/ai-anthropic.ts";
8
+ import { connectPglite } from "../drivers/pglite.ts";
9
+ import { createChannelRuntime } from "../elements/channel/runtime.ts";
10
+ import { createFxContext } from "./fx.ts";
11
+ import { withDryRun } from "./dry-run.ts";
12
+
13
+ describe("EffectEntry.external — optional / additive", () => {
14
+ test("ledger entries without external stay five-field shaped", async () => {
15
+ const { fx, ledger } = createFxContext({
16
+ flow: "local.read",
17
+ effects: { reads: ["sql:notes"] },
18
+ });
19
+ await (fx.store("sql:notes") as { get(key: string): Promise<unknown> }).get("n1");
20
+ const entry = ledger.entries[0];
21
+ expect(entry).toMatchObject({
22
+ kind: "read",
23
+ resource: "sql:notes",
24
+ reversibility: "none",
25
+ });
26
+ expect(entry?.external).toBeUndefined();
27
+ });
28
+ });
29
+
30
+ describe("fx.fetch", () => {
31
+ test("records fetch kind with third-party host from URL", async () => {
32
+ const originalFetch = globalThis.fetch;
33
+ globalThis.fetch = (async () => new Response("ok", { status: 200 })) as unknown as typeof fetch;
34
+ try {
35
+ const { fx, ledger } = createFxContext({
36
+ flow: "stripe.pull",
37
+ effects: { fetches: ["api.stripe.com"] },
38
+ });
39
+ const res = await fx.fetch("https://api.stripe.com/v1/charges");
40
+ expect(res.status).toBe(200);
41
+ expect(ledger.entries).toHaveLength(1);
42
+ expect(ledger.entries[0]).toMatchObject({
43
+ kind: "fetch",
44
+ resource: "api.stripe.com",
45
+ reversibility: "irreversible",
46
+ external: { host: "api.stripe.com", kind: "third-party" },
47
+ });
48
+ } finally {
49
+ globalThis.fetch = originalFetch;
50
+ }
51
+ });
52
+
53
+ test("undeclared host throws UNDECLARED_FETCH", async () => {
54
+ const { fx } = createFxContext({
55
+ flow: "stripe.pull",
56
+ effects: { fetches: ["api.example.com"] },
57
+ });
58
+ await expect(fx.fetch("https://api.stripe.com/v1")).rejects.toMatchObject({
59
+ code: 1008,
60
+ });
61
+ });
62
+
63
+ test("dry-run stubs fetch without contacting the network", async () => {
64
+ let called = false;
65
+ const originalFetch = globalThis.fetch;
66
+ globalThis.fetch = (async () => {
67
+ called = true;
68
+ return new Response("nope");
69
+ }) as unknown as typeof fetch;
70
+ try {
71
+ const { fx, ledger } = createFxContext({
72
+ flow: "stripe.pull",
73
+ effects: { fetches: ["api.stripe.com"] },
74
+ });
75
+ const { wouldHaveFired } = await withDryRun(async () => {
76
+ await fx.fetch("https://api.stripe.com/v1");
77
+ });
78
+ expect(called).toBe(false);
79
+ expect(wouldHaveFired).toEqual([{ kind: "fetch", resource: "api.stripe.com" }]);
80
+ expect(ledger.entries[0]?.external).toMatchObject({
81
+ host: "api.stripe.com",
82
+ kind: "third-party",
83
+ });
84
+ } finally {
85
+ globalThis.fetch = originalFetch;
86
+ }
87
+ });
88
+ });
89
+
90
+ describe("Channel failover — winning provider on EffectEntry", () => {
91
+ test("send stamps external.provider from the winning attempt, not the primary binding", async () => {
92
+ const { channel } = await import("../elements/channel.ts");
93
+ const { driverFromTransport, failingTransport, okTransport } =
94
+ await import("../elements/channel/test-helpers.ts");
95
+ const smtp = {
96
+ ...driverFromTransport("smtp", failingTransport("smtp")),
97
+ external: {
98
+ host: "mail.example.internal",
99
+ provider: "smtp",
100
+ kind: "infrastructure" as const,
101
+ },
102
+ };
103
+ const resend = {
104
+ ...driverFromTransport("resend", okTransport("resend")),
105
+ external: {
106
+ host: "api.resend.com",
107
+ provider: "resend",
108
+ kind: "third-party" as const,
109
+ },
110
+ };
111
+ const channelRuntime = createChannelRuntime({
112
+ templates: [channel.template("welcome", { medium: "email" })],
113
+ drivers: [smtp, resend],
114
+ catalog: { welcome: { en: { subject: "Hi", text: "Hello" } } },
115
+ });
116
+ const { fx, ledger } = createFxContext({
117
+ flow: "mail.send",
118
+ effects: { sends: ["welcome"] },
119
+ channelRuntime,
120
+ });
121
+ await fx.send("welcome", { to: "a@b.com", via: ["smtp", "resend"] });
122
+ const send = ledger.entries.find((e) => e.kind === "send");
123
+ expect(send?.external?.provider).toBe("resend");
124
+ expect(send?.external?.host).toBe("api.resend.com");
125
+ expect(send?.external?.kind).toBe("third-party");
126
+ });
127
+ });
128
+
129
+ describe("Store — in-process PGlite never carries external", () => {
130
+ test("pglite connection omits external", async () => {
131
+ const conn = await connectPglite({ url: "memory://" });
132
+ expect(conn.driverId).toBe("pglite");
133
+ expect(conn.external).toBeUndefined();
134
+ await conn.close?.();
135
+ });
136
+ });
137
+
138
+ describe("AI — infrastructure vs third-party", () => {
139
+ test("self-hosted openai-compatible reports infrastructure when opened with that kind", async () => {
140
+ const originalFetch = globalThis.fetch;
141
+ globalThis.fetch = (async () =>
142
+ new Response(
143
+ JSON.stringify({
144
+ choices: [{ message: { content: "hi" } }],
145
+ model: "llama3",
146
+ usage: { prompt_tokens: 1, completion_tokens: 1 },
147
+ }),
148
+ { status: 200, headers: { "content-type": "application/json" } },
149
+ )) as unknown as typeof fetch;
150
+ try {
151
+ const client = await openOpenaiCompatible({
152
+ baseUrl: "http://127.0.0.1:11434/v1",
153
+ external: { kind: "infrastructure", provider: "local" },
154
+ });
155
+ const result = await client.complete({
156
+ messages: [{ role: "user", content: "hi" }],
157
+ });
158
+ expect(result.external).toEqual({
159
+ host: "127.0.0.1",
160
+ kind: "infrastructure",
161
+ provider: "local",
162
+ });
163
+ } finally {
164
+ globalThis.fetch = originalFetch;
165
+ }
166
+ });
167
+
168
+ test("anthropic cloud reports third-party", async () => {
169
+ const originalFetch = globalThis.fetch;
170
+ globalThis.fetch = (async () =>
171
+ new Response(
172
+ JSON.stringify({
173
+ content: [{ type: "text", text: "hi" }],
174
+ model: "claude",
175
+ usage: { input_tokens: 1, output_tokens: 1 },
176
+ }),
177
+ { status: 200, headers: { "content-type": "application/json" } },
178
+ )) as unknown as typeof fetch;
179
+ try {
180
+ const client = await openAnthropic({
181
+ apiKey: "sk-test",
182
+ baseUrl: "https://api.anthropic.com",
183
+ });
184
+ const result = await client.complete({
185
+ messages: [{ role: "user", content: "hi" }],
186
+ });
187
+ expect(result.external).toEqual({
188
+ host: "api.anthropic.com",
189
+ kind: "third-party",
190
+ provider: "anthropic",
191
+ });
192
+ } finally {
193
+ globalThis.fetch = originalFetch;
194
+ }
195
+ });
196
+ });
@@ -1,8 +1,10 @@
1
1
  import { describe, expect, test, beforeEach } from "bun:test";
2
+ import { clock } from "../elements/clock/declare.ts";
3
+ import { field, store } from "../elements/store.ts";
2
4
  import { oke } from "./app.ts";
3
5
  import { flow, isFlow, resetFlowSeq } from "./flow.ts";
4
6
  import { on, resetBindings } from "./on.ts";
5
- import { every, http, internal, table } from "./triggers.ts";
7
+ import { http, internal } from "./triggers.ts";
6
8
 
7
9
  beforeEach(() => {
8
10
  resetBindings();
@@ -71,8 +73,9 @@ describe("five trigger kinds", () => {
71
73
  }),
72
74
  );
73
75
 
76
+ const hourly = clock("hourly", { every: "1h" });
74
77
  on(
75
- every("1h"),
78
+ hourly,
76
79
  flow("t.every", {
77
80
  do: () => {
78
81
  seen.push("every");
@@ -89,8 +92,14 @@ describe("five trigger kinds", () => {
89
92
  }),
90
93
  );
91
94
 
95
+ const db = store.sql("main");
96
+ const orders = store.schema.table("orders", {
97
+ id: field.text().primaryKey(),
98
+ status: field.text(),
99
+ });
100
+
92
101
  on(
93
- table("orders").changed("status"),
102
+ db.table(orders).changed("status"),
94
103
  flow("t.cdc", {
95
104
  do: (p: { before: { status: string }; after: { status: string } }) => {
96
105
  seen.push(`cdc:${p.before.status}->${p.after.status}`);
@@ -114,7 +123,7 @@ describe("five trigger kinds", () => {
114
123
  expect(res.status).toBe(200);
115
124
  expect(await res.json()).toEqual({ data: { code: "abc" }, error: null });
116
125
 
117
- await app.dispatchEvery("1h");
126
+ await app.dispatchEvery("hourly");
118
127
  await app.dispatchSignal("link-clicked", { code: "sa" });
119
128
  await app.dispatchCdc(
120
129
  "orders",
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * `flow` — the one species of backend behavior.
3
3
  *
4
- * A Flow is a typed contract (`in` / `out` / `errors`) plus a `do` body.
5
- * Triggers are attached by {@link on}; a flow with no trigger is still a
6
- * Flow and is callable via `fx.call` (unified-theory §4, Linkly ⑤).
4
+ * A Flow is a `do` body plus runtime options. Invoke contracts (`in` / `out` /
5
+ * `errors` / `breaking`) are authored on the exposure (`http.*`, `call`,
6
+ * `mcp.tool`) and stamped onto the FlowDef for Manifest / validation.
7
+ * Triggers are attached by {@link on}; {@link call} is the call-only sugar.
7
8
  */
8
9
 
9
10
  import type { Effects, FlowPlane, Slo } from "../manifest/types.ts";
10
- import type { InferSchemaOutput, SchemaInput } from "../validation/standard-schema.ts";
11
+ import type { SchemaInput } from "../validation/standard-schema.ts";
11
12
  import type { FxRetryOptions } from "./concurrency.ts";
12
13
  import type { FlowFailure } from "./errors.ts";
13
14
  import type { Fx } from "./fx.ts";
@@ -22,7 +23,7 @@ export type StandardSchemaResult<Output> =
22
23
  | { readonly value: Output; readonly issues?: undefined }
23
24
  | { readonly issues: ReadonlyArray<{ readonly message: string }> };
24
25
 
25
- /** Map of declared flow-boundary error names to their schemas. */
26
+ /** Map of declared boundary error names to their schemas. */
26
27
  export type FlowErrorMap = Readonly<Record<string, SchemaInput>>;
27
28
 
28
29
  /**
@@ -36,22 +37,12 @@ export type FlowHandler<I = unknown, O = unknown> = (
36
37
  fx: Fx,
37
38
  ) => O | FlowFailure | Promise<O | FlowFailure>;
38
39
 
39
- /** Options for {@link flow}. */
40
- export interface FlowOptions<I = unknown, O = unknown, E extends FlowErrorMap = FlowErrorMap> {
41
- /** Input schema (Standard Schema when present). */
42
- readonly in?: SchemaInput;
43
- /** Output schema (Standard Schema when present). */
44
- readonly out?: SchemaInput;
45
- /** Declared typed errors. */
46
- readonly errors?: E;
40
+ /** Runtime / behavior options for {@link flow} (no invoke contract). */
41
+ export interface FlowOptions<I = unknown, O = unknown> {
47
42
  /** Declared effects (capability token). Inferred later by AoT. */
48
43
  readonly effects?: Effects;
49
44
  /** Journal every effect call. */
50
45
  readonly durable?: boolean;
51
- /**
52
- * Retry the whole `do` body on thrown errors (same journal session when
53
- * durable). Prefer {@link Fx.retry} inside {@link Fx.step} for fine control.
54
- */
55
46
  /**
56
47
  * Retry the whole `do` body on thrown errors (same journal session when
57
48
  * durable). Prefer {@link Fx.retry} inside {@link Fx.step} for fine control.
@@ -70,11 +61,6 @@ export interface FlowOptions<I = unknown, O = unknown, E extends FlowErrorMap =
70
61
  * Cross-plane invocation is a compile error.
71
62
  */
72
63
  readonly plane?: FlowPlane;
73
- /**
74
- * Acknowledge intentional Manifest contract breaks for this flow
75
- * (`oke doctor --diff` / CI gate).
76
- */
77
- readonly breaking?: boolean;
78
64
  /**
79
65
  * Compensation phase after terminal failure on a durable flow.
80
66
  * Runs under the same journal session after auto per-step `{ undo }`
@@ -96,42 +82,32 @@ export interface FlowOptions<I = unknown, O = unknown, E extends FlowErrorMap =
96
82
  * Default `true` when `gate.auth.tenant` is on.
97
83
  */
98
84
  readonly tenantScoped?: boolean;
85
+ /**
86
+ * Mark this HTTP flow as returning `text/event-stream` via `fx.json.stream`
87
+ * (not signal live). Stamps `$routes.stream` for the typed client.
88
+ */
89
+ readonly stream?: true;
99
90
  /** The behavior. */
100
91
  readonly do: FlowHandler<I, O>;
101
92
  }
102
93
 
103
94
  /**
104
- * Infer validated input from a flow options bag.
105
- *
106
- * @typeParam Opts - {@link FlowOptions} / call-site object
107
- */
108
- export type InferFlowIn<Opts> = Opts extends { readonly in: infer S }
109
- ? InferSchemaOutput<S>
110
- : Opts extends { readonly do: FlowHandler<infer I, infer _O> }
111
- ? I
112
- : unknown;
113
-
114
- /**
115
- * Infer success output from a flow options bag.
95
+ * Infer validated input from a flow options bag (handler signature).
116
96
  *
117
97
  * @typeParam Opts - {@link FlowOptions} / call-site object
118
98
  */
119
- export type InferFlowOut<Opts> = Opts extends { readonly out: infer S }
120
- ? InferSchemaOutput<S>
121
- : Opts extends { readonly do: FlowHandler<infer _I, infer O> }
122
- ? O
123
- : unknown;
99
+ export type InferFlowIn<Opts> = Opts extends { readonly do: FlowHandler<infer I, infer _O> }
100
+ ? I
101
+ : unknown;
124
102
 
125
103
  /**
126
- * Infer the error schema map from a flow options bag.
104
+ * Infer success output from a flow options bag (handler signature).
127
105
  *
128
106
  * @typeParam Opts - {@link FlowOptions} / call-site object
129
107
  */
130
- export type InferFlowErrors<Opts> = Opts extends {
131
- readonly errors: infer E extends FlowErrorMap;
132
- }
133
- ? E
134
- : {};
108
+ export type InferFlowOut<Opts> = Opts extends { readonly do: FlowHandler<infer _I, infer O> }
109
+ ? O
110
+ : unknown;
135
111
 
136
112
  /**
137
113
  * Phantom brand for the bound trigger type parameter. Optional so
@@ -142,6 +118,9 @@ declare const triggerPhantom: unique symbol;
142
118
  /**
143
119
  * A Flow definition — one species, trigger-agnostic until {@link on} binds.
144
120
  *
121
+ * `in` / `out` / `errors` / `breaking` are **projections** stamped from the
122
+ * exposure (`http.*` / `call` / `mcp.tool` / signal schema), not authored here.
123
+ *
145
124
  * @typeParam I - Input type
146
125
  * @typeParam O - Output type
147
126
  * @typeParam E - Error map (schemas)
@@ -166,12 +145,12 @@ export interface FlowDef<
166
145
  readonly name: string;
167
146
  /** Unit scope — derived from `name`'s first dot segment (e.g. `"auth.refresh"` → `"auth"`). */
168
147
  readonly unit: string | undefined;
169
- /** Input schema. */
170
- readonly in: SchemaInput | undefined;
171
- /** Output schema. */
172
- readonly out: SchemaInput | undefined;
173
- /** Declared errors. */
174
- readonly errors: E | undefined;
148
+ /** Input schema (stamped from exposure). */
149
+ in: SchemaInput | undefined;
150
+ /** Output schema (stamped from exposure). */
151
+ out: SchemaInput | undefined;
152
+ /** Declared errors (stamped from exposure). */
153
+ errors: E | undefined;
175
154
  /** Declared effects. */
176
155
  readonly effects: Effects | undefined;
177
156
  /** Durability flag. */
@@ -193,13 +172,17 @@ export interface FlowDef<
193
172
  readonly slo: Slo | undefined;
194
173
  /** Plane (user vs operator). */
195
174
  readonly plane: FlowPlane | undefined;
196
- /** Intentional contract-break acknowledgement for Manifest Diff. */
197
- readonly breaking: boolean;
175
+ /** Intentional contract-break acknowledgement (stamped from exposure). */
176
+ breaking: boolean;
198
177
  /**
199
178
  * When `false`, this flow is tenant-unaware (no tenant-role scope union).
200
179
  * Default `true` when `gate.auth.tenant` is on.
201
180
  */
202
181
  readonly tenantScoped: boolean | undefined;
182
+ /**
183
+ * True when this HTTP flow returns SSE via `fx.json.stream` (not live signal).
184
+ */
185
+ readonly stream: true | undefined;
203
186
  /**
204
187
  * Optional compensation phase after terminal durable failure.
205
188
  * See {@link FlowOptions.compensate}.
@@ -253,38 +236,53 @@ export interface FlowDef<
253
236
  export const flowBrand: unique symbol = Symbol("oke.flow");
254
237
 
255
238
  /**
256
- * Define a Flow — the one species of backend behavior.
257
- *
258
- * Input / output types are inferred from Standard Schema `in` / `out` when
259
- * present; otherwise from the `do` handler signature.
239
+ * Define a Flow — behavior only. Author invoke contracts on `http.*` /
240
+ * {@link call} / `mcp.tool`, not here.
260
241
  *
261
242
  * A nameless `flow({ do })` is stamped `unit.export` by the file-tree
262
243
  * generator, {@link unit}, or `.adopt()`. Explicit `flow("notes.get", {…})`
263
244
  * still wins.
264
245
  *
265
- * @param options - Contract and handler (name stamped later)
246
+ * @param options - Runtime options and handler (name stamped later)
266
247
  */
267
- export function flow<Opts extends FlowOptions<any, any, any>>(
248
+ export function flow<Opts extends FlowOptions<any, any>>(
268
249
  options: Opts,
269
- ): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>, InferFlowErrors<Opts>>;
250
+ ): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>>;
270
251
  /**
271
252
  * @param name - Stable name (used by `fx.call` and the Manifest)
272
- * @param options - Contract and handler
253
+ * @param options - Runtime options and handler
273
254
  */
274
- export function flow<Opts extends FlowOptions<any, any, any>>(
255
+ export function flow<Opts extends FlowOptions<any, any>>(
275
256
  name: string,
276
257
  options: Opts & { readonly name?: never },
277
- ): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>, InferFlowErrors<Opts>>;
258
+ ): FlowDef<InferFlowIn<Opts>, InferFlowOut<Opts>>;
278
259
  export function flow(
279
- nameOrOptions: string | FlowOptions<any, any, any>,
280
- maybeOptions?: FlowOptions<any, any, any> & { readonly name?: never },
260
+ nameOrOptions: string | FlowOptions<any, any>,
261
+ maybeOptions?: FlowOptions<any, any> & { readonly name?: never },
281
262
  ): FlowDef {
282
263
  const named = typeof nameOrOptions === "string";
283
264
  const name = named ? nameOrOptions : "";
284
- const options = (named ? maybeOptions : nameOrOptions) as FlowOptions<any, any, any> | undefined;
265
+ const options = (named ? maybeOptions : nameOrOptions) as FlowOptions<any, any> | undefined;
285
266
  if (!options || typeof options.do !== "function") {
286
267
  throw new TypeError("flow() expected an options bag with a do handler");
287
268
  }
269
+ // Reject legacy contract keys so misuse fails loud at definition time.
270
+ const legacy = options as FlowOptions<any, any> & {
271
+ in?: unknown;
272
+ out?: unknown;
273
+ errors?: unknown;
274
+ breaking?: unknown;
275
+ };
276
+ if (
277
+ legacy.in !== undefined ||
278
+ legacy.out !== undefined ||
279
+ legacy.errors !== undefined ||
280
+ legacy.breaking !== undefined
281
+ ) {
282
+ throw new TypeError(
283
+ "flow() no longer accepts in/out/errors/breaking — author the contract on http.*/call/mcp.tool",
284
+ );
285
+ }
288
286
 
289
287
  const triggers: Trigger[] = [];
290
288
  const hooks: Partial<Record<HookStage, HookFn[]>> = {};
@@ -297,9 +295,9 @@ export function flow(
297
295
  [flowBrand]: true,
298
296
  name,
299
297
  unit,
300
- in: options.in,
301
- out: options.out,
302
- errors: (options.errors ?? undefined) as FlowErrorMap | undefined,
298
+ in: undefined,
299
+ out: undefined,
300
+ errors: undefined,
303
301
  effects: options.effects,
304
302
  durable: options.durable ?? false,
305
303
  retry: options.retry,
@@ -308,8 +306,9 @@ export function flow(
308
306
  cache: options.cache,
309
307
  slo: options.slo,
310
308
  plane: options.plane,
311
- breaking: options.breaking ?? false,
309
+ breaking: false,
312
310
  tenantScoped: options.tenantScoped,
311
+ stream: options.stream,
313
312
  compensate: options.compensate as FlowDef["compensate"],
314
313
  do: options.do as FlowHandler,
315
314
  triggers,
@@ -323,7 +322,6 @@ export function flow(
323
322
  },
324
323
  plug(pluginDef) {
325
324
  pendingPlugins.push(pluginDef);
326
- // Decorations accumulate on the interface; runtime object is unchanged.
327
325
  return def as never;
328
326
  },
329
327
  };
@@ -8,12 +8,12 @@ import { z } from "zod";
8
8
  import { ai, createAiRuntime } from "../elements/ai.ts";
9
9
  import { collectWideEvent } from "../runs/collect.ts";
10
10
  import { createEffectLedger } from "./effects.ts";
11
- import { flow } from "./flow.ts";
11
+ import { call } from "./call.ts";
12
12
  import { createFx } from "./fx.ts";
13
13
  import { createRunTelemetry } from "./run-telemetry.ts";
14
14
  import { internal } from "./triggers.ts";
15
15
 
16
- const triageFlow = flow("support.triage", {
16
+ const triageFlow = call("support.triage", {
17
17
  in: z.object({}),
18
18
  do: () => ({ ok: true }),
19
19
  });
@@ -6,7 +6,7 @@ import { createFx, createFxContext, signalReadRef } from "./fx.ts";
6
6
 
7
7
  describe("fx.deadLetters", () => {
8
8
  test("returns dead-lettered messages for a declared signal read", async () => {
9
- const notify = signal("notify", { delivery: "once", retries: 0, deadLetter: true });
9
+ const notify = signal.once("notify", { retries: 0, deadLetter: true });
10
10
  const runtime = openRuntime(notify);
11
11
  const bus = await runtime.start();
12
12
  await bus.subscribe("notify", "c1", async () => {
@@ -33,8 +33,8 @@ describe("fx.deadLetters", () => {
33
33
  });
34
34
 
35
35
  test("undeclared and cross-signal reads throw OKE1001", async () => {
36
- const notify = signal("notify", { delivery: "once", retries: 0, deadLetter: true });
37
- const other = signal("other", { delivery: "once", retries: 0, deadLetter: true });
36
+ const notify = signal.once("notify", { retries: 0, deadLetter: true });
37
+ const other = signal.once("other", { retries: 0, deadLetter: true });
38
38
  const runtime = openRuntime(notify);
39
39
  const fx = createFx({
40
40
  flow: "notifications.failed",
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `fx.fetch` body — lazy chunk so Store-only / edge graphs that never call
3
+ * outbound HTTP do not pay for host parsing + dry-run stub strings.
4
+ */
5
+
6
+ import type { EffectExternal } from "./effects.ts";
7
+ import { isDryRun, recordWouldHaveFired } from "./dry-run.ts";
8
+
9
+ /**
10
+ * Hostname for `fx.fetch` capability / ledger resource.
11
+ *
12
+ * @param href - Absolute URL string
13
+ */
14
+ export function hostFromFetchUrl(href: string): string {
15
+ try {
16
+ const host = new URL(href).hostname;
17
+ if (host) return host;
18
+ } catch {
19
+ /* fall through */
20
+ }
21
+ throw new Error(`fx.fetch: invalid URL "${href}" — expected an absolute URL with a hostname`);
22
+ }
23
+
24
+ type Gated = <T>(
25
+ kind: "fetch",
26
+ resource: string,
27
+ body: () => T | Promise<T>,
28
+ externalOf?:
29
+ | EffectExternal
30
+ | ((result: T | undefined, error: unknown) => EffectExternal | undefined),
31
+ ) => Promise<T>;
32
+
33
+ /**
34
+ * Capability-gated outbound HTTP (always `external.kind: "third-party"`).
35
+ *
36
+ * @param gated - Flow `gated` helper
37
+ * @param url - Absolute URL
38
+ * @param init - Fetch init
39
+ */
40
+ export function runFxFetch(gated: Gated, url: string | URL, init?: RequestInit): Promise<Response> {
41
+ const href = typeof url === "string" ? url : url.href;
42
+ const host = hostFromFetchUrl(href);
43
+ return gated(
44
+ "fetch",
45
+ host,
46
+ async () => {
47
+ if (isDryRun()) {
48
+ recordWouldHaveFired("fetch", host);
49
+ return new Response(null, { status: 204 });
50
+ }
51
+ return globalThis.fetch(url, init);
52
+ },
53
+ { host, kind: "third-party" },
54
+ );
55
+ }
@@ -108,13 +108,13 @@ export function createLiveStream(options: CreateLiveStreamOptions): LiveStreamRe
108
108
  }
109
109
 
110
110
  /**
111
- * Map OKE1014 to HTTP 410 `{ error: { code: "LiveResumeGap" } }`.
111
+ * Map OKE1210 to HTTP 410 `{ error: { code: "LiveResumeGap" } }`.
112
112
  *
113
113
  * @param err - Thrown value
114
114
  */
115
115
  export function encodeGap(err: unknown): Response | undefined {
116
116
  const o = err as { code?: unknown; params?: { signal?: string; afterId?: string } };
117
- if (o?.code !== 1014) return;
117
+ if (o?.code !== 1210) return;
118
118
  return Response.json(
119
119
  {
120
120
  data: null,
@@ -8,14 +8,14 @@ import { createFx, createFxContext, isSseFrame, signalReadRef } from "./fx.ts";
8
8
 
9
9
  describe("fx.live", () => {
10
10
  test("undeclared signal read throws OKE1001", async () => {
11
- const orderStatus = signal("order-status", { delivery: "live", optional: true });
11
+ const orderStatus = signal.live("order-status", { optional: true });
12
12
  const runtime = openRuntime(orderStatus);
13
13
  const fx = createFx({
14
14
  flow: "orders.events",
15
15
  effects: { reads: [signalReadRef("order-status")] },
16
16
  signalRuntime: runtime,
17
17
  });
18
- const other = signal("other-status", { delivery: "live", optional: true });
18
+ const other = signal.live("other-status", { optional: true });
19
19
  let err: unknown;
20
20
  try {
21
21
  const stream = fx.live(other);
@@ -44,7 +44,7 @@ describe("fx.live", () => {
44
44
  });
45
45
 
46
46
  test("ALS abort unsubscribes the live handler", async () => {
47
- const orderStatus = signal("order-status", { delivery: "live", optional: true });
47
+ const orderStatus = signal.live("order-status", { optional: true });
48
48
  const runtime = openRuntime(orderStatus);
49
49
  const bus = await runtime.start();
50
50
  const { fx } = createFxContext({
@@ -74,7 +74,7 @@ describe("fx.live", () => {
74
74
  });
75
75
 
76
76
  test("yields branded SSE frames with id + payload", async () => {
77
- const orderStatus = signal("order-status", { delivery: "live", optional: true });
77
+ const orderStatus = signal.live("order-status", { optional: true });
78
78
  const runtime = openRuntime(orderStatus);
79
79
  const bus = await runtime.start();
80
80
  await runtime.emit("order-status", { orderId: "ord_1", status: "placed" });
@@ -102,7 +102,7 @@ describe("fx.live", () => {
102
102
  });
103
103
 
104
104
  test("afterId skips already-delivered events", async () => {
105
- const orderStatus = signal("order-status", { delivery: "live", optional: true });
105
+ const orderStatus = signal.live("order-status", { optional: true });
106
106
  const runtime = openRuntime(orderStatus);
107
107
  const bus = await runtime.start();
108
108
  await runtime.emit("order-status", { orderId: "ord_1", status: "placed" });
@@ -130,8 +130,8 @@ describe("fx.live", () => {
130
130
  await runtime.close();
131
131
  });
132
132
 
133
- test("ready throws OKE1014 for a missing afterId", async () => {
134
- const orderStatus = signal("order-status", { delivery: "live", optional: true });
133
+ test("ready throws OKE1210 for a missing afterId", async () => {
134
+ const orderStatus = signal.live("order-status", { optional: true });
135
135
  const runtime = openRuntime(orderStatus);
136
136
  const fx = createFx({
137
137
  flow: "orders.events",
@@ -139,7 +139,7 @@ describe("fx.live", () => {
139
139
  signalRuntime: runtime,
140
140
  });
141
141
  const stream = fx.live(orderStatus, { afterId: "missing" });
142
- await expect(stream.ready?.()).rejects.toMatchObject({ code: 1014 });
142
+ await expect(stream.ready?.()).rejects.toMatchObject({ code: 1210 });
143
143
  const res = await encodeExecuteResult({ output: stream });
144
144
  expect(res.status).toBe(410);
145
145
  expect(await res.json()).toMatchObject({