okengine 0.18.5 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. package/src/drivers/ollama.ts +0 -14
@@ -2,8 +2,8 @@
2
2
  * EffectKind presentation for the trace detail Sheet.
3
3
  *
4
4
  * Kernel {@link EffectKind} values (singular): read · write · emit · send ·
5
- * ask · secret · call. Graph edge strokes use the plural Manifest keys
6
- * (reads/writes/emits/calls/asks) — map through {@link effectBarColor}.
5
+ * ask · embed · secret · call. Graph edge strokes use the plural Manifest keys
6
+ * (reads/writes/emits/calls/asks/embeds) — map through {@link effectBarColor}.
7
7
  * Icons reuse the same eight-element HugeIcons vocabulary as Flow graph
8
8
  * nodes (store / signal / flow / ai / channel / vault).
9
9
  */
@@ -23,8 +23,10 @@ export const EFFECT_KIND_LABEL: Readonly<Record<RunEffectKind, string>> = {
23
23
  emit: "Emit",
24
24
  send: "Send",
25
25
  ask: "Ask",
26
+ embed: "Embed",
26
27
  secret: "Secret",
27
28
  call: "Call",
29
+ fetch: "Fetch",
28
30
  };
29
31
 
30
32
  /**
@@ -40,8 +42,10 @@ export function effectKindSummaryLabel(kind: RunEffectKind, count: number): stri
40
42
  emit: "emit",
41
43
  send: "send",
42
44
  ask: "ask",
45
+ embed: "embed",
43
46
  secret: "secret",
44
47
  call: "call",
48
+ fetch: "fetch",
45
49
  };
46
50
  const plural: Record<RunEffectKind, string> = {
47
51
  read: "reads",
@@ -49,8 +53,10 @@ export function effectKindSummaryLabel(kind: RunEffectKind, count: number): stri
49
53
  emit: "emits",
50
54
  send: "sends",
51
55
  ask: "asks",
56
+ embed: "embeds",
52
57
  secret: "secrets",
53
58
  call: "calls",
59
+ fetch: "fetches",
54
60
  };
55
61
  return `${count} ${count === 1 ? singular[kind] : plural[kind]}`;
56
62
  }
@@ -73,8 +79,10 @@ export function effectBarColor(kind: RunEffectKind): string {
73
79
  case "call":
74
80
  return EDGE_STROKE.calls;
75
81
  case "ask":
82
+ case "embed":
76
83
  return EDGE_STROKE.asks;
77
84
  case "send":
85
+ case "fetch":
78
86
  return EDGE_STROKE.sends;
79
87
  case "secret":
80
88
  return EDGE_STROKE.secrets;
@@ -99,8 +107,10 @@ export function effectKindIcon(kind: RunEffectKind, resource?: string): ElementH
99
107
  case "call":
100
108
  return ELEMENT_ICONS.flow.icon;
101
109
  case "ask":
110
+ case "embed":
102
111
  return ELEMENT_ICONS.ai.icon;
103
112
  case "send":
113
+ case "fetch":
104
114
  return ELEMENT_ICONS.channel.icon;
105
115
  case "secret":
106
116
  return ELEMENT_ICONS.vault.icon;
@@ -43,7 +43,9 @@ const KIND_ORDER: readonly RunEffectKind[] = [
43
43
  "emit",
44
44
  "call",
45
45
  "ask",
46
+ "embed",
46
47
  "send",
48
+ "fetch",
47
49
  "secret",
48
50
  ];
49
51
 
@@ -201,8 +203,10 @@ export function effectEventLabel(effect: Pick<RunEffect, "kind" | "resource">):
201
203
  emit: "Emit",
202
204
  send: "Send",
203
205
  ask: "Ask",
206
+ embed: "Embed",
204
207
  secret: "Secret",
205
208
  call: "Call",
209
+ fetch: "Fetch",
206
210
  };
207
211
  return labels[effect.kind];
208
212
  }
@@ -484,6 +484,7 @@ export function TraceDetailSheet({
484
484
  data-slot="trace-event-row"
485
485
  data-index={index}
486
486
  data-focused={focused ? "true" : "false"}
487
+ data-external={effect.external ? "true" : undefined}
487
488
  className={cn(
488
489
  EXPLORER_ROW_CLASS,
489
490
  "cursor-pointer flex-col items-stretch gap-1 text-[11px]",
@@ -510,6 +511,9 @@ export function TraceDetailSheet({
510
511
  </span>
511
512
  <span className="min-w-0 flex-1 truncate font-mono text-muted-foreground">
512
513
  {effect.resource}
514
+ {effect.external
515
+ ? ` · ↗ ${effect.external.host}${effect.external.kind ? ` (${effect.external.kind})` : ""}`
516
+ : ""}
513
517
  </span>
514
518
  <span className="shrink-0 tabular-nums text-muted-foreground">
515
519
  {formatDuration(effect.duration)}
@@ -815,6 +819,7 @@ function OverviewTrack({
815
819
  "absolute inset-y-0.5 cursor-pointer rounded-full transition-opacity",
816
820
  hoverIndex !== null && hoverIndex !== bar.index && !focused && "opacity-35",
817
821
  focused && "ring-1 ring-foreground/40",
822
+ bar.external && "outline outline-1 outline-dashed outline-foreground/50",
818
823
  )}
819
824
  style={{
820
825
  left: `${mapped.left * 100}%`,
@@ -822,6 +827,7 @@ function OverviewTrack({
822
827
  backgroundColor: effectBarColor(bar.kind),
823
828
  }}
824
829
  data-slot="trace-waterfall-overview-bar"
830
+ data-external={bar.external ? "true" : undefined}
825
831
  onClick={(event) => {
826
832
  event.stopPropagation();
827
833
  onSelect?.(bar.index);
@@ -995,6 +1001,7 @@ function PlaybackBarFill({
995
1001
  "absolute inset-y-0 rounded-full transition-opacity",
996
1002
  !animated && dimmed && !focused && "opacity-35",
997
1003
  focused && "ring-1 ring-foreground/40",
1004
+ bar.external && "outline outline-1 outline-dashed outline-foreground/50",
998
1005
  )}
999
1006
  style={{
1000
1007
  left: `${left * 100}%`,
@@ -1004,6 +1011,7 @@ function PlaybackBarFill({
1004
1011
  ...(animated ? { scaleX, opacity } : {}),
1005
1012
  }}
1006
1013
  data-slot={slot}
1014
+ data-external={bar.external ? "true" : undefined}
1007
1015
  />
1008
1016
  );
1009
1017
  }
@@ -30,6 +30,8 @@ export type WaterfallBar = {
30
30
  * `offsetRatio + widthRatio === 1`.
31
31
  */
32
32
  readonly widthRatio: number;
33
+ /** Egress identity when the effect left the process. */
34
+ readonly external?: RunEffect["external"];
33
35
  };
34
36
 
35
37
  /**
@@ -58,6 +60,7 @@ export function waterfallBars(
58
60
  durationMs: 0,
59
61
  offsetRatio: 0,
60
62
  widthRatio: 0,
63
+ ...(effect.external !== undefined ? { external: effect.external } : {}),
61
64
  }));
62
65
  }
63
66
 
@@ -75,6 +78,7 @@ export function waterfallBars(
75
78
  durationMs: clampedDuration,
76
79
  offsetRatio: startOffsetMs / durationMs,
77
80
  widthRatio: clampedDuration / durationMs,
81
+ ...(effect.external !== undefined ? { external: effect.external } : {}),
78
82
  };
79
83
  });
80
84
  }
@@ -9,12 +9,18 @@ import type { WaterfallBar } from "./waterfall-bars.ts";
9
9
  /**
10
10
  * Build the shadcn Tooltip content for one waterfall bar.
11
11
  *
12
- * Format: `{kind label} · {resource} · {duration} · +{start offset}`.
12
+ * Format: `{kind label} · {resource} · {duration} · +{start offset}`
13
+ * plus optional egress (`host` / provider / kind) when `external` is set.
13
14
  *
14
15
  * @param bar - Positioned bar (layout + effect identity)
15
16
  */
16
17
  export function waterfallBarTooltip(
17
- bar: Pick<WaterfallBar, "kind" | "resource" | "durationMs" | "startOffsetMs">,
18
+ bar: Pick<WaterfallBar, "kind" | "resource" | "durationMs" | "startOffsetMs" | "external">,
18
19
  ): string {
19
- return `${effectEventLabel(bar)} · ${bar.resource} · ${formatDuration(bar.durationMs)} · +${formatDuration(bar.startOffsetMs)}`;
20
+ const base = `${effectEventLabel(bar)} · ${bar.resource} · ${formatDuration(bar.durationMs)} · +${formatDuration(bar.startOffsetMs)}`;
21
+ if (!bar.external) return base;
22
+ const parts = [bar.external.host];
23
+ if (bar.external.provider) parts.push(bar.external.provider);
24
+ if (bar.external.kind) parts.push(bar.external.kind);
25
+ return `${base} · ↗ ${parts.join(" · ")}`;
20
26
  }
@@ -7,6 +7,7 @@ import { ViewIcon, ViewOffSlashIcon } from "@hugeicons/core-free-icons";
7
7
  import { HugeiconsIcon } from "@hugeicons/react";
8
8
  import { Button } from "@/components/ui/button";
9
9
  import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
10
+ import { formatTemporalCell, formatGridCell } from "../lib/grid-model.ts";
10
11
  import { useStoreReveal } from "../data/use-store-reveal.ts";
11
12
 
12
13
  /** Props for {@link RevealCell}. */
@@ -38,7 +39,9 @@ export function RevealCell({
38
39
  if (clear !== null) {
39
40
  return (
40
41
  <span className="inline-flex max-w-full items-center gap-1" data-slot="reveal-cell">
41
- <span className="min-w-0 truncate font-mono text-[11px]">{formatCell(clear.value)}</span>
42
+ <span className="min-w-0 truncate font-mono text-[11px]">
43
+ {formatRevealCell(column, clear.value)}
44
+ </span>
42
45
  <Tooltip>
43
46
  <TooltipTrigger
44
47
  render={(props) => (
@@ -120,18 +123,11 @@ export function RevealCell({
120
123
  {reveal.error.message}
121
124
  </span>
122
125
  ) : null}
123
- <span className="sr-only">{formatCell(maskedValue)}</span>
126
+ <span className="sr-only">{formatRevealCell(column, maskedValue)}</span>
124
127
  </span>
125
128
  );
126
129
  }
127
130
 
128
- function formatCell(value: unknown): string {
129
- if (value === null || value === undefined) return "—";
130
- if (typeof value === "string") return value;
131
- if (typeof value === "number" || typeof value === "boolean") return String(value);
132
- try {
133
- return JSON.stringify(value);
134
- } catch {
135
- return "[unserializable]";
136
- }
131
+ function formatRevealCell(column: string, value: unknown): string {
132
+ return formatTemporalCell(column, value) ?? formatGridCell(value);
137
133
  }
@@ -25,7 +25,7 @@ import { cn } from "@/lib/utils";
25
25
  import { useStoreEdit } from "../data/use-store-edit.ts";
26
26
  import { RevealCell } from "./reveal-cell.tsx";
27
27
  import {
28
- formatGridCell,
28
+ formatStoreCell,
29
29
  type StoreGridColumn,
30
30
  type StoreGridModel,
31
31
  type StoreGridRow,
@@ -236,7 +236,7 @@ export function StoreRowDetailSheet({
236
236
  className="min-w-0 flex-1 font-mono text-[11px] break-words"
237
237
  dir={isRtlText(value) ? "rtl" : "ltr"}
238
238
  >
239
- {formatGridCell(value)}
239
+ {formatStoreCell(col, value)}
240
240
  </span>
241
241
  )}
242
242
  </li>
@@ -66,6 +66,8 @@ function fieldFromColumn(
66
66
  ...(description !== undefined ? { description } : {}),
67
67
  ...(col.pii === true ? { pii: true } : {}),
68
68
  ...(col.sensitive === true ? { sensitive: true } : {}),
69
+ ...(declared?.searchable ? { searchable: true, searchWeight: declared.searchable.weight } : {}),
70
+ ...(declared?.embed ? { embed: true, embedDims: declared.embed.dims } : {}),
69
71
  ...(declared?.primaryKey === true ? { primaryKey: true } : {}),
70
72
  ...(declared?.unique === true && declared.primaryKey !== true ? { unique: true } : {}),
71
73
  ...(declared?.references?.table
@@ -1,5 +1,11 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { buildStoreGridModel, sqlRowId } from "./grid-model.ts";
2
+ import {
3
+ buildStoreGridModel,
4
+ formatStoreCell,
5
+ formatTemporalCell,
6
+ isTemporalColumnKey,
7
+ sqlRowId,
8
+ } from "./grid-model.ts";
3
9
 
4
10
  describe("buildStoreGridModel", () => {
5
11
  test("sql rows map to editable typed columns with pii flags", () => {
@@ -125,3 +131,21 @@ describe("sqlRowId", () => {
125
131
  expect(sqlRowId({})).toBe("");
126
132
  });
127
133
  });
134
+
135
+ describe("formatTemporalCell / formatStoreCell", () => {
136
+ test("formats epoch-ms *_at columns as ISO", () => {
137
+ expect(isTemporalColumnKey("created_at")).toBe(true);
138
+ expect(isTemporalColumnKey("createdAt")).toBe(true);
139
+ expect(isTemporalColumnKey("seats")).toBe(false);
140
+ expect(formatTemporalCell("created_at", 1_723_622_400_000)).toBe("2024-08-14T08:00:00.000Z");
141
+ expect(
142
+ formatStoreCell(
143
+ { key: "created_at", type: "integer", editable: false, pii: false },
144
+ 1_723_622_400_000,
145
+ ),
146
+ ).toBe("2024-08-14T08:00:00.000Z");
147
+ expect(formatStoreCell({ key: "seats", type: "integer", editable: true, pii: false }, 2)).toBe(
148
+ "2",
149
+ );
150
+ });
151
+ });
@@ -197,6 +197,7 @@ export function sqlRowId(row: Readonly<Record<string, unknown>>): string {
197
197
  /** Format a cell value for display. */
198
198
  export function formatGridCell(value: unknown): string {
199
199
  if (value === null || value === undefined) return "—";
200
+ if (value instanceof Date) return value.toISOString();
200
201
  if (typeof value === "string") return value;
201
202
  if (typeof value === "number" || typeof value === "boolean") return String(value);
202
203
  try {
@@ -207,7 +208,39 @@ export function formatGridCell(value: unknown): string {
207
208
  }
208
209
 
209
210
  /**
210
- * Format a cell using the column's display hint (TTL / bytes) when set.
211
+ * True when a SQL column name looks like an instant (`created_at`, `expiresAt`, ).
212
+ *
213
+ * @param key - Column key (camelCase or snake_case)
214
+ */
215
+ export function isTemporalColumnKey(key: string): boolean {
216
+ return /(?:^|_)(at|time|timestamp)$/i.test(key) || /(?:At|Time|Timestamp)$/.test(key);
217
+ }
218
+
219
+ /**
220
+ * Format epoch-ms / `Date` / ISO for a temporal column. Returns `null` when the
221
+ * value should keep the default grid formatter (non-temporal or non-instant).
222
+ *
223
+ * @param key - Column key
224
+ * @param value - Raw cell
225
+ */
226
+ export function formatTemporalCell(key: string, value: unknown): string | null {
227
+ if (value === null || value === undefined) return null;
228
+ if (value instanceof Date) return value.toISOString();
229
+ if (typeof value === "string") {
230
+ if (!isTemporalColumnKey(key)) return null;
231
+ const ms = Date.parse(value);
232
+ return Number.isNaN(ms) ? null : new Date(ms).toISOString();
233
+ }
234
+ if (typeof value === "number" && Number.isFinite(value) && isTemporalColumnKey(key)) {
235
+ // Auth / clock system tables store epoch-ms; reject tiny integers (counts).
236
+ if (value < 1_000_000_000_000 || value > 10_000_000_000_000) return null;
237
+ return new Date(value).toISOString();
238
+ }
239
+ return null;
240
+ }
241
+
242
+ /**
243
+ * Format a cell using the column's display hint (TTL / bytes / temporal) when set.
211
244
  *
212
245
  * @param col - Column descriptor
213
246
  * @param value - Raw cell
@@ -215,5 +248,7 @@ export function formatGridCell(value: unknown): string {
215
248
  export function formatStoreCell(col: StoreGridColumn, value: unknown): string {
216
249
  if (col.format === "ttl") return formatKvTtl(value);
217
250
  if (col.format === "bytes") return formatByteSize(value);
251
+ const temporal = formatTemporalCell(col.key, value);
252
+ if (temporal !== null) return temporal;
218
253
  return formatGridCell(value);
219
254
  }
@@ -36,7 +36,7 @@ import { cn } from "@/lib/utils.ts";
36
36
  import { JsonValueSheet } from "../detail/json-value-sheet.tsx";
37
37
  import { ToolbarTip } from "@/components/ui/toolbar-tip.tsx";
38
38
  import { cellExportText, rowsToCsv } from "../lib/grid-transfer.ts";
39
- import { formatGridCell } from "../lib/grid-model.ts";
39
+ import { formatGridCell, formatTemporalCell } from "../lib/grid-model.ts";
40
40
  import { asInspectableJson } from "../lib/json-value.ts";
41
41
  import type { QueryHighlightLanguage } from "../lib/query-highlight.ts";
42
42
  import { QueryHighlightView } from "./query-highlight-view.tsx";
@@ -310,10 +310,10 @@ export function QueryResults({
310
310
  setInspect({ rowId: String(i), column: col, value })
311
311
  }
312
312
  >
313
- {formatGridCell(value)}
313
+ {formatTemporalCell(col, value) ?? formatGridCell(value)}
314
314
  </button>
315
315
  ) : (
316
- formatGridCell(value)
316
+ (formatTemporalCell(col, value) ?? formatGridCell(value))
317
317
  )}
318
318
  </td>
319
319
  );
@@ -82,7 +82,9 @@ function flattenEffects(effects: Effects | undefined): EffectChip[] {
82
82
  push("emit", effects.emits);
83
83
  push("send", effects.sends);
84
84
  push("ask", effects.asks);
85
+ push("embed", effects.embeds);
85
86
  push("secret", effects.secrets);
86
87
  push("call", effects.calls);
88
+ push("fetch", effects.fetches);
87
89
  return out;
88
90
  }
@@ -16,7 +16,9 @@ export function isReadSafeCall(row: UnitFlowRow): boolean {
16
16
  (effects.writes?.length ?? 0) === 0 &&
17
17
  (effects.emits?.length ?? 0) === 0 &&
18
18
  (effects.sends?.length ?? 0) === 0 &&
19
- (effects.asks?.length ?? 0) === 0
19
+ (effects.asks?.length ?? 0) === 0 &&
20
+ (effects.embeds?.length ?? 0) === 0 &&
21
+ (effects.fetches?.length ?? 0) === 0
20
22
  );
21
23
  }
22
24
 
@@ -25,6 +25,12 @@ export interface FormField {
25
25
  readonly pii?: boolean;
26
26
  /** When true, column is sensitive-classified (Store schema pills). */
27
27
  readonly sensitive?: boolean;
28
+ /** Built-in hybrid search — BM25 via `.searchable()`. */
29
+ readonly searchable?: boolean;
30
+ readonly searchWeight?: number;
31
+ /** Async LSH embed via `.embed()`. */
32
+ readonly embed?: boolean;
33
+ readonly embedDims?: number;
28
34
  /** When true, column is a primary key (Store schema pills). */
29
35
  readonly primaryKey?: boolean;
30
36
  /** When true, column is a foreign key (declared or inferred). */
@@ -5,7 +5,6 @@ import {
5
5
  normalizeAiProbeUrl,
6
6
  probeAiModelStatus,
7
7
  startAiModelWatch,
8
- statusFromOllamaTags,
9
8
  statusFromOpenAiModels,
10
9
  } from "./ai-model-status.ts";
11
10
 
@@ -17,12 +16,9 @@ describe("ai-model-status", () => {
17
16
  expect(normalizeAiProbeUrl("http://127.0.0.1:8080/v1/", "openai-compatible")).toBe(
18
17
  "http://127.0.0.1:8080/v1",
19
18
  );
20
- expect(normalizeAiProbeUrl("http://127.0.0.1:11434/v1", "ollama")).toBe(
21
- "http://127.0.0.1:11434",
22
- );
23
19
  });
24
20
 
25
- test("aiModelIdsMatch tolerates llama.cpp quant aliasing", () => {
21
+ test("aiModelIdsMatch tolerates quant aliasing", () => {
26
22
  expect(aiModelIdsMatch("gemma4:e4b-q4_K_M", "gemma4:Q4_K_M")).toBe(true);
27
23
  expect(aiModelIdsMatch("smollm2", "smollm2")).toBe(true);
28
24
  expect(aiModelIdsMatch("gemma4:e4b-q4_K_M", "qwen3:8b")).toBe(false);
@@ -45,13 +41,6 @@ describe("ai-model-status", () => {
45
41
  expect(statusFromOpenAiModels("smollm2", { data: [] }).phase).toBe("starting");
46
42
  });
47
43
 
48
- test("statusFromOllamaTags ready when present", () => {
49
- expect(statusFromOllamaTags("qwen3.5:9b", { models: [{ name: "qwen3.5:9b" }] }).phase).toBe(
50
- "ready",
51
- );
52
- expect(statusFromOllamaTags("qwen3.5:9b", { models: [] }).phase).toBe("loading");
53
- });
54
-
55
44
  test("probeAiModelStatus uses injectable fetch", async () => {
56
45
  const status = await probeAiModelStatus({
57
46
  url: "http://127.0.0.1:9/v1",
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Probe local AI servers for configured-model readiness (llama.cpp / Ollama /
3
- * OpenAI-compatible). Used by `oke dev` to show model id + phase without
2
+ * Probe OpenAI-compatible AI servers for configured-model readiness
3
+ * (BYO `OKE_AI_URL`). Used by `oke dev` to show model id + phase without
4
4
  * blocking boot.
5
5
  */
6
6
 
@@ -19,14 +19,14 @@ export type AiModelStatus = {
19
19
  };
20
20
 
21
21
  /** Which HTTP surface to probe. */
22
- export type AiModelProbeKind = "openai-compatible" | "ollama";
22
+ export type AiModelProbeKind = "openai-compatible";
23
23
 
24
24
  /** Fetch contract (injectable for tests). */
25
25
  export type AiModelFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
26
26
 
27
27
  /** Options for {@link probeAiModelStatus}. */
28
28
  export type ProbeAiModelStatusOptions = {
29
- /** Base URL — with or without trailing `/v1` for OpenAI-compatible. */
29
+ /** Base URL — with or without trailing `/v1`. */
30
30
  readonly url: string;
31
31
  readonly model: string;
32
32
  readonly kind: AiModelProbeKind;
@@ -34,17 +34,15 @@ export type ProbeAiModelStatusOptions = {
34
34
  };
35
35
 
36
36
  /**
37
- * Strip trailing slashes; for OpenAI-compatible keep a single `/v1` suffix.
37
+ * Strip trailing slashes; keep a single `/v1` suffix for OpenAI-compatible.
38
38
  *
39
39
  * @param url - Raw base URL
40
40
  * @param kind - Probe kind
41
41
  */
42
42
  export function normalizeAiProbeUrl(url: string, kind: AiModelProbeKind): string {
43
43
  let base = url.trim().replace(/\/+$/, "");
44
- if (kind === "openai-compatible") {
45
- if (!base.endsWith("/v1")) base = `${base}/v1`;
46
- } else if (base.endsWith("/v1")) {
47
- base = base.slice(0, -3);
44
+ if (kind === "openai-compatible" && !base.endsWith("/v1")) {
45
+ base = `${base}/v1`;
48
46
  }
49
47
  return base;
50
48
  }
@@ -52,7 +50,7 @@ export function normalizeAiProbeUrl(url: string, kind: AiModelProbeKind): string
52
50
  /**
53
51
  * Whether a reported model id covers the requested `OKE_AI_MODEL`.
54
52
  *
55
- * Tolerates llama.cpp alias normalization (`gemma4:e4b-q4_K_M` ↔ `gemma4:Q4_K_M`).
53
+ * Tolerates quant / alias normalization (`gemma4:e4b-q4_K_M` ↔ `gemma4:Q4_K_M`).
56
54
  *
57
55
  * @param want - Requested id
58
56
  * @param reported - Server id
@@ -123,30 +121,6 @@ export function statusFromOpenAiModels(model: string, json: unknown): AiModelSta
123
121
  };
124
122
  }
125
123
 
126
- /**
127
- * Parse Ollama `GET /api/tags` into a status for `model`.
128
- *
129
- * @param model - Requested id
130
- * @param json - Parsed JSON
131
- */
132
- export function statusFromOllamaTags(model: string, json: unknown): AiModelStatus {
133
- if (!json || typeof json !== "object") {
134
- return { model, phase: "starting", detail: "invalid /api/tags body" };
135
- }
136
- const models = (json as { models?: unknown }).models;
137
- if (!Array.isArray(models)) {
138
- return { model, phase: "starting" };
139
- }
140
- for (const row of models) {
141
- if (!row || typeof row !== "object") continue;
142
- const name = (row as { name?: unknown }).name;
143
- if (typeof name === "string" && aiModelIdsMatch(model, name)) {
144
- return { model, phase: "ready", reportedId: name };
145
- }
146
- }
147
- return { model, phase: "loading", detail: "not in /api/tags yet" };
148
- }
149
-
150
124
  /**
151
125
  * Map a server-native status string to {@link AiModelPhase}.
152
126
  *
@@ -173,7 +147,7 @@ export async function probeAiModelStatus(opts: ProbeAiModelStatusOptions): Promi
173
147
  if (!model) return { model: "", phase: "error", detail: "OKE_AI_MODEL is empty" };
174
148
  const fetchFn = opts.fetch ?? globalThis.fetch;
175
149
  const base = normalizeAiProbeUrl(opts.url, opts.kind);
176
- const path = opts.kind === "ollama" ? `${base}/api/tags` : `${base}/models`;
150
+ const path = `${base}/models`;
177
151
  try {
178
152
  const res = await fetchFn(path, { method: "GET" });
179
153
  if (!res.ok) {
@@ -184,9 +158,7 @@ export async function probeAiModelStatus(opts: ProbeAiModelStatusOptions): Promi
184
158
  };
185
159
  }
186
160
  const json: unknown = await res.json().catch(() => null);
187
- return opts.kind === "ollama"
188
- ? statusFromOllamaTags(model, json)
189
- : statusFromOpenAiModels(model, json);
161
+ return statusFromOpenAiModels(model, json);
190
162
  } catch (err) {
191
163
  const msg = err instanceof Error ? err.message : String(err);
192
164
  return { model, phase: "unreachable", detail: msg };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Unit tests for compose-up progress parsing / labels.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import {
7
+ composeUpPhaseLabel,
8
+ composeUpPhaseStatus,
9
+ formatComposeUpEventMessage,
10
+ parseComposeProgressLine,
11
+ shortenComposeTarget,
12
+ stripAnsi,
13
+ } from "./compose-up.ts";
14
+
15
+ describe("parseComposeProgressLine", () => {
16
+ test("parses Image / Container lifecycle lines", () => {
17
+ expect(parseComposeProgressLine(" Image postgres:16-alpine Pulling ")).toEqual({
18
+ raw: "Image postgres:16-alpine Pulling",
19
+ target: "postgres:16-alpine",
20
+ kind: "image",
21
+ phase: "pulling",
22
+ });
23
+ expect(parseComposeProgressLine("Container oke-oke-2-postgres-1 Started")).toEqual({
24
+ raw: "Container oke-oke-2-postgres-1 Started",
25
+ target: "oke-oke-2-postgres-1",
26
+ kind: "container",
27
+ phase: "started",
28
+ });
29
+ expect(parseComposeProgressLine("Network oke-oke-2_default Creating")).toEqual({
30
+ raw: "Network oke-oke-2_default Creating",
31
+ target: "oke-oke-2_default",
32
+ kind: "network",
33
+ phase: "creating",
34
+ });
35
+ });
36
+
37
+ test("parses bare service Pulling / Pulled", () => {
38
+ expect(parseComposeProgressLine("postgres Pulling")).toEqual({
39
+ raw: "postgres Pulling",
40
+ target: "postgres",
41
+ phase: "pulling",
42
+ });
43
+ expect(parseComposeProgressLine("redis Pulled")).toEqual({
44
+ raw: "redis Pulled",
45
+ target: "redis",
46
+ phase: "pulled",
47
+ });
48
+ });
49
+
50
+ test("parses download / extract / error chatter", () => {
51
+ expect(parseComposeProgressLine("1f2544649a1a Downloading [====>] 5MB/12MB")?.phase).toBe(
52
+ "downloading",
53
+ );
54
+ expect(parseComposeProgressLine("1f2544649a1a Extracting [========>]")?.phase).toBe(
55
+ "extracting",
56
+ );
57
+ expect(parseComposeProgressLine("Error response from daemon: boom")?.phase).toBe("error");
58
+ });
59
+
60
+ test("keeps unknown lines as activity", () => {
61
+ expect(parseComposeProgressLine("some compose note")).toEqual({
62
+ raw: "some compose note",
63
+ phase: "other",
64
+ detail: "some compose note",
65
+ });
66
+ });
67
+
68
+ test("skips empty / spinner-only", () => {
69
+ expect(parseComposeProgressLine(" ")).toBeNull();
70
+ expect(parseComposeProgressLine("…")).toBeNull();
71
+ });
72
+
73
+ test("strips ANSI before parse", () => {
74
+ const line = "\x1b[32mContainer oke-x-redis-1 Started\x1b[0m";
75
+ expect(parseComposeProgressLine(line)?.phase).toBe("started");
76
+ expect(stripAnsi(line)).toBe("Container oke-x-redis-1 Started");
77
+ });
78
+ });
79
+
80
+ describe("shortenComposeTarget / labels", () => {
81
+ test("shortens oke container and image refs", () => {
82
+ expect(shortenComposeTarget("oke-oke-2-postgres-1")).toBe("postgres");
83
+ expect(shortenComposeTarget("postgres:16-alpine")).toBe("postgres");
84
+ expect(shortenComposeTarget("ghcr.io/org/mailpit:latest")).toBe("mailpit");
85
+ });
86
+
87
+ test("formats event messages and status dots", () => {
88
+ expect(
89
+ formatComposeUpEventMessage({
90
+ raw: "Image postgres:16 Pulling",
91
+ target: "postgres:16",
92
+ phase: "pulling",
93
+ }),
94
+ ).toBe("postgres pulling…");
95
+ expect(composeUpPhaseLabel("started")).toBe("started");
96
+ expect(composeUpPhaseStatus("pulling")).toBe("pending");
97
+ expect(composeUpPhaseStatus("started")).toBe("ready");
98
+ expect(composeUpPhaseStatus("error")).toBe("error");
99
+ });
100
+ });