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