okengine 0.18.5 → 0.19.1
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.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +208 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +235 -0
- package/site/content/docs/elements/ai/index.mdx +342 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +257 -0
- package/site/content/docs/elements/channel/email.mdx +269 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +301 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +448 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +375 -0
- package/site/content/docs/elements/index.mdx +20 -10
- package/site/content/docs/elements/signal/broadcast.mdx +519 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +591 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +597 -0
- package/site/content/docs/elements/store/files.mdx +667 -0
- package/site/content/docs/elements/store/index.mdx +313 -0
- package/site/content/docs/elements/store/kv.mdx +637 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +950 -0
- package/site/content/docs/elements/store/sql.mdx +941 -0
- package/site/content/docs/elements/vault/config.mdx +259 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +276 -0
- package/site/content/docs/elements/vault/secrets.mdx +288 -0
- package/site/content/docs/index.mdx +52 -40
- package/site/content/docs/meta.json +8 -7
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +138 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-architecture.mdx +264 -0
- package/site/content/docs/understand/try-it.mdx +58 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +80 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +257 -43
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-C_qLDhTq.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BHVqr3TN.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-CKe92lRQ.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-DEwTl8ZX.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-BCBtDrCt.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-JroqeuCp.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-CVHa0RTt.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-DjJW6hqe.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-DC5HBdLU.js +1 -0
- package/src/console/ui-next/dist/assets/index-CYjiZ3WO.js +63 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/observability-page-CAYMyKb3.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-yAQYLv75.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-D0yusGxJ.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-BTKJeJ02.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-Bp-Yygs5.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DoaVDfAM.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-BRz7xyYL.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-3jQt-bOJ.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.test.ts +1 -1
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +5 -3
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +4 -5
- package/src/console/ui-next/src/features/flows/traces/trace-gates.ts +3 -6
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/gate/declare.ts +1 -1
- package/src/elements/gate.ts +1 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +214 -0
- package/src/elements/store/search-runtime.pglite.test.ts +186 -0
- package/src/elements/store/search-runtime.ts +314 -0
- package/src/elements/store/search.test.ts +226 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +52 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +2 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -144
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -114
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- package/src/drivers/ollama.ts +0 -14
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25F scoring — Robertson–Zaragoza with field weights applied to TF
|
|
3
|
+
* **before** saturation (not post-hoc score summing).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BM25_B, BM25_K1 } from "./search-errors.ts";
|
|
7
|
+
|
|
8
|
+
/** One field's contribution to a document. */
|
|
9
|
+
export interface Bm25FieldTerms {
|
|
10
|
+
readonly weight: number;
|
|
11
|
+
/** term → raw term frequency in this field */
|
|
12
|
+
readonly tf: ReadonlyMap<string, number>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* BM25F score for one document given query terms and corpus stats.
|
|
17
|
+
*
|
|
18
|
+
* @param queryTerms - Tokenized query (unique terms scored once)
|
|
19
|
+
* @param fields - Per-field TF maps with BM25F weights
|
|
20
|
+
* @param docLen - Total document length (sum of field token counts)
|
|
21
|
+
* @param avgdl - Corpus average document length
|
|
22
|
+
* @param N - Corpus size
|
|
23
|
+
* @param df - Document frequency per term
|
|
24
|
+
*/
|
|
25
|
+
export function bm25fScore(
|
|
26
|
+
queryTerms: readonly string[],
|
|
27
|
+
fields: readonly Bm25FieldTerms[],
|
|
28
|
+
docLen: number,
|
|
29
|
+
avgdl: number,
|
|
30
|
+
N: number,
|
|
31
|
+
df: ReadonlyMap<string, number>,
|
|
32
|
+
): number {
|
|
33
|
+
if (N <= 0 || avgdl <= 0) return 0;
|
|
34
|
+
let score = 0;
|
|
35
|
+
const seen = new Set<string>();
|
|
36
|
+
for (const term of queryTerms) {
|
|
37
|
+
if (seen.has(term)) continue;
|
|
38
|
+
seen.add(term);
|
|
39
|
+
let weightedTf = 0;
|
|
40
|
+
for (const field of fields) {
|
|
41
|
+
const tf = field.tf.get(term) ?? 0;
|
|
42
|
+
if (tf > 0) weightedTf += field.weight * tf;
|
|
43
|
+
}
|
|
44
|
+
if (weightedTf <= 0) continue;
|
|
45
|
+
const n = df.get(term) ?? 0;
|
|
46
|
+
const idf = Math.log(1 + (N - n + 0.5) / (n + 0.5));
|
|
47
|
+
const denom = weightedTf + BM25_K1 * (1 - BM25_B + BM25_B * (docLen / avgdl));
|
|
48
|
+
score += idf * ((weightedTf * (BM25_K1 + 1)) / denom);
|
|
49
|
+
}
|
|
50
|
+
return score;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Simple whitespace + lower-case tokenizer for BM25 (English-oriented).
|
|
55
|
+
* Used for in-process scoring and tests; SQL path uses to_tsvector terms.
|
|
56
|
+
*
|
|
57
|
+
* @param text - Raw text
|
|
58
|
+
*/
|
|
59
|
+
export function tokenize(text: string): string[] {
|
|
60
|
+
return text
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, " ")
|
|
63
|
+
.split(/\s+/)
|
|
64
|
+
.filter((t) => t.length > 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build a TF map from tokens.
|
|
69
|
+
*
|
|
70
|
+
* @param tokens - Token list
|
|
71
|
+
*/
|
|
72
|
+
export function termFrequencies(tokens: readonly string[]): Map<string, number> {
|
|
73
|
+
const tf = new Map<string, number>();
|
|
74
|
+
for (const t of tokens) {
|
|
75
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
76
|
+
}
|
|
77
|
+
return tf;
|
|
78
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-SQL DDL for built-in hybrid search (PostgreSQL 15+, zero extensions).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { DeclaredColumn } from "../../manifest/types.ts";
|
|
6
|
+
import { LSH_DEFAULT_K } from "./search-errors.ts";
|
|
7
|
+
import { generateHyperplanes, hyperplaneSeed, serializePlanes } from "./search-lsh.ts";
|
|
8
|
+
|
|
9
|
+
/** Shadow / system column and index names. */
|
|
10
|
+
export const OKE_TSV_COL = "__oke_tsv";
|
|
11
|
+
export const OKE_SEARCH_STATS = "oke_search_stats";
|
|
12
|
+
export const OKE_SEARCH_DF = "oke_search_df";
|
|
13
|
+
export const OKE_SEARCH_PLANES = "oke_search_planes";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param sqlName - Column SQL name
|
|
17
|
+
*/
|
|
18
|
+
export function embColumn(sqlName: string): string {
|
|
19
|
+
return `__oke_emb_${sqlName}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param sqlName - Column SQL name
|
|
24
|
+
*/
|
|
25
|
+
export function lshColumn(sqlName: string): string {
|
|
26
|
+
return `__oke_lsh_${sqlName}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Emit CREATE TABLE / ALTER / INDEX statements for searchable columns.
|
|
31
|
+
*
|
|
32
|
+
* @param table - SQL table name
|
|
33
|
+
* @param columns - Manifest columns map
|
|
34
|
+
*/
|
|
35
|
+
export function searchDdlForTable(
|
|
36
|
+
table: string,
|
|
37
|
+
columns: Record<string, DeclaredColumn>,
|
|
38
|
+
): string[] {
|
|
39
|
+
const searchable: Array<{ key: string; sqlName: string; weight: number }> = [];
|
|
40
|
+
const embedCols: Array<{ key: string; sqlName: string; dims: number; model?: string }> = [];
|
|
41
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
42
|
+
if (!col.searchable) continue;
|
|
43
|
+
const sqlName = col.sqlName ?? key;
|
|
44
|
+
searchable.push({ key, sqlName, weight: col.searchable.weight });
|
|
45
|
+
if (col.embed) {
|
|
46
|
+
embedCols.push({
|
|
47
|
+
key,
|
|
48
|
+
sqlName,
|
|
49
|
+
dims: col.embed.dims,
|
|
50
|
+
...(col.embed.model ? { model: col.embed.model } : {}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (searchable.length === 0) return [];
|
|
55
|
+
|
|
56
|
+
const stmts: string[] = [
|
|
57
|
+
`CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_STATS} (
|
|
58
|
+
table_name text PRIMARY KEY,
|
|
59
|
+
n bigint NOT NULL DEFAULT 0,
|
|
60
|
+
avgdl double precision NOT NULL DEFAULT 0,
|
|
61
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
62
|
+
)`,
|
|
63
|
+
`CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_DF} (
|
|
64
|
+
table_name text NOT NULL,
|
|
65
|
+
term text NOT NULL,
|
|
66
|
+
df bigint NOT NULL DEFAULT 0,
|
|
67
|
+
PRIMARY KEY (table_name, term)
|
|
68
|
+
)`,
|
|
69
|
+
`CREATE TABLE IF NOT EXISTS ${OKE_SEARCH_PLANES} (
|
|
70
|
+
table_name text NOT NULL,
|
|
71
|
+
column_name text NOT NULL,
|
|
72
|
+
dims integer NOT NULL,
|
|
73
|
+
k integer NOT NULL,
|
|
74
|
+
seed text NOT NULL,
|
|
75
|
+
planes bytea NOT NULL,
|
|
76
|
+
PRIMARY KEY (table_name, column_name)
|
|
77
|
+
)`,
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// Weighted tsvector expression for GIN candidate retrieval.
|
|
81
|
+
const tsvParts = searchable.map(
|
|
82
|
+
(c) => `coalesce(to_tsvector('english', ${quoteIdent(c.sqlName)}), '')`,
|
|
83
|
+
);
|
|
84
|
+
const tsvExpr = tsvParts.join(" || ");
|
|
85
|
+
stmts.push(
|
|
86
|
+
`ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${OKE_TSV_COL} tsvector GENERATED ALWAYS AS (${tsvExpr}) STORED`,
|
|
87
|
+
);
|
|
88
|
+
stmts.push(
|
|
89
|
+
`CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_${OKE_TSV_COL}_gin`)} ON ${quoteIdent(table)} USING gin (${OKE_TSV_COL})`,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
for (const c of embedCols) {
|
|
93
|
+
stmts.push(
|
|
94
|
+
`ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${quoteIdent(embColumn(c.sqlName))} real[]`,
|
|
95
|
+
);
|
|
96
|
+
stmts.push(
|
|
97
|
+
`ALTER TABLE ${quoteIdent(table)} ADD COLUMN IF NOT EXISTS ${quoteIdent(lshColumn(c.sqlName))} bigint`,
|
|
98
|
+
);
|
|
99
|
+
stmts.push(
|
|
100
|
+
`CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_${lshColumn(c.sqlName)}_btree`)} ON ${quoteIdent(table)} (${quoteIdent(lshColumn(c.sqlName))})`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return stmts;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Insert-once hyperplane rows for embed columns (never regenerate).
|
|
109
|
+
*
|
|
110
|
+
* @param table - SQL table
|
|
111
|
+
* @param columns - Manifest columns
|
|
112
|
+
*/
|
|
113
|
+
export function ensureHyperplaneInserts(
|
|
114
|
+
table: string,
|
|
115
|
+
columns: Record<string, DeclaredColumn>,
|
|
116
|
+
): Array<{ sql: string; params: unknown[] }> {
|
|
117
|
+
const out: Array<{ sql: string; params: unknown[] }> = [];
|
|
118
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
119
|
+
if (!col.embed) continue;
|
|
120
|
+
const sqlName = col.sqlName ?? key;
|
|
121
|
+
const dims = col.embed.dims;
|
|
122
|
+
const k = LSH_DEFAULT_K;
|
|
123
|
+
const seed = hyperplaneSeed(table, sqlName, dims, k);
|
|
124
|
+
const planes = generateHyperplanes(seed, dims, k);
|
|
125
|
+
const blob = serializePlanes(planes);
|
|
126
|
+
const hex = blob.toString("hex");
|
|
127
|
+
out.push({
|
|
128
|
+
sql: `INSERT INTO ${OKE_SEARCH_PLANES} (table_name, column_name, dims, k, seed, planes)
|
|
129
|
+
VALUES (?, ?, ?, ?, ?, decode(?, 'hex'))
|
|
130
|
+
ON CONFLICT (table_name, column_name) DO NOTHING`,
|
|
131
|
+
params: [table, sqlName, dims, k, seed, hex],
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Minimum PostgreSQL major version for hybrid search DDL.
|
|
139
|
+
*/
|
|
140
|
+
export const SEARCH_PG_MIN_MAJOR = 15;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param versionString - `SELECT version()` / `server_version` text
|
|
144
|
+
*/
|
|
145
|
+
export function parsePgMajor(versionString: string): number | undefined {
|
|
146
|
+
const m = /(\d+)\.\d+/.exec(versionString);
|
|
147
|
+
if (!m) return undefined;
|
|
148
|
+
return Number(m[1]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function quoteIdent(name: string): string {
|
|
152
|
+
if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
|
|
153
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
154
|
+
}
|
|
155
|
+
return name;
|
|
156
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System-owned durable CDC flow that embeds `.embed()` columns asynchronously.
|
|
3
|
+
* Writer flows never carry `effects.embeds` — only this flow does.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DeclaredColumn, Manifest } from "../../manifest/types.ts";
|
|
7
|
+
import type { Fx } from "../../kernel/fx.ts";
|
|
8
|
+
import { LSH_DEFAULT_K, SearchConfigError } from "./search-errors.ts";
|
|
9
|
+
import { embColumn, lshColumn, OKE_SEARCH_PLANES } from "./search-ddl.ts";
|
|
10
|
+
import { deserializePlanes, lshBucket, lshBucketToSql } from "./search-lsh.ts";
|
|
11
|
+
|
|
12
|
+
/** CDC payload shape from bindRealtimeBridge / dispatchCdc. */
|
|
13
|
+
export interface SearchEmbedCdcPayload {
|
|
14
|
+
readonly before: Record<string, unknown> | null;
|
|
15
|
+
readonly after: Record<string, unknown> | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* System flow name for a searchable-embed table.
|
|
20
|
+
*
|
|
21
|
+
* @param table - SQL table name
|
|
22
|
+
*/
|
|
23
|
+
export function searchEmbedFlowName(table: string): string {
|
|
24
|
+
return `_oke_search_embed_${table}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Collect tables that need the system embed CDC flow.
|
|
29
|
+
*
|
|
30
|
+
* @param manifest - Project manifest
|
|
31
|
+
*/
|
|
32
|
+
export function tablesNeedingSearchEmbed(manifest: Manifest): Array<{
|
|
33
|
+
readonly table: string;
|
|
34
|
+
readonly store: string;
|
|
35
|
+
readonly columns: Array<{ sqlName: string; dims: number; model?: string }>;
|
|
36
|
+
readonly pk: string;
|
|
37
|
+
}> {
|
|
38
|
+
const out: Array<{
|
|
39
|
+
readonly table: string;
|
|
40
|
+
readonly store: string;
|
|
41
|
+
readonly columns: Array<{ sqlName: string; dims: number; model?: string }>;
|
|
42
|
+
readonly pk: string;
|
|
43
|
+
}> = [];
|
|
44
|
+
for (const [storeName, store] of Object.entries(manifest.stores ?? {})) {
|
|
45
|
+
if (store.facet !== "sql" || !store.tables) continue;
|
|
46
|
+
for (const [tableName, table] of Object.entries(store.tables)) {
|
|
47
|
+
const embedCols: Array<{ sqlName: string; dims: number; model?: string }> = [];
|
|
48
|
+
let pk = "id";
|
|
49
|
+
for (const [key, col] of Object.entries(table.columns ?? {})) {
|
|
50
|
+
if (!col || typeof col !== "object") continue;
|
|
51
|
+
const c = col as DeclaredColumn;
|
|
52
|
+
if (c.primaryKey) pk = c.sqlName ?? key;
|
|
53
|
+
if (c.embed) {
|
|
54
|
+
embedCols.push({
|
|
55
|
+
sqlName: c.sqlName ?? key,
|
|
56
|
+
dims: c.embed.dims,
|
|
57
|
+
...(c.embed.model ? { model: c.embed.model } : {}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (embedCols.length > 0) {
|
|
62
|
+
out.push({ table: tableName, store: storeName, columns: embedCols, pk });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Run one embed step for a CDC event (call inside `fx.step`).
|
|
71
|
+
*
|
|
72
|
+
* @param fx - Flow fx
|
|
73
|
+
* @param table - Table name
|
|
74
|
+
* @param pk - Primary key SQL name
|
|
75
|
+
* @param columns - Embed columns
|
|
76
|
+
* @param payload - CDC before/after
|
|
77
|
+
* @param sqlRef - Resource ref for raw SQL (`sql:app`)
|
|
78
|
+
*/
|
|
79
|
+
export async function applySearchEmbedCdc(
|
|
80
|
+
fx: Fx,
|
|
81
|
+
table: string,
|
|
82
|
+
pk: string,
|
|
83
|
+
columns: Array<{ sqlName: string; dims: number; model?: string }>,
|
|
84
|
+
payload: SearchEmbedCdcPayload,
|
|
85
|
+
sqlRef: `sql:${string}`,
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
const store = fx.store({
|
|
88
|
+
kind: "sql",
|
|
89
|
+
name: sqlRef.slice(4),
|
|
90
|
+
ref: sqlRef,
|
|
91
|
+
facet: "sql",
|
|
92
|
+
} as never);
|
|
93
|
+
|
|
94
|
+
if (!payload.after) {
|
|
95
|
+
// Delete — embeddings cleared with the row; nothing to do.
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const id = String(payload.after[pk] ?? "");
|
|
100
|
+
if (!id) return;
|
|
101
|
+
|
|
102
|
+
for (const col of columns) {
|
|
103
|
+
const text = String(payload.after[col.sqlName] ?? "");
|
|
104
|
+
const beforeText =
|
|
105
|
+
payload.before && typeof payload.before[col.sqlName] === "string"
|
|
106
|
+
? String(payload.before[col.sqlName])
|
|
107
|
+
: undefined;
|
|
108
|
+
if (beforeText !== undefined && beforeText === text) continue;
|
|
109
|
+
|
|
110
|
+
const model = col.model ?? "default";
|
|
111
|
+
const vector = await fx.embed(model, text);
|
|
112
|
+
if (vector.length !== col.dims) {
|
|
113
|
+
throw new SearchConfigError(
|
|
114
|
+
table,
|
|
115
|
+
col.sqlName,
|
|
116
|
+
`embedding length ${vector.length} !== declared dims ${col.dims}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const planesRows = await store.raw(
|
|
121
|
+
`SELECT k, planes FROM ${OKE_SEARCH_PLANES} WHERE table_name = ? AND column_name = ?`,
|
|
122
|
+
[table, col.sqlName],
|
|
123
|
+
);
|
|
124
|
+
const prow = planesRows[0];
|
|
125
|
+
if (!prow) {
|
|
126
|
+
throw new SearchConfigError(
|
|
127
|
+
table,
|
|
128
|
+
col.sqlName,
|
|
129
|
+
"missing hyperplanes — ensure search DDL / search-backfill ran",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const k = Number(prow["k"] ?? LSH_DEFAULT_K);
|
|
133
|
+
const planes = deserializePlanes(Buffer.from(prow["planes"] as Buffer), k);
|
|
134
|
+
const bucket = lshBucket(vector, planes);
|
|
135
|
+
|
|
136
|
+
await store.raw(
|
|
137
|
+
`UPDATE ${table} SET ${embColumn(col.sqlName)} = ?::real[], ${lshColumn(col.sqlName)} = ? WHERE ${pk} = ?`,
|
|
138
|
+
[`{${Array.from(vector).join(",")}}`, lshBucketToSql(bucket), id],
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-loud errors for built-in hybrid SQL search.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Thrown when `.embed()` / search config is invalid or AI is missing. */
|
|
6
|
+
export class SearchConfigError extends Error {
|
|
7
|
+
override readonly name = "SearchConfigError";
|
|
8
|
+
readonly table: string;
|
|
9
|
+
readonly column: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param table - SQL table name
|
|
13
|
+
* @param column - JS or SQL column key
|
|
14
|
+
* @param message - Human-readable fix
|
|
15
|
+
*/
|
|
16
|
+
constructor(table: string, column: string, message: string) {
|
|
17
|
+
super(`SearchConfigError: ${table}.${column}: ${message}`);
|
|
18
|
+
this.table = table;
|
|
19
|
+
this.column = column;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Corpus size below which IDF / BM25 stats are not meaningful. */
|
|
24
|
+
export const SEARCH_LOW_CORPUS_WARN_N = 100;
|
|
25
|
+
|
|
26
|
+
/** Default RRF damping constant (Cormack, Clarke, Büttcher — SIGIR 2009). */
|
|
27
|
+
export const RRF_DEFAULT_K = 60;
|
|
28
|
+
|
|
29
|
+
/** Fixed LSH hyperplane count (fits bigint bit packing). */
|
|
30
|
+
export const LSH_DEFAULT_K = 64;
|
|
31
|
+
|
|
32
|
+
/** BM25 saturation / length-norm defaults (Robertson–Zaragoza). */
|
|
33
|
+
export const BM25_K1 = 1.2;
|
|
34
|
+
export const BM25_B = 0.75;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rank fusion — RRF (default) and weighted combination (opt-in).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RRF_DEFAULT_K } from "./search-errors.ts";
|
|
6
|
+
|
|
7
|
+
/** One ranked list entry (1-based rank preferred for RRF). */
|
|
8
|
+
export interface RankedHit {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly score: number;
|
|
11
|
+
readonly rank: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type FuseStrategy = "rrf" | "weighted";
|
|
15
|
+
|
|
16
|
+
export interface FuseOptions {
|
|
17
|
+
readonly strategy?: FuseStrategy;
|
|
18
|
+
/** RRF damping constant — default {@link RRF_DEFAULT_K} (Cormack et al. 2009). */
|
|
19
|
+
readonly k?: number;
|
|
20
|
+
/** Weighted fusion only. */
|
|
21
|
+
readonly weights?: { readonly bm25?: number; readonly vector?: number };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reciprocal Rank Fusion: Σ 1/(k + rank).
|
|
26
|
+
*
|
|
27
|
+
* @param lists - Named ranked lists (e.g. bm25, vector)
|
|
28
|
+
* @param k - Damping constant
|
|
29
|
+
*/
|
|
30
|
+
export function fuseRrf(
|
|
31
|
+
lists: ReadonlyMap<string, readonly RankedHit[]>,
|
|
32
|
+
k: number = RRF_DEFAULT_K,
|
|
33
|
+
): RankedHit[] {
|
|
34
|
+
const scores = new Map<string, number>();
|
|
35
|
+
for (const list of lists.values()) {
|
|
36
|
+
for (const hit of list) {
|
|
37
|
+
const contrib = 1 / (k + hit.rank);
|
|
38
|
+
scores.set(hit.id, (scores.get(hit.id) ?? 0) + contrib);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return toRanked(scores);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Weighted linear combination of raw scores (min-max normalized per list).
|
|
46
|
+
*
|
|
47
|
+
* @param lists - Named scored lists
|
|
48
|
+
* @param weights - Per-engine weights
|
|
49
|
+
*/
|
|
50
|
+
export function fuseWeighted(
|
|
51
|
+
lists: ReadonlyMap<string, readonly RankedHit[]>,
|
|
52
|
+
weights: { readonly bm25?: number; readonly vector?: number } = {},
|
|
53
|
+
): RankedHit[] {
|
|
54
|
+
const wBm25 = weights.bm25 ?? 0.5;
|
|
55
|
+
const wVec = weights.vector ?? 0.5;
|
|
56
|
+
const norm = normalizeLists(lists);
|
|
57
|
+
const scores = new Map<string, number>();
|
|
58
|
+
for (const [name, list] of norm) {
|
|
59
|
+
const w = name === "vector" ? wVec : name === "bm25" ? wBm25 : 1;
|
|
60
|
+
for (const hit of list) {
|
|
61
|
+
scores.set(hit.id, (scores.get(hit.id) ?? 0) + w * hit.score);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return toRanked(scores);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Apply {@link FuseOptions} — omitted / rrf → RRF with k=60.
|
|
69
|
+
*
|
|
70
|
+
* @param lists - Ranked lists
|
|
71
|
+
* @param fuse - Options
|
|
72
|
+
*/
|
|
73
|
+
export function fuseLists(
|
|
74
|
+
lists: ReadonlyMap<string, readonly RankedHit[]>,
|
|
75
|
+
fuse?: FuseOptions,
|
|
76
|
+
): { hits: RankedHit[]; strategy: FuseStrategy; k?: number } {
|
|
77
|
+
const strategy = fuse?.strategy ?? "rrf";
|
|
78
|
+
if (strategy === "weighted") {
|
|
79
|
+
return { hits: fuseWeighted(lists, fuse?.weights), strategy };
|
|
80
|
+
}
|
|
81
|
+
const k = fuse?.k ?? RRF_DEFAULT_K;
|
|
82
|
+
return { hits: fuseRrf(lists, k), strategy, k };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeLists(
|
|
86
|
+
lists: ReadonlyMap<string, readonly RankedHit[]>,
|
|
87
|
+
): Map<string, RankedHit[]> {
|
|
88
|
+
const out = new Map<string, RankedHit[]>();
|
|
89
|
+
for (const [name, list] of lists) {
|
|
90
|
+
let min = Infinity;
|
|
91
|
+
let max = -Infinity;
|
|
92
|
+
for (const h of list) {
|
|
93
|
+
if (h.score < min) min = h.score;
|
|
94
|
+
if (h.score > max) max = h.score;
|
|
95
|
+
}
|
|
96
|
+
const span = max - min || 1;
|
|
97
|
+
out.set(
|
|
98
|
+
name,
|
|
99
|
+
list.map((h) => ({
|
|
100
|
+
id: h.id,
|
|
101
|
+
rank: h.rank,
|
|
102
|
+
score: (h.score - min) / span,
|
|
103
|
+
})),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function toRanked(scores: Map<string, number>): RankedHit[] {
|
|
110
|
+
const sorted = [...scores.entries()].sort((a, b) => b[1]! - a[1]!);
|
|
111
|
+
return sorted.map(([id, score], i) => ({ id, score, rank: i + 1 }));
|
|
112
|
+
}
|