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
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * `openai-compatible` AI driver — thin HTTP client for OpenAI-shaped APIs.
3
3
  *
4
- * One protocol driver for OpenAI, Groq, Together, OpenRouter, vLLM, LM Studio,
5
- * Ollama `/v1`, and other chat/completions endpoints. Configure via
6
- * `baseUrl` + `apiKey` + `model` (+ optional `headers`). Native Ollama stays
7
- * on the separate `ollama` driver. Injectable `fetch` for tests. Never a
4
+ * One protocol driver for OpenAI, Groq, Together, OpenRouter, LM Studio, and
5
+ * other chat/completions `/v1` endpoints. Configure via `baseUrl` + `apiKey` +
6
+ * `model` (+ optional `headers`). Injectable `fetch` for tests. Never a
8
7
  * production default — prod must declare.
9
8
  */
10
9
 
@@ -19,6 +18,8 @@ import type {
19
18
  AiStreamChunk,
20
19
  AiToolCall,
21
20
  } from "./ai-types.ts";
21
+ import type { DriverExternal } from "./external.ts";
22
+ import { hostFromUrl } from "./external.ts";
22
23
 
23
24
  /** Default OpenAI cloud base — apiKey is required for this origin. */
24
25
  export const OPENAI_COMPAT_DEFAULT_BASE = "https://api.openai.com/v1";
@@ -90,6 +91,7 @@ export async function openOpenaiCompatible(options: AiOpenOptions = {}): Promise
90
91
  const fetchFn = options.fetch ?? globalThis.fetch;
91
92
  const extraHeaders = options.headers;
92
93
  preconnectFetch(fetchFn, baseUrl);
94
+ const externalBase = resolveOpenaiCompatibleExternal(baseUrl, options);
93
95
 
94
96
  return {
95
97
  driverId: "openai-compatible",
@@ -140,6 +142,7 @@ export async function openOpenaiCompatible(options: AiOpenOptions = {}): Promise
140
142
  inputTokens: raw.usage?.prompt_tokens,
141
143
  outputTokens: raw.usage?.completion_tokens,
142
144
  },
145
+ ...(externalBase !== undefined ? { external: externalBase } : {}),
143
146
  };
144
147
  },
145
148
  async *stream(opts: AiCompleteOptions): AsyncIterable<AiStreamChunk> {
@@ -186,11 +189,35 @@ export async function openOpenaiCompatible(options: AiOpenOptions = {}): Promise
186
189
  vectors,
187
190
  model: raw.model ?? resolvedModel,
188
191
  driverId: "openai-compatible",
192
+ ...(externalBase !== undefined ? { external: externalBase } : {}),
189
193
  };
190
194
  },
191
195
  };
192
196
  }
193
197
 
198
+ /**
199
+ * Build egress metadata from open options + the real request base URL.
200
+ *
201
+ * Kind comes from declare-time {@link AiOpenOptions.external} (or defaults to
202
+ * third-party for the OpenAI cloud base). Host is always the real request host.
203
+ *
204
+ * @param baseUrl - Normalized chat/embeddings base
205
+ * @param options - Open options
206
+ */
207
+ function resolveOpenaiCompatibleExternal(
208
+ baseUrl: string,
209
+ options: AiOpenOptions,
210
+ ): DriverExternal | undefined {
211
+ const host = hostFromUrl(baseUrl);
212
+ if (!host) return undefined;
213
+ const kind = options.external?.kind ?? "third-party";
214
+ return {
215
+ host,
216
+ kind,
217
+ ...(options.external?.provider !== undefined ? { provider: options.external.provider } : {}),
218
+ };
219
+ }
220
+
194
221
  /** Protocol-named openai-compatible driver. */
195
222
  export const openaiCompatibleAiDriver: AiDriver = {
196
223
  id: "openai-compatible",
@@ -198,8 +225,8 @@ export const openaiCompatibleAiDriver: AiDriver = {
198
225
  };
199
226
 
200
227
  /**
201
- * llama.cpp granite empties `content` on `json_schema`; `json_object` plus
202
- * the ask-time schema instruction is the portable contract.
228
+ * Some openai-compatible servers empty `content` on `json_schema`;
229
+ * `json_object` plus the ask-time schema instruction is the portable contract.
203
230
  *
204
231
  * @param responseFormat - Runtime format (may be json_schema)
205
232
  */
@@ -309,7 +336,7 @@ async function* readOpenaiSse(res: Response, signal?: AbortSignal): AsyncGenerat
309
336
  function responseTextStream(res: Response): AsyncIterable<string> {
310
337
  const stream = (res as Response & { textStream?: () => AsyncIterable<string> }).textStream;
311
338
  if (typeof stream !== "function") {
312
- throw new Error("openai-compatible: Response.textStream is required (Bun >= 1.4.0)");
339
+ throw new Error("openai-compatible: Response.textStream is required (Bun >= 1.4.2)");
313
340
  }
314
341
  return stream.call(res);
315
342
  }
@@ -5,14 +5,6 @@
5
5
  import { describe, expect, test } from "bun:test";
6
6
  import { anthropicAiDriver, openAnthropic } from "./ai-anthropic.ts";
7
7
  import { mockAiDriver } from "./ai-mock.ts";
8
- import {
9
- normalizeOllamaBaseUrl,
10
- ollamaAiDriver,
11
- OLLAMA_DEFAULT_MODEL,
12
- openOllama,
13
- OllamaUnavailableError,
14
- resolveOllamaModel,
15
- } from "./ai-ollama.ts";
16
8
  import { openaiCompatibleAiDriver, openOpenaiCompatible } from "./ai-openai-compatible.ts";
17
9
 
18
10
  describe("anthropic driver", () => {
@@ -266,102 +258,3 @@ describe("openai-compatible driver", () => {
266
258
  );
267
259
  });
268
260
  });
269
-
270
- describe("ollama driver", () => {
271
- test("id is ollama; documented default model is qwen3.5:9b (overridable)", () => {
272
- expect(ollamaAiDriver.id).toBe("ollama");
273
- expect(OLLAMA_DEFAULT_MODEL).toBe("qwen3.5:9b");
274
- expect(resolveOllamaModel({ model: "llama3.2:1b" })).toBe("llama3.2:1b");
275
- expect(normalizeOllamaBaseUrl("localhost:11434")).toBe("http://localhost:11434");
276
- expect(normalizeOllamaBaseUrl("http://127.0.0.1:11434/v1")).toBe("http://127.0.0.1:11434");
277
- });
278
-
279
- test("complete via injectable fetch — native /api/chat, any model name", async () => {
280
- const calls: Array<{ url: string; body: Record<string, unknown> }> = [];
281
- const fetchFn: typeof fetch = Object.assign(
282
- async (input: string | URL | Request, init?: RequestInit) => {
283
- const url = String(input);
284
- if (url.endsWith("/api/tags")) {
285
- return new Response(JSON.stringify({ models: [] }), { status: 200 });
286
- }
287
- const body = JSON.parse(String(init?.body)) as Record<string, unknown>;
288
- calls.push({ url, body });
289
- return new Response(
290
- JSON.stringify({
291
- model: "llama3.2:1b",
292
- message: { role: "assistant", content: "pong" },
293
- prompt_eval_count: 4,
294
- eval_count: 1,
295
- }),
296
- { status: 200 },
297
- );
298
- },
299
- { preconnect: () => {} },
300
- ) as typeof fetch;
301
-
302
- const client = await openOllama({
303
- model: "llama3.2:1b",
304
- baseUrl: "http://ollama.test:11434",
305
- fetch: fetchFn,
306
- });
307
- const result = await client.complete({
308
- messages: [{ role: "user", content: "ping" }],
309
- temperature: 0,
310
- maxTokens: 16,
311
- });
312
-
313
- expect(result.driverId).toBe("ollama");
314
- expect(result.text).toBe("pong");
315
- expect(result.usage?.inputTokens).toBe(4);
316
- expect(calls).toHaveLength(1);
317
- expect(calls[0]!.url).toBe("http://ollama.test:11434/api/chat");
318
- expect(calls[0]!.body.model).toBe("llama3.2:1b");
319
- expect(calls[0]!.body.stream).toBe(false);
320
- expect(calls[0]!.body.think).toBe(false);
321
- expect(calls[0]!.body.options).toEqual({ temperature: 0, num_predict: 16 });
322
- });
323
-
324
- test("embed via injectable fetch — native /api/embeddings", async () => {
325
- const paths: string[] = [];
326
- const fetchFn: typeof fetch = Object.assign(
327
- async (input: string | URL | Request) => {
328
- const url = String(input);
329
- paths.push(url);
330
- if (url.endsWith("/api/tags")) {
331
- return new Response(JSON.stringify({ models: [] }), { status: 200 });
332
- }
333
- if (url.endsWith("/api/embeddings")) {
334
- return new Response(JSON.stringify({ embedding: [0.1, 0.2, 0.3] }), {
335
- status: 200,
336
- });
337
- }
338
- return new Response(JSON.stringify({ error: "unexpected" }), { status: 500 });
339
- },
340
- { preconnect: () => {} },
341
- ) as typeof fetch;
342
-
343
- const client = await openOllama({
344
- model: "nomic-embed-text",
345
- baseUrl: "http://ollama.test:11434",
346
- fetch: fetchFn,
347
- });
348
- const emb = await client.embed!({ input: ["a", "b"] });
349
- expect(emb.driverId).toBe("ollama");
350
- expect(emb.vectors).toHaveLength(2);
351
- expect(emb.vectors[0]).toEqual([0.1, 0.2, 0.3]);
352
- expect(paths.filter((p) => p.endsWith("/api/embeddings"))).toHaveLength(2);
353
- });
354
-
355
- test("unreachable server throws OllamaUnavailableError (no silent fallback)", async () => {
356
- const fetchFn: typeof fetch = Object.assign(
357
- async () => {
358
- throw new TypeError("connection refused");
359
- },
360
- { preconnect: () => {} },
361
- ) as typeof fetch;
362
-
363
- await expect(
364
- openOllama({ baseUrl: "http://127.0.0.1:9", fetch: fetchFn }),
365
- ).rejects.toBeInstanceOf(OllamaUnavailableError);
366
- });
367
- });
@@ -7,7 +7,6 @@ import { withAbortSignal } from "../kernel/abort-scope.ts";
7
7
  import { createFx, createFxContext } from "../kernel/fx.ts";
8
8
  import { ai, createAiRuntime } from "../elements/ai.ts";
9
9
  import { openOpenaiCompatible } from "./ai-openai-compatible.ts";
10
- import { openOllama } from "./ai-ollama.ts";
11
10
  import { mockAiDriver } from "./ai-mock.ts";
12
11
 
13
12
  describe("openai-compatible SSE stream", () => {
@@ -59,39 +58,6 @@ describe("openai-compatible SSE stream", () => {
59
58
  });
60
59
  });
61
60
 
62
- describe("ollama NDJSON stream", () => {
63
- test("yields message.content deltas", async () => {
64
- const ndjson =
65
- JSON.stringify({ message: { content: "A" }, done: false }) +
66
- "\n" +
67
- JSON.stringify({ message: { content: "B" }, done: true }) +
68
- "\n";
69
- const fetchFn: typeof fetch = Object.assign(
70
- async (input: string | URL | Request) => {
71
- const url = String(input);
72
- if (url.endsWith("/api/tags")) {
73
- return new Response(JSON.stringify({ models: [] }), { status: 200 });
74
- }
75
- return new Response(ndjson, { status: 200 });
76
- },
77
- { preconnect: () => {} },
78
- ) as typeof fetch;
79
-
80
- const client = await openOllama({
81
- model: "llama3.2:1b",
82
- baseUrl: "http://ollama.test:11434",
83
- fetch: fetchFn,
84
- });
85
- const parts: string[] = [];
86
- for await (const chunk of client.stream!({
87
- messages: [{ role: "user", content: "x" }],
88
- })) {
89
- if (chunk.text) parts.push(chunk.text);
90
- }
91
- expect(parts.join("")).toBe("AB");
92
- });
93
- });
94
-
95
61
  describe("fx.stream uses ambient AbortSignal", () => {
96
62
  test("race abort cancels in-flight stream fetch", async () => {
97
63
  let sawAbort = false;
@@ -1,18 +1,14 @@
1
1
  /**
2
2
  * Protocol-named AI model driver contracts.
3
3
  *
4
- * Driver ids: `mock` · `anthropic` · `openai-compatible` · `bedrock` · `vertex` · `ollama`.
4
+ * Driver ids: `mock` · `anthropic` · `openai-compatible` · `bedrock` · `vertex`.
5
5
  * Dev default is `mock`. There is **no** production default — prod must declare.
6
6
  */
7
7
 
8
+ import type { DriverExternal } from "./external.ts";
9
+
8
10
  /** Protocol ids for AI model drivers. */
9
- export type AiDriverId =
10
- | "mock"
11
- | "anthropic"
12
- | "openai-compatible"
13
- | "bedrock"
14
- | "vertex"
15
- | "ollama";
11
+ export type AiDriverId = "mock" | "anthropic" | "openai-compatible" | "bedrock" | "vertex";
16
12
 
17
13
  /** Tool definition offered to a model (Flow-backed at the fx layer). */
18
14
  export interface AiToolDef {
@@ -66,6 +62,8 @@ export interface AiCompleteResult {
66
62
  readonly outputTokens?: number;
67
63
  readonly cost?: number;
68
64
  };
65
+ /** Egress identity when the completion left the process. */
66
+ readonly external?: DriverExternal;
69
67
  }
70
68
 
71
69
  /** One streamed token / delta from a model. */
@@ -85,6 +83,8 @@ export interface AiEmbedResult {
85
83
  readonly vectors: readonly (readonly number[])[];
86
84
  readonly model: string;
87
85
  readonly driverId: AiDriverId;
86
+ /** Egress identity when the embed left the process. */
87
+ readonly external?: DriverExternal;
88
88
  }
89
89
 
90
90
  /** Opened model client. */
@@ -115,6 +115,15 @@ export interface AiOpenOptions {
115
115
  /** Mock canned responses keyed by prompt name or substring. */
116
116
  readonly mockResponses?: Readonly<Record<string, unknown>>;
117
117
  readonly fetch?: typeof globalThis.fetch;
118
+ /**
119
+ * Driver-reported egress classification for ledger entries.
120
+ * Set by the AI element from declare-time provider identity — never guessed
121
+ * by hostname regex inside the driver.
122
+ */
123
+ readonly external?: {
124
+ readonly kind: "third-party" | "infrastructure";
125
+ readonly provider?: string;
126
+ };
118
127
  }
119
128
 
120
129
  /** AI model driver factory. */
@@ -72,7 +72,12 @@ export function openFcmChannel(options: ChannelOpenOptions = {}): ChannelDriver
72
72
  verify: () => transport.verify(),
73
73
  };
74
74
 
75
- return { id: "fcm", channel, pushTransport: transport };
75
+ return {
76
+ id: "fcm",
77
+ channel,
78
+ pushTransport: transport,
79
+ external: { host: "fcm.googleapis.com", provider: "fcm", kind: "third-party" },
80
+ };
76
81
  }
77
82
 
78
83
  /** FCM driver factory. */
@@ -51,7 +51,12 @@ export function openMsegatChannel(options: ChannelOpenOptions = {}): ChannelDriv
51
51
  verify: () => transport.verify(),
52
52
  };
53
53
 
54
- return { id: "msegat", channel, smsTransport: transport };
54
+ return {
55
+ id: "msegat",
56
+ channel,
57
+ smsTransport: transport,
58
+ external: { host: "www.msegat.com", provider: "msegat", kind: "third-party" },
59
+ };
55
60
  }
56
61
 
57
62
  /** Msegat driver factory. */
@@ -15,7 +15,11 @@ export function openResendChannel(options: ChannelOpenOptions = {}): ChannelDriv
15
15
  throw new Error("resend channel: apiKey is required");
16
16
  }
17
17
  const transport = new ResendTransport({ apiKey: options.apiKey });
18
- return { id: "resend", transport };
18
+ return {
19
+ id: "resend",
20
+ transport,
21
+ external: { host: "api.resend.com", provider: "resend", kind: "third-party" },
22
+ };
19
23
  }
20
24
 
21
25
  /** Resend driver factory. */
@@ -25,7 +25,11 @@ export function openSmtpChannel(options: ChannelOpenOptions = {}): ChannelDriver
25
25
  adapter: new BunAdapter({ secure }),
26
26
  ...(options.user && options.pass ? { auth: { user: options.user, pass: options.pass } } : {}),
27
27
  });
28
- return { id: "smtp", transport };
28
+ return {
29
+ id: "smtp",
30
+ transport,
31
+ external: { host: options.host, provider: "smtp", kind: "infrastructure" },
32
+ };
29
33
  }
30
34
 
31
35
  /** SMTP driver factory. */
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { SndrTransport } from "sently/transports/sndr";
6
6
  import type { ChannelDriver, ChannelOpenOptions } from "./channel-types.ts";
7
+ import { hostFromUrl } from "./external.ts";
7
8
 
8
9
  /**
9
10
  * Open an SNDR channel driver.
@@ -18,7 +19,15 @@ export function openSndrChannel(options: ChannelOpenOptions = {}): ChannelDriver
18
19
  apiKey: options.apiKey,
19
20
  ...(options.url ? { baseUrl: options.url } : {}),
20
21
  });
21
- return { id: "sndr", transport };
22
+ return {
23
+ id: "sndr",
24
+ transport,
25
+ external: {
26
+ host: (options.url ? hostFromUrl(options.url) : undefined) ?? "api.sndr.email",
27
+ provider: "sndr",
28
+ kind: "third-party",
29
+ },
30
+ };
22
31
  }
23
32
 
24
33
  /** SNDR driver factory. */
@@ -24,7 +24,11 @@ export function openTaqnyatMailChannel(options: ChannelOpenOptions = {}): Channe
24
24
  throw new Error("taqnyat-mail channel: campaignName is required");
25
25
  }
26
26
  const transport = new TaqnyatMailTransport({ bearerToken, campaignName });
27
- return { id: "taqnyat-mail", transport };
27
+ return {
28
+ id: "taqnyat-mail",
29
+ transport,
30
+ external: { host: "api.taqnyat.sa", provider: "taqnyat-mail", kind: "third-party" },
31
+ };
28
32
  }
29
33
 
30
34
  /** Taqnyat Email driver factory. */
@@ -84,7 +84,12 @@ export function openTaqnyatWhatsAppChannel(options: ChannelOpenOptions = {}): Ch
84
84
  verify: () => transport.verify(),
85
85
  };
86
86
 
87
- return { id: "taqnyat-whatsapp", channel, whatsappTransport: transport };
87
+ return {
88
+ id: "taqnyat-whatsapp",
89
+ channel,
90
+ whatsappTransport: transport,
91
+ external: { host: "api.taqnyat.sa", provider: "taqnyat-whatsapp", kind: "third-party" },
92
+ };
88
93
  }
89
94
 
90
95
  /** Taqnyat WhatsApp driver factory. */
@@ -47,7 +47,12 @@ export function openTaqnyatChannel(options: ChannelOpenOptions = {}): ChannelDri
47
47
  verify: () => transport.verify(),
48
48
  };
49
49
 
50
- return { id: "taqnyat", channel, smsTransport: transport };
50
+ return {
51
+ id: "taqnyat",
52
+ channel,
53
+ smsTransport: transport,
54
+ external: { host: "api.taqnyat.sa", provider: "taqnyat", kind: "third-party" },
55
+ };
51
56
  }
52
57
 
53
58
  /** Taqnyat driver factory. */
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { MailOptions, SendResult, Transport, VerifyResult } from "sently";
10
+ import type { DriverExternal } from "./external.ts";
10
11
 
11
12
  export type {
12
13
  Address,
@@ -185,6 +186,8 @@ export interface ChannelAttempt {
185
186
  readonly error?: string;
186
187
  readonly at: number;
187
188
  readonly messageId?: string;
189
+ /** Egress identity for this attempt when the call left the process. */
190
+ readonly external?: DriverExternal;
188
191
  }
189
192
 
190
193
  /** Result of a channel send including chain history. */
@@ -195,6 +198,8 @@ export interface ChannelSendResult {
195
198
  readonly attempts: readonly ChannelAttempt[];
196
199
  /** Present when an email transport returned a sently {@link SendResult}. */
197
200
  readonly mail?: SendResult;
201
+ /** Egress identity for the winning (or last) attempt. */
202
+ readonly external?: DriverExternal;
198
203
  }
199
204
 
200
205
  /** Medium-agnostic transport (sms / whatsapp / push / console). */
@@ -223,6 +228,11 @@ export interface ChannelDriver {
223
228
  readonly pushTransport?: PushTransport;
224
229
  /** Medium-agnostic transport. */
225
230
  readonly channel?: ChannelTransport;
231
+ /**
232
+ * Driver-reported egress identity for sends through this binding.
233
+ * Omitted for in-process console / local sinks.
234
+ */
235
+ readonly external?: DriverExternal;
226
236
  }
227
237
 
228
238
  /** Open options shared by channel drivers. */
@@ -48,7 +48,12 @@ export function openUnifonicChannel(options: ChannelOpenOptions = {}): ChannelDr
48
48
  verify: () => transport.verify(),
49
49
  };
50
50
 
51
- return { id: "unifonic", channel, smsTransport: transport };
51
+ return {
52
+ id: "unifonic",
53
+ channel,
54
+ smsTransport: transport,
55
+ external: { host: "el.cloud.unifonic.com", provider: "unifonic", kind: "third-party" },
56
+ };
52
57
  }
53
58
 
54
59
  /** Unifonic driver factory. */
@@ -56,7 +56,12 @@ export function openWaCloudChannel(options: ChannelOpenOptions = {}): ChannelDri
56
56
  verify: () => transport.verify(),
57
57
  };
58
58
 
59
- return { id: "wa-cloud", channel, whatsappTransport: transport };
59
+ return {
60
+ id: "wa-cloud",
61
+ channel,
62
+ whatsappTransport: transport,
63
+ external: { host: "graph.facebook.com", provider: "wa-cloud", kind: "third-party" },
64
+ };
60
65
  }
61
66
 
62
67
  /** WhatsApp Cloud driver factory. */
@@ -68,7 +68,12 @@ export function openWebPushChannel(options: ChannelOpenOptions = {}): ChannelDri
68
68
  close: () => sender.close(),
69
69
  };
70
70
 
71
- return { id: "webpush", channel, pushTransport: transport };
71
+ return {
72
+ id: "webpush",
73
+ channel,
74
+ pushTransport: transport,
75
+ external: { host: "webpush", provider: "webpush", kind: "third-party" },
76
+ };
72
77
  }
73
78
 
74
79
  /** Web Push driver factory. */
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Shared egress identity for driver call results and {@link EffectEntry.external}.
3
+ *
4
+ * Drivers report this on real call results — never inferred by hostname regex
5
+ * at the ledger layer. Only `host` is required when present.
6
+ */
7
+
8
+ /** Egress peer for an effect that left the process. */
9
+ export interface DriverExternal {
10
+ /** Peer hostname (or equivalent) the call reached. */
11
+ readonly host: string;
12
+ /** Vendor / transport / registry label when known. */
13
+ readonly provider?: string;
14
+ /**
15
+ * `"third-party"` — vendor business API.
16
+ * `"infrastructure"` — developer-owned egress.
17
+ */
18
+ readonly kind?: "third-party" | "infrastructure";
19
+ }
20
+
21
+ /**
22
+ * Hostname from an absolute URL or base URL string.
23
+ *
24
+ * @param url - Absolute URL
25
+ */
26
+ export function hostFromUrl(url: string): string | undefined {
27
+ try {
28
+ const host = new URL(url).hostname;
29
+ return host || undefined;
30
+ } catch {
31
+ return undefined;
32
+ }
33
+ }
@@ -245,13 +245,3 @@ export {
245
245
  isOpenaiCloudBase,
246
246
  openaiCompatibleHeaders,
247
247
  } from "./ai-openai-compatible.ts";
248
- export {
249
- ollamaAiDriver,
250
- openOllama,
251
- OllamaUnavailableError,
252
- OLLAMA_DEFAULT_MODEL,
253
- OLLAMA_DEFAULT_BASE_URL,
254
- normalizeOllamaBaseUrl,
255
- resolveOllamaBaseUrl,
256
- resolveOllamaModel,
257
- } from "./ai-ollama.ts";
@@ -21,6 +21,7 @@ import type {
21
21
  TextIndexSearchResult,
22
22
  TextIndexStore,
23
23
  } from "./types.ts";
24
+ import { hostFromUrl } from "./external.ts";
24
25
 
25
26
  /** Error thrown when the remote Meilisearch is unreachable / unhealthy / errors. */
26
27
  export class MeilisearchUnavailableError extends Error {
@@ -195,6 +196,11 @@ export async function openMeilisearchIndex(
195
196
 
196
197
  return {
197
198
  driverId: "meilisearch",
199
+ external: {
200
+ host: hostFromUrl(url) ?? "meilisearch",
201
+ provider: "meilisearch",
202
+ kind: "infrastructure",
203
+ },
198
204
  async upsert(id, document) {
199
205
  const body = [{ ...document, id }];
200
206
  const task = await enqueue(`/indexes/${uid}/documents`, {
@@ -11,6 +11,8 @@
11
11
  * grants are never implemented.
12
12
  */
13
13
 
14
+ import type { DriverExternal } from "./external.ts";
15
+
14
16
  /** Supported social providers. */
15
17
  export type OAuthDriverId =
16
18
  | "apple"
@@ -80,6 +82,8 @@ export interface OAuthExchangeInput {
80
82
  /** Result of {@link OAuthDriver.exchangeCode}. */
81
83
  export interface OAuthExchangeResult {
82
84
  readonly tokens: OAuthTokenSet;
85
+ /** Egress identity for the token endpoint call. */
86
+ readonly external?: DriverExternal;
83
87
  }
84
88
 
85
89
  /** Input to {@link OAuthDriver.resolveAssertion}. */