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,289 @@
1
+ /**
2
+ * OpenAI-compatible provider registry — resolve / override / unknown / limited.
3
+ */
4
+
5
+ import { afterEach, describe, expect, test } from "bun:test";
6
+ import { openaiCompatibleAiDriver } from "../../drivers/ai-openai-compatible.ts";
7
+ import { extractFromSources } from "../../compiler/extract.ts";
8
+ import {
9
+ ai,
10
+ createAiRuntime,
11
+ formatAiProviderTier2Warn,
12
+ getAiProviderEntry,
13
+ resolveAiModelBaseUrl,
14
+ resetAiDecls,
15
+ } from "../ai.ts";
16
+
17
+ afterEach(() => {
18
+ resetAiDecls();
19
+ });
20
+
21
+ describe("resolveAiModelBaseUrl", () => {
22
+ test("known provider omits baseUrl → registry URL", () => {
23
+ expect(resolveAiModelBaseUrl({ provider: "openrouter" })).toEqual({
24
+ baseUrl: "https://openrouter.ai/api/v1",
25
+ });
26
+ expect(resolveAiModelBaseUrl({ provider: "groq" }).baseUrl).toBe(
27
+ "https://api.groq.com/openai/v1",
28
+ );
29
+ expect(resolveAiModelBaseUrl({ provider: "together" }).baseUrl).toBe(
30
+ "https://api.together.ai/v1",
31
+ );
32
+ expect(resolveAiModelBaseUrl({ provider: "deepinfra" }).baseUrl).toBe(
33
+ "https://api.deepinfra.com/v1/openai",
34
+ );
35
+ expect(resolveAiModelBaseUrl({ provider: "deepseek" }).baseUrl).toBe(
36
+ "https://api.deepseek.com",
37
+ );
38
+ expect(resolveAiModelBaseUrl({ provider: "vercel" }).baseUrl).toBe(
39
+ "https://ai-gateway.vercel.sh/v1",
40
+ );
41
+ });
42
+
43
+ test("explicit baseUrl always wins for a known provider", () => {
44
+ expect(
45
+ resolveAiModelBaseUrl({
46
+ provider: "openrouter",
47
+ baseUrl: "https://proxy.example/v1",
48
+ }),
49
+ ).toEqual({ baseUrl: "https://proxy.example/v1" });
50
+ });
51
+
52
+ test("unknown provider without baseUrl fails loud", () => {
53
+ expect(() => resolveAiModelBaseUrl({ provider: "cloudflare" })).toThrow(
54
+ /unknown provider "cloudflare" requires an explicit baseUrl/,
55
+ );
56
+ expect(() => resolveAiModelBaseUrl({ provider: "meta" })).toThrow(
57
+ /unknown provider "meta" requires an explicit baseUrl/,
58
+ );
59
+ expect(() => resolveAiModelBaseUrl({ provider: "not-a-real-provider" })).toThrow(
60
+ /unknown provider "not-a-real-provider"/,
61
+ );
62
+ });
63
+
64
+ test("unknown provider with explicit baseUrl is allowed", () => {
65
+ expect(
66
+ resolveAiModelBaseUrl({
67
+ provider: "cloudflare",
68
+ baseUrl: "https://api.cloudflare.com/client/v4/accounts/abc/ai/v1",
69
+ }),
70
+ ).toEqual({
71
+ baseUrl: "https://api.cloudflare.com/client/v4/accounts/abc/ai/v1",
72
+ });
73
+ });
74
+
75
+ test("native driverId skips openai-compat registry for anthropic", () => {
76
+ expect(
77
+ resolveAiModelBaseUrl({
78
+ provider: "anthropic",
79
+ driverId: "anthropic",
80
+ }),
81
+ ).toEqual({});
82
+ });
83
+
84
+ test("limited anthropic / google surface caveat when auto-resolved", () => {
85
+ const anth = resolveAiModelBaseUrl({ provider: "anthropic" });
86
+ expect(anth.baseUrl).toBe("https://api.anthropic.com/v1");
87
+ expect(anth.tier2Caveat).toContain("testing/evaluation only");
88
+ expect(anth.tier2Provider).toBe("anthropic");
89
+
90
+ const google = resolveAiModelBaseUrl({ provider: "google" });
91
+ expect(google.baseUrl).toBe("https://generativelanguage.googleapis.com/v1beta/openai");
92
+ expect(google.tier2Caveat).toContain("tool");
93
+
94
+ const gemini = resolveAiModelBaseUrl({ provider: "gemini" });
95
+ expect(gemini.baseUrl).toBe(google.baseUrl);
96
+ expect(gemini.tier2Caveat).toBe(google.tier2Caveat);
97
+ });
98
+
99
+ test("mock / local / openai-compatible exempt from unknown-provider error", () => {
100
+ expect(resolveAiModelBaseUrl({ provider: "mock" })).toEqual({});
101
+ expect(resolveAiModelBaseUrl({ provider: "local" })).toEqual({});
102
+ expect(resolveAiModelBaseUrl({ provider: "openai-compatible" })).toEqual({});
103
+ expect(() => resolveAiModelBaseUrl({ provider: "ollama" })).toThrow(/explicit baseUrl/);
104
+ });
105
+ });
106
+
107
+ describe("ai.model provider registry", () => {
108
+ test("auto-resolves openrouter baseUrl on declare", () => {
109
+ const m = ai.model("smart", {
110
+ provider: "openrouter",
111
+ model: "openrouter/free",
112
+ apiKey: "sk-or",
113
+ });
114
+ expect(m.baseUrl).toBe("https://openrouter.ai/api/v1");
115
+ expect(m.apiKey).toBe("sk-or");
116
+ });
117
+
118
+ test("explicit baseUrl override wins on declare", () => {
119
+ const m = ai.model("smart", {
120
+ provider: "groq",
121
+ baseUrl: "http://127.0.0.1:9000/v1",
122
+ apiKey: "gsk",
123
+ });
124
+ expect(m.baseUrl).toBe("http://127.0.0.1:9000/v1");
125
+ });
126
+
127
+ test("unknown provider without baseUrl throws on declare", () => {
128
+ expect(() => ai.model("x", { provider: "acme-llm" })).toThrow(
129
+ /unknown provider "acme-llm" requires an explicit baseUrl/,
130
+ );
131
+ });
132
+
133
+ test("native anthropic driverId does not inject /v1 openai-compat base", () => {
134
+ const m = ai.model("claude", {
135
+ provider: "anthropic",
136
+ driverId: "anthropic",
137
+ model: "claude-sonnet-4",
138
+ apiKey: "sk-ant",
139
+ });
140
+ expect(m.baseUrl).toBeUndefined();
141
+ expect(m.driverId).toBe("anthropic");
142
+ });
143
+
144
+ test("limited-compat declare warns with caveat text", () => {
145
+ const warns: string[] = [];
146
+ const original = console.warn;
147
+ console.warn = (msg?: unknown) => {
148
+ warns.push(String(msg));
149
+ };
150
+ try {
151
+ ai.model("eval-claude", { provider: "anthropic", model: "claude-sonnet-4" });
152
+ } finally {
153
+ console.warn = original;
154
+ }
155
+ expect(
156
+ warns.some(
157
+ (w) => w.includes('provider "anthropic"') && w.includes("limited OpenAI-compatible"),
158
+ ),
159
+ ).toBe(true);
160
+ expect(warns.some((w) => w.includes("testing/evaluation only"))).toBe(true);
161
+ });
162
+ });
163
+
164
+ describe("multi-provider apiKey isolation", () => {
165
+ test("openrouter + groq open distinct clients with own baseUrl and apiKey", async () => {
166
+ const opens: Array<{ baseUrl?: string; apiKey?: string; model?: string }> = [];
167
+ const fetchFn = Object.assign(
168
+ async () =>
169
+ new Response(
170
+ JSON.stringify({
171
+ choices: [{ message: { content: "{}" } }],
172
+ usage: { prompt_tokens: 1, completion_tokens: 1 },
173
+ }),
174
+ { status: 200, headers: { "content-type": "application/json" } },
175
+ ),
176
+ { preconnect: () => {} },
177
+ ) as typeof fetch;
178
+
179
+ const driver = {
180
+ id: "openai-compatible" as const,
181
+ async open(opts: { model?: string; baseUrl?: string; apiKey?: string }) {
182
+ opens.push({
183
+ ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),
184
+ ...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),
185
+ ...(opts.model !== undefined ? { model: opts.model } : {}),
186
+ });
187
+ return openaiCompatibleAiDriver.open({ ...opts, fetch: fetchFn });
188
+ },
189
+ };
190
+
191
+ const or = ai.model("via-or", {
192
+ provider: "openrouter",
193
+ model: "openrouter/free",
194
+ apiKey: "sk-openrouter",
195
+ });
196
+ const gq = ai.model("via-groq", {
197
+ provider: "groq",
198
+ model: "llama-3.1-8b-instant",
199
+ apiKey: "gsk-groq",
200
+ });
201
+ const pOr = or.prompt("ping-or");
202
+ const pGq = gq.prompt("ping-groq");
203
+
204
+ const rt = createAiRuntime({
205
+ models: [or, gq],
206
+ prompts: [pOr, pGq],
207
+ defaultDriver: driver,
208
+ });
209
+
210
+ await rt.ask("ping-or", {});
211
+ await rt.ask("ping-groq", {});
212
+
213
+ expect(opens).toHaveLength(2);
214
+ expect(opens[0]).toEqual({
215
+ baseUrl: "https://openrouter.ai/api/v1",
216
+ apiKey: "sk-openrouter",
217
+ model: "openrouter/free",
218
+ });
219
+ expect(opens[1]).toEqual({
220
+ baseUrl: "https://api.groq.com/openai/v1",
221
+ apiKey: "gsk-groq",
222
+ model: "llama-3.1-8b-instant",
223
+ });
224
+ });
225
+ });
226
+
227
+ describe("extract limited-compat provider warn", () => {
228
+ test("warns when ai.model uses limited provider without baseUrl", async () => {
229
+ const warns: string[] = [];
230
+ const original = console.warn;
231
+ console.warn = (msg?: unknown) => {
232
+ warns.push(String(msg));
233
+ };
234
+ try {
235
+ await extractFromSources({
236
+ "src/ai.ts": `
237
+ import { ai } from "okengine";
238
+ export const smart = ai.model("smart", { provider: "google", model: "gemini-2.5-flash" });
239
+ `,
240
+ });
241
+ } finally {
242
+ console.warn = original;
243
+ }
244
+ expect(
245
+ warns.some(
246
+ (w) =>
247
+ w.startsWith("[oke extract] warn:") &&
248
+ w.includes('provider "google"') &&
249
+ w.includes("limited OpenAI-compatible"),
250
+ ),
251
+ ).toBe(true);
252
+ });
253
+
254
+ test("does not warn for native anthropic driverId", async () => {
255
+ const warns: string[] = [];
256
+ const original = console.warn;
257
+ console.warn = (msg?: unknown) => {
258
+ warns.push(String(msg));
259
+ };
260
+ try {
261
+ await extractFromSources({
262
+ "src/ai.ts": `
263
+ import { ai } from "okengine";
264
+ export const claude = ai.model("claude", {
265
+ provider: "anthropic",
266
+ driverId: "anthropic",
267
+ model: "claude-sonnet-4",
268
+ });
269
+ `,
270
+ });
271
+ } finally {
272
+ console.warn = original;
273
+ }
274
+ expect(warns.some((w) => w.includes("limited OpenAI-compatible"))).toBe(false);
275
+ });
276
+ });
277
+
278
+ describe("registry entries", () => {
279
+ test("gemini aliases google", () => {
280
+ expect(getAiProviderEntry("gemini")).toEqual(getAiProviderEntry("google"));
281
+ });
282
+
283
+ test("formatAiProviderTier2Warn prefixes correctly", () => {
284
+ const entry = getAiProviderEntry("anthropic")!;
285
+ expect(formatAiProviderTier2Warn("anthropic", entry.caveat!, "ai.model")).toContain(
286
+ 'ai.model: provider "anthropic"',
287
+ );
288
+ });
289
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Verified OpenAI-compatible provider registry for {@link ai.model}.
3
+ *
4
+ * Known names resolve `baseUrl` automatically. Explicit `baseUrl` always wins.
5
+ * Unknown names require an explicit `baseUrl` (fail loud). Limited-compatibility
6
+ * entries carry caveat text surfaced at declare/extract time.
7
+ */
8
+
9
+ /** Registry status — 1 = verified OpenAI-compat; 2 = limited compatibility. */
10
+ export type AiProviderTier = 1 | 2;
11
+
12
+ /** One verified OpenAI-compatible provider. */
13
+ export interface AiProviderEntry {
14
+ readonly baseUrl: string;
15
+ readonly tier: AiProviderTier;
16
+ /** Honest limitation text for limited-compatibility providers (tool-calling / eval-only). */
17
+ readonly caveat?: string;
18
+ }
19
+
20
+ /** Drivers that own their own base URL — never inject openai-compat registry. */
21
+ export const AI_NATIVE_DRIVER_IDS: ReadonlySet<string> = new Set(["anthropic", "mock"]);
22
+
23
+ /**
24
+ * Provider labels that are not OpenAI-compat cloud names and do not require
25
+ * `baseUrl` (native/local/mock bindings; driver defaults apply).
26
+ */
27
+ export const AI_PROVIDER_BASEURL_EXEMPT: ReadonlySet<string> = new Set([
28
+ "mock",
29
+ "local",
30
+ "openai-compatible",
31
+ ]);
32
+
33
+ const ANTHROPIC_OPENAI_COMPAT_CAVEAT =
34
+ 'Anthropic\'s OpenAI-compatible endpoint (https://api.anthropic.com/v1) is for testing/evaluation only — Anthropic recommends the native Claude API for production. Caveats: tools[].function.strict is ignored (no schema guarantee), n must be 1, no embeddings on this surface. Prefer driverId: "anthropic" for production Anthropic usage.';
35
+
36
+ const GOOGLE_OPENAI_COMPAT_CAVEAT =
37
+ "Google's OpenAI-compatible endpoint does not follow full OpenAI tool/parameter JSON Schema fidelity (OpenAPI-shaped params; complex schemas with $ref / anyOf / additionalProperties / $schema commonly fail). Agents that call Flows as tools can misbehave silently — do not treat this as a verified provider for tool-calling-dependent production.";
38
+
39
+ const GOOGLE_ENTRY: AiProviderEntry = {
40
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
41
+ tier: 2,
42
+ caveat: GOOGLE_OPENAI_COMPAT_CAVEAT,
43
+ };
44
+
45
+ /**
46
+ * Verified OpenAI-compatible providers — URLs locked from official sources.
47
+ * Omitted (require explicit `baseUrl`): Cloudflare (account-scoped URL);
48
+ * Meta (retired Llama API / unverified Muse Spark host — never guess).
49
+ */
50
+ export const AI_OPENAI_COMPAT_PROVIDERS: Readonly<Record<string, AiProviderEntry>> = {
51
+ openai: { baseUrl: "https://api.openai.com/v1", tier: 1 },
52
+ openrouter: { baseUrl: "https://openrouter.ai/api/v1", tier: 1 },
53
+ groq: { baseUrl: "https://api.groq.com/openai/v1", tier: 1 },
54
+ together: { baseUrl: "https://api.together.ai/v1", tier: 1 },
55
+ deepinfra: { baseUrl: "https://api.deepinfra.com/v1/openai", tier: 1 },
56
+ xai: { baseUrl: "https://api.x.ai/v1", tier: 1 },
57
+ mistral: { baseUrl: "https://api.mistral.ai/v1", tier: 1 },
58
+ deepseek: { baseUrl: "https://api.deepseek.com", tier: 1 },
59
+ vercel: { baseUrl: "https://ai-gateway.vercel.sh/v1", tier: 1 },
60
+ anthropic: {
61
+ baseUrl: "https://api.anthropic.com/v1",
62
+ tier: 2,
63
+ caveat: ANTHROPIC_OPENAI_COMPAT_CAVEAT,
64
+ },
65
+ google: GOOGLE_ENTRY,
66
+ /** CLI catalog alias for {@link GOOGLE_ENTRY}. */
67
+ gemini: GOOGLE_ENTRY,
68
+ };
69
+
70
+ /**
71
+ * Look up a verified OpenAI-compatible provider by name.
72
+ *
73
+ * @param provider - Provider name from `ai.model({ provider })`
74
+ */
75
+ export function getAiProviderEntry(provider: string): AiProviderEntry | undefined {
76
+ return AI_OPENAI_COMPAT_PROVIDERS[provider];
77
+ }
78
+
79
+ /**
80
+ * Error when `provider` is set, not in the registry, and `baseUrl` is omitted.
81
+ *
82
+ * @param provider - Unknown provider name
83
+ */
84
+ export function unknownAiProviderBaseUrlError(provider: string): Error {
85
+ return new Error(
86
+ `ai.model: unknown provider "${provider}" requires an explicit baseUrl ` +
87
+ `(known OpenAI-compatible providers: ${Object.keys(AI_OPENAI_COMPAT_PROVIDERS)
88
+ .filter((k) => k !== "gemini")
89
+ .sort()
90
+ .join(", ")}; ` +
91
+ `gemini is an alias of google). Cloudflare, Meta, and other omitted / account-scoped endpoints always need baseUrl.`,
92
+ );
93
+ }
94
+
95
+ /** Inputs for {@link resolveAiModelBaseUrl}. */
96
+ export interface ResolveAiModelBaseUrlInput {
97
+ readonly provider?: string;
98
+ readonly baseUrl?: string;
99
+ readonly driverId?: string;
100
+ }
101
+
102
+ /** Result of baseUrl resolution for one model binding. */
103
+ export interface ResolveAiModelBaseUrlResult {
104
+ readonly baseUrl?: string;
105
+ /** Limited-compatibility caveat to surface at declare/extract time (when auto-resolved). */
106
+ readonly tier2Caveat?: string;
107
+ /** Provider name that produced the limited-compatibility caveat. */
108
+ readonly tier2Provider?: string;
109
+ }
110
+
111
+ /**
112
+ * Resolve `baseUrl` for an `ai.model` declaration.
113
+ *
114
+ * Explicit `baseUrl` always wins. Native drivers skip the openai-compat
115
+ * registry. Known providers auto-fill; unknown providers without `baseUrl`
116
+ * throw.
117
+ *
118
+ * @param input - Provider / baseUrl / driverId from AiModelOptions
119
+ */
120
+ export function resolveAiModelBaseUrl(
121
+ input: ResolveAiModelBaseUrlInput,
122
+ ): ResolveAiModelBaseUrlResult {
123
+ if (input.baseUrl !== undefined) {
124
+ return { baseUrl: input.baseUrl };
125
+ }
126
+
127
+ if (input.driverId !== undefined && AI_NATIVE_DRIVER_IDS.has(input.driverId)) {
128
+ return {};
129
+ }
130
+
131
+ const provider = input.provider;
132
+ if (provider === undefined) {
133
+ return {};
134
+ }
135
+
136
+ const entry = getAiProviderEntry(provider);
137
+ if (!entry) {
138
+ if (AI_PROVIDER_BASEURL_EXEMPT.has(provider)) {
139
+ return {};
140
+ }
141
+ throw unknownAiProviderBaseUrlError(provider);
142
+ }
143
+
144
+ if (entry.tier === 2 && entry.caveat !== undefined) {
145
+ return {
146
+ baseUrl: entry.baseUrl,
147
+ tier2Caveat: entry.caveat,
148
+ tier2Provider: provider,
149
+ };
150
+ }
151
+
152
+ return { baseUrl: entry.baseUrl };
153
+ }
154
+
155
+ /**
156
+ * Format a limited-compatibility caveat for declare-time / extract-time
157
+ * `console.warn`.
158
+ *
159
+ * @param provider - Provider name
160
+ * @param caveat - Registry caveat text
161
+ * @param prefix - Log prefix (`ai.model` or `[oke extract] warn`)
162
+ */
163
+ export function formatAiProviderTier2Warn(
164
+ provider: string,
165
+ caveat: string,
166
+ prefix: "ai.model" | "[oke extract] warn",
167
+ ): string {
168
+ return `${prefix}: provider "${provider}" (limited OpenAI-compatible): ${caveat}`;
169
+ }
@@ -76,7 +76,7 @@ export interface AgentDenial {
76
76
 
77
77
  /** Effect on a tool flow — same vocabulary as Flows / Traces. */
78
78
  export interface AgentToolEffect {
79
- readonly kind: "read" | "write" | "emit" | "send" | "ask" | "secret" | "call";
79
+ readonly kind: "read" | "write" | "emit" | "send" | "ask" | "embed" | "secret" | "call";
80
80
  readonly resource: string;
81
81
  }
82
82
 
@@ -287,6 +287,11 @@ export interface AiRuntime {
287
287
  readonly agentRuns: readonly AgentRunRecord[];
288
288
  /** Journal of ask results (replay without re-calling the model). */
289
289
  readonly journal: readonly AiJournalEntry[];
290
+ /**
291
+ * Egress identity from the most recent ask / embed / stream complete.
292
+ * Used by `fx.ask` / `fx.embed` to stamp {@link EffectEntry.external}.
293
+ */
294
+ lastExternal?: import("../../drivers/external.ts").DriverExternal;
290
295
  /**
291
296
  * Ask a prompt with optional model fallback chain and optional tools.
292
297
  *
@@ -335,6 +340,14 @@ export interface AiRuntime {
335
340
  * @param text - Text to embed
336
341
  */
337
342
  embed(embed: string, id: string, text: string): Promise<void>;
343
+ /**
344
+ * Produce an embedding vector for text via a named model (no index write).
345
+ * Used by built-in hybrid search (`fx.embed`) and the system embed CDC flow.
346
+ *
347
+ * @param model - Model name
348
+ * @param text - Text to embed
349
+ */
350
+ embedVector(model: string, text: string): Promise<readonly number[]>;
338
351
  }
339
352
 
340
353
  /**
@@ -388,6 +401,10 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
388
401
  const now = options.now ?? (() => Date.now());
389
402
  const journalingForced = options.forceJournal !== false;
390
403
  let runSeq = 0;
404
+ /** Mutable egress stamp shared by ask / embed / toolLoop. */
405
+ const egress: {
406
+ lastExternal?: import("../../drivers/external.ts").DriverExternal;
407
+ } = {};
391
408
 
392
409
  async function clientFor(name: string): Promise<AiModelClient> {
393
410
  const existing = clients.get(name);
@@ -403,15 +420,40 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
403
420
  : `ai: no client for model "${name}" and no defaultDriver`,
404
421
  );
405
422
  }
423
+ const externalOpen = aiOpenExternal(model);
406
424
  const opened = await driver.open({
407
425
  model: model?.model ?? name,
408
426
  ...(model?.baseUrl !== undefined ? { baseUrl: model.baseUrl } : {}),
409
427
  ...(model?.apiKey !== undefined ? { apiKey: model.apiKey } : {}),
428
+ ...(externalOpen !== undefined ? { external: externalOpen } : {}),
410
429
  });
411
430
  clients.set(name, opened);
412
431
  return opened;
413
432
  }
414
433
 
434
+ /**
435
+ * Declare-time egress classification for a model binding (never hostname regex).
436
+ *
437
+ * @param model - Model declaration
438
+ */
439
+ function aiOpenExternal(
440
+ model: AiModelDecl | undefined,
441
+ ): { kind: "third-party" | "infrastructure"; provider?: string } | undefined {
442
+ if (!model) return undefined;
443
+ const provider = model.provider?.toLowerCase();
444
+ if (model.driverId === "mock" || provider === "mock") return undefined;
445
+ if (provider === "local") {
446
+ return { kind: "infrastructure", provider: "local" };
447
+ }
448
+ if (model.driverId === "anthropic") {
449
+ return { kind: "third-party", provider: provider ?? "anthropic" };
450
+ }
451
+ return {
452
+ kind: "third-party",
453
+ ...(provider !== undefined ? { provider } : {}),
454
+ };
455
+ }
456
+
415
457
  /** Wire model id for a logical binding (never send the binding name to providers). */
416
458
  function wireModel(logicalName: string, client: AiModelClient): string {
417
459
  return models.get(logicalName)?.model ?? client.model;
@@ -581,6 +623,9 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
581
623
  responseFormat: opts.responseFormat,
582
624
  ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
583
625
  });
626
+ if (result.external !== undefined) {
627
+ egress.lastExternal = result.external;
628
+ }
584
629
  cost += result.usage?.cost ?? 0;
585
630
  addUsageTokens(tokens, result.usage);
586
631
  lastText = result.text;
@@ -646,7 +691,7 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
646
691
  };
647
692
  }
648
693
 
649
- return {
694
+ const runtime: AiRuntime = {
650
695
  prompts,
651
696
  agents,
652
697
  embeds,
@@ -655,6 +700,12 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
655
700
  denials,
656
701
  agentRuns,
657
702
  journal,
703
+ get lastExternal() {
704
+ return egress.lastExternal;
705
+ },
706
+ set lastExternal(value) {
707
+ egress.lastExternal = value;
708
+ },
658
709
  async ask(prompt, input, opts) {
659
710
  const pin = parsePromptRef(prompt);
660
711
  const decl = prompts.get(pin.name) ?? prompts.get(prompt);
@@ -765,6 +816,9 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
765
816
  ...(responseFormat !== undefined ? { responseFormat } : {}),
766
817
  ...(signal !== undefined ? { signal } : {}),
767
818
  });
819
+ if (result.external !== undefined) {
820
+ egress.lastExternal = result.external;
821
+ }
768
822
  attemptCost = result.usage?.cost ?? 0;
769
823
  totalCost += attemptCost;
770
824
  addUsageTokens(totalTokens, result.usage);
@@ -1004,16 +1058,25 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
1004
1058
  );
1005
1059
  }
1006
1060
  const modelName = decl.model ?? [...models.keys()][0] ?? "mock";
1061
+ const vector = await this.embedVector(modelName, text);
1062
+ await index.upsert(id, vector, { text });
1063
+ },
1064
+
1065
+ async embedVector(modelName, text) {
1007
1066
  const client = await clientFor(modelName);
1008
1067
  if (!client.embed) {
1009
1068
  throw new Error(`ai: model "${modelName}" does not support embed`);
1010
1069
  }
1011
- const { vectors } = await client.embed({ input: text, model: modelName });
1012
- const vector = vectors[0];
1070
+ const result = await client.embed({ input: text, model: modelName });
1071
+ if (result.external !== undefined) {
1072
+ egress.lastExternal = result.external;
1073
+ }
1074
+ const vector = result.vectors[0];
1013
1075
  if (!vector) throw new Error("ai: empty embedding");
1014
- await index.upsert(id, vector, { text });
1076
+ return vector;
1015
1077
  },
1016
1078
  };
1079
+ return runtime;
1017
1080
  }
1018
1081
 
1019
1082
  export { AiSchemaValidationError } from "./schema.ts";
@@ -120,8 +120,8 @@ export function promptOutJsonSchema(out: unknown): Record<string, unknown> | und
120
120
 
121
121
  /**
122
122
  * Driver `response_format` for a declared `out` — JSON Schema wrapper.
123
- * OpenAI-compatible wires this as `json_object` (llama.cpp granite empties
124
- * `content` on `json_schema`).
123
+ * OpenAI-compatible wires this as `json_object` (some openai-compatible
124
+ * servers empty `content` on `json_schema`).
125
125
  *
126
126
  * @param prompt - Prompt name (schema id)
127
127
  * @param out - Declared output
@@ -653,21 +653,21 @@ describe("ask budget and cancel", () => {
653
653
 
654
654
  test("driverId opens the matching protocol driver", async () => {
655
655
  let opened = "";
656
- const local = ai.model("local", { driverId: "ollama", model: "llama" });
656
+ const local = ai.model("local", { driverId: "openai-compatible", model: "llama" });
657
657
  const prompt = local.prompt("ping");
658
658
  const runtime = createAiRuntime({
659
659
  models: [local],
660
660
  prompts: [prompt],
661
661
  drivers: {
662
- ollama: {
663
- id: "ollama",
662
+ "openai-compatible": {
663
+ id: "openai-compatible",
664
664
  async open() {
665
- opened = "ollama";
665
+ opened = "openai-compatible";
666
666
  return {
667
- driverId: "ollama",
667
+ driverId: "openai-compatible",
668
668
  model: "llama",
669
669
  async complete() {
670
- return { text: "{}", raw: {}, model: "llama", driverId: "ollama" };
670
+ return { text: "{}", raw: {}, model: "llama", driverId: "openai-compatible" };
671
671
  },
672
672
  };
673
673
  },
@@ -675,7 +675,7 @@ describe("ask budget and cancel", () => {
675
675
  },
676
676
  });
677
677
  await runtime.ask("ping", {});
678
- expect(opened).toBe("ollama");
678
+ expect(opened).toBe("openai-compatible");
679
679
  });
680
680
  });
681
681
 
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Physics: inference · prompts · embeddings · agents.
5
5
  * Drivers: `mock` (dev) · `anthropic` · `openai-compatible` · `bedrock` ·
6
- * `vertex` · `ollama`. No production default — prod must declare.
6
+ * `vertex`. No production default — prod must declare.
7
7
  *
8
8
  * Prompts are versioned artifacts with validated output shapes and eval sets
9
9
  * (`oke eval` gates CI). Agents' tools are the app's own flows (gates apply).
@@ -79,6 +79,22 @@ export type { AiRatePreset } from "./ai/rate.ts";
79
79
 
80
80
  export { assertAllowPiiForAsk, AiPiiBuildError, type PiiAskCheckInput } from "./ai/pii.ts";
81
81
 
82
+ export {
83
+ AI_NATIVE_DRIVER_IDS,
84
+ AI_OPENAI_COMPAT_PROVIDERS,
85
+ AI_PROVIDER_BASEURL_EXEMPT,
86
+ formatAiProviderTier2Warn,
87
+ getAiProviderEntry,
88
+ resolveAiModelBaseUrl,
89
+ unknownAiProviderBaseUrlError,
90
+ } from "./ai/providers.ts";
91
+ export type {
92
+ AiProviderEntry,
93
+ AiProviderTier,
94
+ ResolveAiModelBaseUrlInput,
95
+ ResolveAiModelBaseUrlResult,
96
+ } from "./ai/providers.ts";
97
+
82
98
  export {
83
99
  runPromptEvals,
84
100
  parseEvalJsonl,