@happyvertical/smrt-inference 0.60.15 → 0.60.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/AGENTS.md +10 -5
  2. package/agents/fusions.md +16 -3
  3. package/agents/hosted.md +29 -13
  4. package/agents/settings.md +30 -7
  5. package/dist/chunks/{InferenceOverrideCollection-wl7sI-3C.js → InferenceOverrideCollection-Dat4TrK5.js} +29 -4
  6. package/dist/chunks/InferenceOverrideCollection-Dat4TrK5.js.map +1 -0
  7. package/dist/chunks/{provider-config-4Bx8N52X.js → provider-config-BSpIQRVM.js} +37 -11
  8. package/dist/chunks/{provider-config-4Bx8N52X.js.map → provider-config-BSpIQRVM.js.map} +1 -1
  9. package/dist/fusion.d.ts.map +1 -1
  10. package/dist/hosted.d.ts +31 -24
  11. package/dist/hosted.d.ts.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/manifest.json +1 -1
  14. package/dist/models/InferenceOverride.d.ts.map +1 -1
  15. package/dist/models.js +1 -1
  16. package/dist/node.js +86 -33
  17. package/dist/node.js.map +1 -1
  18. package/dist/register.d.ts.map +1 -1
  19. package/dist/resolver.d.ts.map +1 -1
  20. package/dist/resolver.js +4 -1
  21. package/dist/resolver.js.map +1 -1
  22. package/dist/server.d.ts +3 -2
  23. package/dist/server.d.ts.map +1 -1
  24. package/dist/server.js +194 -41
  25. package/dist/server.js.map +1 -1
  26. package/dist/settings/layers.d.ts +14 -0
  27. package/dist/settings/layers.d.ts.map +1 -1
  28. package/dist/settings/override-service.d.ts.map +1 -1
  29. package/dist/settings/resolver.d.ts +7 -0
  30. package/dist/settings/resolver.d.ts.map +1 -1
  31. package/dist/settings/settings-service.d.ts +80 -7
  32. package/dist/settings/settings-service.d.ts.map +1 -1
  33. package/dist/settings/targets.d.ts +21 -2
  34. package/dist/settings/targets.d.ts.map +1 -1
  35. package/dist/settings/transcripts.d.ts +33 -0
  36. package/dist/settings/transcripts.d.ts.map +1 -0
  37. package/dist/smrt-knowledge.json +17 -11
  38. package/dist/smrt-knowledge.okf.md +2 -2
  39. package/dist/svelte/components/InferenceFusionList.svelte +6 -0
  40. package/dist/svelte/components/InferenceFusionList.svelte.d.ts.map +1 -1
  41. package/dist/svelte/components/InferenceSettingsPanel.svelte +49 -2
  42. package/dist/svelte/components/InferenceSettingsPanel.svelte.d.ts +21 -2
  43. package/dist/svelte/components/InferenceSettingsPanel.svelte.d.ts.map +1 -1
  44. package/dist/svelte/components/InferenceTranscriptSettings.svelte +165 -0
  45. package/dist/svelte/components/InferenceTranscriptSettings.svelte.d.ts +18 -0
  46. package/dist/svelte/components/InferenceTranscriptSettings.svelte.d.ts.map +1 -0
  47. package/dist/svelte/components/InferenceUsageTable.svelte +64 -14
  48. package/dist/svelte/components/InferenceUsageTable.svelte.d.ts +10 -4
  49. package/dist/svelte/components/InferenceUsageTable.svelte.d.ts.map +1 -1
  50. package/dist/svelte/index.d.ts +1 -1
  51. package/dist/svelte/index.d.ts.map +1 -1
  52. package/dist/svelte/index.js +1 -1
  53. package/dist/svelte/types.d.ts +66 -1
  54. package/dist/svelte/types.d.ts.map +1 -1
  55. package/dist/svelte/types.js +28 -0
  56. package/package.json +10 -10
  57. package/dist/chunks/InferenceOverrideCollection-wl7sI-3C.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAKL,KAAK,iBAAiB,EAGvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO1D;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,CAAC,EAAE,iBAAiB,GACvB,iBAAiB,CAuBnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,IAAI,CASN;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAKL,KAAK,iBAAiB,EAGvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAO1D;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,CAAC,EAAE,iBAAiB,GACvB,iBAAiB,CA8BnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,IAAI,CASN;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
package/dist/resolver.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as inferenceProviderBindingFor, c as inferenceProviderTargets, d as redactInferenceTarget, f as createInferenceResolver, i as InferenceProviderConfigError, l as parseInferenceProviderConfig, n as INFERENCE_PROVIDER_CONFIG_SOURCES, o as inferenceProviderConfigSecretName, r as INFERENCE_PROVIDER_PARAM_KEYS, s as inferenceProviderSource, t as INFERENCE_PROVIDER_CONFIG_SECRET_TYPE, u as redactInferenceProviderConfig } from "./chunks/provider-config-4Bx8N52X.js";
1
+ import { a as inferenceProviderBindingFor, c as inferenceProviderTargets, d as redactInferenceTarget, f as createInferenceResolver, i as InferenceProviderConfigError, l as parseInferenceProviderConfig, n as INFERENCE_PROVIDER_CONFIG_SOURCES, o as inferenceProviderConfigSecretName, r as INFERENCE_PROVIDER_PARAM_KEYS, s as inferenceProviderSource, t as INFERENCE_PROVIDER_CONFIG_SECRET_TYPE, u as redactInferenceProviderConfig } from "./chunks/provider-config-BSpIQRVM.js";
2
2
  import { isInferenceTargetSource, resolveAutoTier, setInferenceResolver } from "@happyvertical/smrt-core";
3
3
  import { SecretService } from "@happyvertical/smrt-secrets";
4
4
  import { getCurrentTenant, withTenant } from "@happyvertical/smrt-tenancy";
@@ -203,6 +203,9 @@ function createUserInferenceResolver(options = {}) {
203
203
  unavailable: "off"
204
204
  } : { mode: chosen.mode };
205
205
  },
206
+ async lookupFusion(key, request, context) {
207
+ return options.fallback?.lookupFusion?.(key, request, context);
208
+ },
206
209
  async resolveBindings(bindingOptions = {}) {
207
210
  const redact = bindingOptions.redact !== false;
208
211
  const { tenantId, userId } = identityFor({});
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.js","names":["userId","target","source"],"sources":["../src/resolver.ts"],"sourcesContent":["/**\n * `@happyvertical/smrt-inference/resolver` — per-user and per-tenant\n * inference bindings over smrt-secrets (#4756). Node-only: it reads secrets\n * and the ambient principal, so it stays off the browser-safe root entry.\n *\n * Precedence for each tier, highest first:\n * 1. the current user's stored `inference.provider-config` (tier binding,\n * then the user's default binding);\n * 2. the tenant's stored config (same order);\n * 3. `fallback` (another resolver), else the app's `inference` block;\n * 4. unbound: the configured client (`SMRT_AI_*` / the process `ai` block).\n * Every resolution carries `target.source` (`byo | local | hosted | app`),\n * which core records as the `source` usage tag.\n *\n * Inference mode (#4757): a stored config may carry `mode`. The user's mode,\n * else the tenant's, applies: `off` answers every tier (and untiered calls,\n * through `mode()`) as unavailable; `byo` / `local` / `hosted` bind only a\n * stored binding of that source (user, then tenant) and report\n * `unconfigured` when there is none, never falling through to app config.\n */\nimport {\n type InferenceMode,\n type InferenceModeRequest,\n type InferenceModeState,\n type InferenceRequest,\n type InferenceResolution,\n type InferenceResolver,\n type InferenceResolverContext,\n type InferenceTarget,\n type InferenceTier,\n isInferenceTargetSource,\n resolveAutoTier,\n setInferenceResolver,\n} from '@happyvertical/smrt-core';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { getCurrentSessionPermissionContext } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n type InferenceProviderConfig,\n inferenceProviderConfigSecretName,\n inferenceProviderTargets,\n parseInferenceProviderConfig,\n type RedactedInferenceProviderConfig,\n redactInferenceProviderConfig,\n redactInferenceTarget,\n} from './provider-config.js';\nimport { createInferenceResolver } from './register.js';\n\nexport * from './provider-config.js';\n\n/** `invalidate()` of every live resolver this process created. */\nconst liveResolvers = new Set<WeakRef<UserInferenceResolver['invalidate']>>();\n\n/**\n * Drop cached configs in every user resolver this process created (all, one\n * tenant, or one user). Writers outside a resolver (hosted provisioning,\n * #4759) call this after storing a config; other processes see the change\n * when their cache entry expires (`ttlMs`, 30 s by default).\n */\nexport function invalidateUserInferenceResolvers(scope?: {\n tenantId?: string;\n userId?: string;\n}): void {\n for (const ref of [...liveResolvers]) {\n const invalidate = ref.deref();\n if (invalidate) invalidate(scope);\n else liveResolvers.delete(ref);\n }\n}\n\n/** Default cache lifetime of decrypted configs, per `(tenantId, userId)`. */\nexport const USER_INFERENCE_CACHE_TTL_MS = 30_000;\n\n/**\n * The subset of `SecretService` the resolver uses. Reads go through\n * `retrieve()` (audited, access-counted); writes through `store()`.\n */\nexport interface InferenceSecretStore {\n retrieve(name: string): Promise<{ value: string }>;\n store(\n name: string,\n value: string,\n options?: { category?: string; description?: string },\n ): Promise<unknown>;\n delete(name: string): Promise<boolean>;\n}\n\n/** Who a resolution acts for. */\nexport interface InferencePrincipal {\n tenantId: string;\n /** Absent for service principals and background jobs: no user layer. */\n userId?: string;\n}\n\n/** Options for {@link createUserInferenceResolver}. */\nexport interface UserInferenceResolverOptions {\n /** Database for a `SecretService` built on first use (when no `secrets`). */\n db?: DatabaseInterface;\n /** The secret store; defaults to `SecretService.create({ db })`. */\n secrets?: InferenceSecretStore | SecretService;\n /**\n * Resolver for tiers neither the user nor the tenant binds. Default: the\n * app's `inference` block through core's built-in resolution.\n */\n fallback?: InferenceResolver;\n /** Cache lifetime in ms (default {@link USER_INFERENCE_CACHE_TTL_MS}). */\n ttlMs?: number;\n /** Clock seam for tests. */\n now?: () => number;\n /**\n * Principal seam for tests and hosts with their own identity. Default:\n * the ambient principal, read the way smrt-preferences reads it.\n */\n principal?: () => InferencePrincipal | undefined;\n /**\n * Let a request for another tenant than the principal's (an object of\n * tenant B used by a principal of tenant A) read tenant B's stored config.\n * Default `false`: such a request skips the stored layers and falls through\n * to the app config, because reading B's key for A's action is a\n * cross-tenant read. Principal-less contexts (jobs) always use the\n * request's tenant.\n */\n allowCrossTenant?: boolean;\n}\n\n/** The stored layers for one `(tenantId, userId)`. */\ninterface Layers {\n user?: InferenceProviderConfig;\n tenant?: InferenceProviderConfig;\n}\n\n/** The explicit mode that applies: the user's, else the tenant's. */\nfunction explicitMode(\n layers: Layers,\n): { mode: InferenceMode; layer: 'user' | 'tenant' } | undefined {\n if (layers.user?.mode) return { mode: layers.user.mode, layer: 'user' };\n if (layers.tenant?.mode) return { mode: layers.tenant.mode, layer: 'tenant' };\n return undefined;\n}\n\n/** One tier of {@link InferenceBindingMap}. */\nexport type InferenceBindingEntry = (\n | InferenceTarget\n | (Omit<InferenceTarget, 'apiKey'> & { hasApiKey: boolean })\n) & { layer: 'user' | 'tenant' };\n\n/** What {@link UserInferenceResolver.resolveBindings} returns. */\nexport interface InferenceBindingMap {\n tenantId?: string;\n userId?: string;\n /** Per tier: the stored binding that answers it (absent: app config). */\n tiers: Partial<Record<InferenceTier, InferenceBindingEntry>>;\n /** The stored configs themselves, redacted unless `redact: false`. */\n user?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n tenant?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n /** The explicit mode that applies (#4757) and the layer that set it. */\n mode?: { mode: InferenceMode; layer: 'user' | 'tenant' };\n}\n\n/** The scope a config is written to. */\nexport type InferenceConfigLevel = 'user' | 'tenant';\n\n/** A resolver plus the read/write helpers the settings panel uses. */\nexport interface UserInferenceResolver extends InferenceResolver {\n /**\n * The tier → target map for the ambient principal (never a caller-chosen\n * tenant or user), without\n * keys unless `redact: false` (server-only; never send that to a client).\n */\n resolveBindings(options?: { redact?: boolean }): Promise<InferenceBindingMap>;\n /**\n * Validate and store a config for the current principal through\n * `SecretService.store()`, then invalidate the cache. `user` needs a\n * principal with a user id. Authorization (who may write the tenant\n * config) is the host's server action's job.\n */\n write(level: InferenceConfigLevel, config: unknown): Promise<void>;\n /** Delete the stored config at `level` for the current principal. */\n remove(level: InferenceConfigLevel): Promise<boolean>;\n /**\n * Set (or with `null` clear) the stored mode at `level` (#4757), keeping\n * the stored binding. Works without a key: a mode-only config is valid.\n * A config left with neither binding nor mode is deleted. Invalidates the\n * cache, so the next call sees it.\n */\n setMode(\n level: InferenceConfigLevel,\n mode: InferenceMode | null,\n ): Promise<void>;\n /** The explicit stored mode for untiered calls (core's `mode()` hook). */\n mode(request: InferenceModeRequest): Promise<InferenceModeState | undefined>;\n /**\n * Drop cached configs: all, one tenant's (every user), or one\n * `(tenantId, userId)`. Call after writing a config elsewhere.\n */\n invalidate(scope?: { tenantId?: string; userId?: string }): void;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== ''\n ? value.trim()\n : undefined;\n}\n\n/**\n * The ambient principal: the tenancy context first, then the users session\n * permission context (smrt-preferences' order). Never guessed.\n */\nexport function getAmbientInferencePrincipal(): InferencePrincipal | undefined {\n const tenant = getCurrentTenant();\n const tenantId = text(tenant?.tenantId);\n if (tenantId) {\n const userId = text(tenant?.userId);\n return userId ? { tenantId, userId } : { tenantId };\n }\n const session = getCurrentSessionPermissionContext();\n const sessionTenant = text(session?.tenantId);\n if (!sessionTenant) return undefined;\n const userId = text(session?.userId);\n return userId\n ? { tenantId: sessionTenant, userId }\n : { tenantId: sessionTenant };\n}\n\nfunction isAbsentSecretError(error: unknown, name: string): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.message === `Secret '${name}' not found` ||\n error.message === 'Secret not found' ||\n error.message === 'Secret disabled' ||\n error.message === 'Secret expired'\n );\n}\n\nconst cacheKey = (tenantId: string, userId: string | undefined) =>\n `${tenantId}\\u0000${userId ?? ''}`;\n\n/**\n * Create the per-user / per-tenant {@link InferenceResolver}. Register it\n * with {@link registerUserInferenceResolver} (keeps #4752's validation in\n * front) or `setInferenceResolver()`.\n */\nexport function createUserInferenceResolver(\n options: UserInferenceResolverOptions = {},\n): UserInferenceResolver {\n const ttlMs = options.ttlMs ?? USER_INFERENCE_CACHE_TTL_MS;\n const now = options.now ?? Date.now;\n const principalOf = options.principal ?? getAmbientInferencePrincipal;\n const cache = new Map<string, { expires: number; value: Promise<Layers> }>();\n let secretsPromise: Promise<InferenceSecretStore> | undefined;\n\n const getSecrets = (): Promise<InferenceSecretStore> => {\n if (options.secrets) return Promise.resolve(options.secrets);\n if (!options.db) {\n return Promise.reject(\n new Error(\n 'createUserInferenceResolver needs `secrets` or `db` to read stored inference configs.',\n ),\n );\n }\n secretsPromise ??= SecretService.create({ db: options.db }).catch(\n (error: unknown) => {\n secretsPromise = undefined;\n throw error;\n },\n );\n return secretsPromise;\n };\n\n /** Run `fn` in a tenancy context for `principal` (audit keeps the user). */\n const inTenant = <T>(\n principal: InferencePrincipal,\n fn: () => Promise<T>,\n ): Promise<T> => {\n const current = getCurrentTenant();\n if (\n text(current?.tenantId) === principal.tenantId &&\n text(current?.userId) === principal.userId\n ) {\n return fn();\n }\n return withTenant(\n {\n tenantId: principal.tenantId,\n ...(principal.userId ? { userId: principal.userId } : {}),\n },\n fn,\n );\n };\n\n const readConfig = async (\n secrets: InferenceSecretStore,\n name: string,\n ): Promise<InferenceProviderConfig | undefined> => {\n let value: string;\n try {\n value = (await secrets.retrieve(name)).value;\n } catch (error) {\n if (isAbsentSecretError(error, name)) return undefined;\n throw error;\n }\n return parseInferenceProviderConfig(value);\n };\n\n const loadLayers = async (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const secrets = await getSecrets();\n return inTenant({ tenantId, ...(userId ? { userId } : {}) }, async () => {\n const layers: Layers = {};\n if (userId) {\n layers.user = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({ userId }),\n );\n }\n layers.tenant = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({}),\n );\n return layers;\n });\n };\n\n const getLayers = (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const key = cacheKey(tenantId, userId);\n const hit = cache.get(key);\n if (hit && hit.expires > now()) return hit.value;\n const value = loadLayers(tenantId, userId);\n const entry = { expires: now() + ttlMs, value };\n cache.set(key, entry);\n value.catch(() => {\n if (cache.get(key) === entry) cache.delete(key);\n });\n return value;\n };\n\n /**\n * Whose stored layers answer this request. With a principal: its tenant\n * (or the request's when they match), its user unless the request says\n * `userId: null` (a client shared beyond one user, e.g. a long-lived\n * agent). A request for another tenant reads nothing stored unless\n * `allowCrossTenant`. Without a principal: the request's tenant, no user.\n */\n const identityFor = (\n request: Pick<InferenceRequest, 'tenantId' | 'userId'>,\n ): { tenantId?: string; userId?: string } => {\n const principal = principalOf();\n const requested = text(request.tenantId);\n if (!principal) return requested ? { tenantId: requested } : {};\n if (requested && requested !== principal.tenantId) {\n return options.allowCrossTenant ? { tenantId: requested } : {};\n }\n const tenantId = principal.tenantId;\n const userId = request.userId === null ? undefined : text(principal.userId);\n return userId ? { tenantId, userId } : { tenantId };\n };\n\n const invalidate: UserInferenceResolver['invalidate'] = (scope) => {\n if (!scope?.tenantId) {\n cache.clear();\n return;\n }\n if (scope.userId) {\n cache.delete(cacheKey(scope.tenantId, scope.userId));\n return;\n }\n const prefix = `${scope.tenantId}\\u0000`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n };\n\n const writeScope = (level: InferenceConfigLevel): InferencePrincipal => {\n const principal = principalOf();\n if (!principal?.tenantId) {\n throw new Error(\n 'Writing an inference provider config needs an authenticated tenant context.',\n );\n }\n if (level === 'user' && !principal.userId) {\n throw new Error(\n 'Writing a user inference provider config needs a principal with a user id.',\n );\n }\n return principal;\n };\n\n liveResolvers.add(new WeakRef(invalidate));\n\n return {\n async resolve(\n request: InferenceRequest,\n context: InferenceResolverContext,\n ): Promise<InferenceResolution | undefined> {\n const tierResolved =\n request.tier === 'auto'\n ? resolveAutoTier(context.config)\n : request.tier;\n const { tenantId, userId } = identityFor(request);\n if (tenantId) {\n const layers = await getLayers(tenantId, userId);\n const chosen = explicitMode(layers);\n if (chosen?.mode === 'off') {\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'off',\n };\n }\n const candidates = [\n inferenceProviderTargets(layers.user)[tierResolved],\n inferenceProviderTargets(layers.tenant)[tierResolved],\n ];\n if (chosen) {\n // An explicit mode binds only its own kind of stored binding.\n const target = candidates.find(\n (entry) => entry?.source === chosen.mode,\n );\n if (target) {\n return { tierResolved, target, via: 'resolver', mode: chosen.mode };\n }\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'unconfigured',\n };\n }\n const target = candidates.find(Boolean);\n if (target) {\n const source = target.source;\n return {\n tierResolved,\n target,\n via: 'resolver',\n ...(source === 'byo' || source === 'local' || source === 'hosted'\n ? { mode: source }\n : {}),\n };\n }\n }\n const fallen =\n (await options.fallback?.resolve(request, context)) ??\n context.resolveFromConfig(request);\n const source = fallen.target?.source;\n return {\n ...fallen,\n target: {\n ...fallen.target,\n source: isInferenceTargetSource(source) ? source : 'app',\n },\n };\n },\n\n async mode(request) {\n const { tenantId, userId } = identityFor(request);\n if (!tenantId) return undefined;\n const chosen = explicitMode(await getLayers(tenantId, userId));\n if (!chosen) return undefined;\n return chosen.mode === 'off'\n ? { mode: 'off', unavailable: 'off' }\n : { mode: chosen.mode };\n },\n\n async resolveBindings(bindingOptions = {}) {\n const redact = bindingOptions.redact !== false;\n // Identity is ambient only: no caller-chosen tenant or user.\n const { tenantId, userId } = identityFor({});\n if (!tenantId) return { tiers: {} };\n const layers = await getLayers(tenantId, userId);\n const map: InferenceBindingMap = {\n tenantId,\n ...(userId ? { userId } : {}),\n tiers: {},\n };\n for (const layer of ['tenant', 'user'] as const) {\n const config = layers[layer];\n if (!config) continue;\n map[layer] = redact ? redactInferenceProviderConfig(config) : config;\n for (const [tier, target] of Object.entries(\n inferenceProviderTargets(config),\n )) {\n map.tiers[tier as InferenceTier] = {\n ...(redact ? redactInferenceTarget(target) : target),\n layer,\n };\n }\n }\n const chosen = explicitMode(layers);\n if (chosen) map.mode = chosen;\n return map;\n },\n\n async write(level, config) {\n const principal = writeScope(level);\n const parsed = parseInferenceProviderConfig(config);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n await inTenant(principal, () =>\n secrets.store(name, JSON.stringify(parsed), {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n }),\n );\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n async remove(level) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n const removed = await inTenant(principal, () => secrets.delete(name));\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n return removed;\n },\n\n async setMode(level, mode) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n // Read fresh (not the cache) so a concurrent binding write is kept.\n const current = await inTenant(principal, () =>\n readConfig(secrets, name),\n );\n const next: InferenceProviderConfig = { ...(current ?? {}) };\n if (mode === null) delete next.mode;\n else next.mode = parseInferenceProviderConfig({ mode }).mode;\n const empty = Object.keys(next).every(\n (key) =>\n key === 'version' || key === 'source' || key === 'gatewayKeyAlias',\n );\n await inTenant(principal, async () => {\n if (empty) {\n if (current) await secrets.delete(name);\n return;\n }\n await secrets.store(\n name,\n JSON.stringify(parseInferenceProviderConfig(next)),\n {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n },\n );\n });\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n invalidate,\n };\n}\n\n/**\n * Register `resolver` behind smrt-inference's validating resolver (#4752):\n * chain config is validated first, then the per-user resolver binds, then\n * its `fallback` / the app config. Safe in either import order: a later\n * `registerInference()` keeps this registration.\n */\nexport function registerUserInferenceResolver(\n resolver: UserInferenceResolver,\n): UserInferenceResolver {\n setInferenceResolver(createInferenceResolver(resolver));\n return resolver;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolver.js","names":["userId","target","source"],"sources":["../src/resolver.ts"],"sourcesContent":["/**\n * `@happyvertical/smrt-inference/resolver` — per-user and per-tenant\n * inference bindings over smrt-secrets (#4756). Node-only: it reads secrets\n * and the ambient principal, so it stays off the browser-safe root entry.\n *\n * Precedence for each tier, highest first:\n * 1. the current user's stored `inference.provider-config` (tier binding,\n * then the user's default binding);\n * 2. the tenant's stored config (same order);\n * 3. `fallback` (another resolver), else the app's `inference` block;\n * 4. unbound: the configured client (`SMRT_AI_*` / the process `ai` block).\n * Every resolution carries `target.source` (`byo | local | hosted | app`),\n * which core records as the `source` usage tag.\n *\n * Inference mode (#4757): a stored config may carry `mode`. The user's mode,\n * else the tenant's, applies: `off` answers every tier (and untiered calls,\n * through `mode()`) as unavailable; `byo` / `local` / `hosted` bind only a\n * stored binding of that source (user, then tenant) and report\n * `unconfigured` when there is none, never falling through to app config.\n */\nimport {\n type InferenceMode,\n type InferenceModeRequest,\n type InferenceModeState,\n type InferenceRequest,\n type InferenceResolution,\n type InferenceResolver,\n type InferenceResolverContext,\n type InferenceTarget,\n type InferenceTier,\n isInferenceTargetSource,\n resolveAutoTier,\n setInferenceResolver,\n} from '@happyvertical/smrt-core';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { getCurrentSessionPermissionContext } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n type InferenceProviderConfig,\n inferenceProviderConfigSecretName,\n inferenceProviderTargets,\n parseInferenceProviderConfig,\n type RedactedInferenceProviderConfig,\n redactInferenceProviderConfig,\n redactInferenceTarget,\n} from './provider-config.js';\nimport { createInferenceResolver } from './register.js';\n\nexport * from './provider-config.js';\n\n/** `invalidate()` of every live resolver this process created. */\nconst liveResolvers = new Set<WeakRef<UserInferenceResolver['invalidate']>>();\n\n/**\n * Drop cached configs in every user resolver this process created (all, one\n * tenant, or one user). Writers outside a resolver (hosted provisioning,\n * #4759) call this after storing a config; other processes see the change\n * when their cache entry expires (`ttlMs`, 30 s by default).\n */\nexport function invalidateUserInferenceResolvers(scope?: {\n tenantId?: string;\n userId?: string;\n}): void {\n for (const ref of [...liveResolvers]) {\n const invalidate = ref.deref();\n if (invalidate) invalidate(scope);\n else liveResolvers.delete(ref);\n }\n}\n\n/** Default cache lifetime of decrypted configs, per `(tenantId, userId)`. */\nexport const USER_INFERENCE_CACHE_TTL_MS = 30_000;\n\n/**\n * The subset of `SecretService` the resolver uses. Reads go through\n * `retrieve()` (audited, access-counted); writes through `store()`.\n */\nexport interface InferenceSecretStore {\n retrieve(name: string): Promise<{ value: string }>;\n store(\n name: string,\n value: string,\n options?: { category?: string; description?: string },\n ): Promise<unknown>;\n delete(name: string): Promise<boolean>;\n}\n\n/** Who a resolution acts for. */\nexport interface InferencePrincipal {\n tenantId: string;\n /** Absent for service principals and background jobs: no user layer. */\n userId?: string;\n}\n\n/** Options for {@link createUserInferenceResolver}. */\nexport interface UserInferenceResolverOptions {\n /** Database for a `SecretService` built on first use (when no `secrets`). */\n db?: DatabaseInterface;\n /** The secret store; defaults to `SecretService.create({ db })`. */\n secrets?: InferenceSecretStore | SecretService;\n /**\n * Resolver for tiers neither the user nor the tenant binds. Default: the\n * app's `inference` block through core's built-in resolution.\n */\n fallback?: InferenceResolver;\n /** Cache lifetime in ms (default {@link USER_INFERENCE_CACHE_TTL_MS}). */\n ttlMs?: number;\n /** Clock seam for tests. */\n now?: () => number;\n /**\n * Principal seam for tests and hosts with their own identity. Default:\n * the ambient principal, read the way smrt-preferences reads it.\n */\n principal?: () => InferencePrincipal | undefined;\n /**\n * Let a request for another tenant than the principal's (an object of\n * tenant B used by a principal of tenant A) read tenant B's stored config.\n * Default `false`: such a request skips the stored layers and falls through\n * to the app config, because reading B's key for A's action is a\n * cross-tenant read. Principal-less contexts (jobs) always use the\n * request's tenant.\n */\n allowCrossTenant?: boolean;\n}\n\n/** The stored layers for one `(tenantId, userId)`. */\ninterface Layers {\n user?: InferenceProviderConfig;\n tenant?: InferenceProviderConfig;\n}\n\n/** The explicit mode that applies: the user's, else the tenant's. */\nfunction explicitMode(\n layers: Layers,\n): { mode: InferenceMode; layer: 'user' | 'tenant' } | undefined {\n if (layers.user?.mode) return { mode: layers.user.mode, layer: 'user' };\n if (layers.tenant?.mode) return { mode: layers.tenant.mode, layer: 'tenant' };\n return undefined;\n}\n\n/** One tier of {@link InferenceBindingMap}. */\nexport type InferenceBindingEntry = (\n | InferenceTarget\n | (Omit<InferenceTarget, 'apiKey'> & { hasApiKey: boolean })\n) & { layer: 'user' | 'tenant' };\n\n/** What {@link UserInferenceResolver.resolveBindings} returns. */\nexport interface InferenceBindingMap {\n tenantId?: string;\n userId?: string;\n /** Per tier: the stored binding that answers it (absent: app config). */\n tiers: Partial<Record<InferenceTier, InferenceBindingEntry>>;\n /** The stored configs themselves, redacted unless `redact: false`. */\n user?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n tenant?: InferenceProviderConfig | RedactedInferenceProviderConfig;\n /** The explicit mode that applies (#4757) and the layer that set it. */\n mode?: { mode: InferenceMode; layer: 'user' | 'tenant' };\n}\n\n/** The scope a config is written to. */\nexport type InferenceConfigLevel = 'user' | 'tenant';\n\n/** A resolver plus the read/write helpers the settings panel uses. */\nexport interface UserInferenceResolver extends InferenceResolver {\n /**\n * The tier → target map for the ambient principal (never a caller-chosen\n * tenant or user), without\n * keys unless `redact: false` (server-only; never send that to a client).\n */\n resolveBindings(options?: { redact?: boolean }): Promise<InferenceBindingMap>;\n /**\n * Validate and store a config for the current principal through\n * `SecretService.store()`, then invalidate the cache. `user` needs a\n * principal with a user id. Authorization (who may write the tenant\n * config) is the host's server action's job.\n */\n write(level: InferenceConfigLevel, config: unknown): Promise<void>;\n /** Delete the stored config at `level` for the current principal. */\n remove(level: InferenceConfigLevel): Promise<boolean>;\n /**\n * Set (or with `null` clear) the stored mode at `level` (#4757), keeping\n * the stored binding. Works without a key: a mode-only config is valid.\n * A config left with neither binding nor mode is deleted. Invalidates the\n * cache, so the next call sees it.\n */\n setMode(\n level: InferenceConfigLevel,\n mode: InferenceMode | null,\n ): Promise<void>;\n /** The explicit stored mode for untiered calls (core's `mode()` hook). */\n mode(request: InferenceModeRequest): Promise<InferenceModeState | undefined>;\n /**\n * Drop cached configs: all, one tenant's (every user), or one\n * `(tenantId, userId)`. Call after writing a config elsewhere.\n */\n invalidate(scope?: { tenantId?: string; userId?: string }): void;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== ''\n ? value.trim()\n : undefined;\n}\n\n/**\n * The ambient principal: the tenancy context first, then the users session\n * permission context (smrt-preferences' order). Never guessed.\n */\nexport function getAmbientInferencePrincipal(): InferencePrincipal | undefined {\n const tenant = getCurrentTenant();\n const tenantId = text(tenant?.tenantId);\n if (tenantId) {\n const userId = text(tenant?.userId);\n return userId ? { tenantId, userId } : { tenantId };\n }\n const session = getCurrentSessionPermissionContext();\n const sessionTenant = text(session?.tenantId);\n if (!sessionTenant) return undefined;\n const userId = text(session?.userId);\n return userId\n ? { tenantId: sessionTenant, userId }\n : { tenantId: sessionTenant };\n}\n\nfunction isAbsentSecretError(error: unknown, name: string): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.message === `Secret '${name}' not found` ||\n error.message === 'Secret not found' ||\n error.message === 'Secret disabled' ||\n error.message === 'Secret expired'\n );\n}\n\nconst cacheKey = (tenantId: string, userId: string | undefined) =>\n `${tenantId}\\u0000${userId ?? ''}`;\n\n/**\n * Create the per-user / per-tenant {@link InferenceResolver}. Register it\n * with {@link registerUserInferenceResolver} (keeps #4752's validation in\n * front) or `setInferenceResolver()`.\n */\nexport function createUserInferenceResolver(\n options: UserInferenceResolverOptions = {},\n): UserInferenceResolver {\n const ttlMs = options.ttlMs ?? USER_INFERENCE_CACHE_TTL_MS;\n const now = options.now ?? Date.now;\n const principalOf = options.principal ?? getAmbientInferencePrincipal;\n const cache = new Map<string, { expires: number; value: Promise<Layers> }>();\n let secretsPromise: Promise<InferenceSecretStore> | undefined;\n\n const getSecrets = (): Promise<InferenceSecretStore> => {\n if (options.secrets) return Promise.resolve(options.secrets);\n if (!options.db) {\n return Promise.reject(\n new Error(\n 'createUserInferenceResolver needs `secrets` or `db` to read stored inference configs.',\n ),\n );\n }\n secretsPromise ??= SecretService.create({ db: options.db }).catch(\n (error: unknown) => {\n secretsPromise = undefined;\n throw error;\n },\n );\n return secretsPromise;\n };\n\n /** Run `fn` in a tenancy context for `principal` (audit keeps the user). */\n const inTenant = <T>(\n principal: InferencePrincipal,\n fn: () => Promise<T>,\n ): Promise<T> => {\n const current = getCurrentTenant();\n if (\n text(current?.tenantId) === principal.tenantId &&\n text(current?.userId) === principal.userId\n ) {\n return fn();\n }\n return withTenant(\n {\n tenantId: principal.tenantId,\n ...(principal.userId ? { userId: principal.userId } : {}),\n },\n fn,\n );\n };\n\n const readConfig = async (\n secrets: InferenceSecretStore,\n name: string,\n ): Promise<InferenceProviderConfig | undefined> => {\n let value: string;\n try {\n value = (await secrets.retrieve(name)).value;\n } catch (error) {\n if (isAbsentSecretError(error, name)) return undefined;\n throw error;\n }\n return parseInferenceProviderConfig(value);\n };\n\n const loadLayers = async (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const secrets = await getSecrets();\n return inTenant({ tenantId, ...(userId ? { userId } : {}) }, async () => {\n const layers: Layers = {};\n if (userId) {\n layers.user = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({ userId }),\n );\n }\n layers.tenant = await readConfig(\n secrets,\n inferenceProviderConfigSecretName({}),\n );\n return layers;\n });\n };\n\n const getLayers = (\n tenantId: string,\n userId: string | undefined,\n ): Promise<Layers> => {\n const key = cacheKey(tenantId, userId);\n const hit = cache.get(key);\n if (hit && hit.expires > now()) return hit.value;\n const value = loadLayers(tenantId, userId);\n const entry = { expires: now() + ttlMs, value };\n cache.set(key, entry);\n value.catch(() => {\n if (cache.get(key) === entry) cache.delete(key);\n });\n return value;\n };\n\n /**\n * Whose stored layers answer this request. With a principal: its tenant\n * (or the request's when they match), its user unless the request says\n * `userId: null` (a client shared beyond one user, e.g. a long-lived\n * agent). A request for another tenant reads nothing stored unless\n * `allowCrossTenant`. Without a principal: the request's tenant, no user.\n */\n const identityFor = (\n request: Pick<InferenceRequest, 'tenantId' | 'userId'>,\n ): { tenantId?: string; userId?: string } => {\n const principal = principalOf();\n const requested = text(request.tenantId);\n if (!principal) return requested ? { tenantId: requested } : {};\n if (requested && requested !== principal.tenantId) {\n return options.allowCrossTenant ? { tenantId: requested } : {};\n }\n const tenantId = principal.tenantId;\n const userId = request.userId === null ? undefined : text(principal.userId);\n return userId ? { tenantId, userId } : { tenantId };\n };\n\n const invalidate: UserInferenceResolver['invalidate'] = (scope) => {\n if (!scope?.tenantId) {\n cache.clear();\n return;\n }\n if (scope.userId) {\n cache.delete(cacheKey(scope.tenantId, scope.userId));\n return;\n }\n const prefix = `${scope.tenantId}\\u0000`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n };\n\n const writeScope = (level: InferenceConfigLevel): InferencePrincipal => {\n const principal = principalOf();\n if (!principal?.tenantId) {\n throw new Error(\n 'Writing an inference provider config needs an authenticated tenant context.',\n );\n }\n if (level === 'user' && !principal.userId) {\n throw new Error(\n 'Writing a user inference provider config needs a principal with a user id.',\n );\n }\n return principal;\n };\n\n liveResolvers.add(new WeakRef(invalidate));\n\n return {\n async resolve(\n request: InferenceRequest,\n context: InferenceResolverContext,\n ): Promise<InferenceResolution | undefined> {\n const tierResolved =\n request.tier === 'auto'\n ? resolveAutoTier(context.config)\n : request.tier;\n const { tenantId, userId } = identityFor(request);\n if (tenantId) {\n const layers = await getLayers(tenantId, userId);\n const chosen = explicitMode(layers);\n if (chosen?.mode === 'off') {\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'off',\n };\n }\n const candidates = [\n inferenceProviderTargets(layers.user)[tierResolved],\n inferenceProviderTargets(layers.tenant)[tierResolved],\n ];\n if (chosen) {\n // An explicit mode binds only its own kind of stored binding.\n const target = candidates.find(\n (entry) => entry?.source === chosen.mode,\n );\n if (target) {\n return { tierResolved, target, via: 'resolver', mode: chosen.mode };\n }\n return {\n tierResolved,\n target: {},\n via: 'resolver',\n mode: 'off',\n unavailable: 'unconfigured',\n };\n }\n const target = candidates.find(Boolean);\n if (target) {\n const source = target.source;\n return {\n tierResolved,\n target,\n via: 'resolver',\n ...(source === 'byo' || source === 'local' || source === 'hosted'\n ? { mode: source }\n : {}),\n };\n }\n }\n const fallen =\n (await options.fallback?.resolve(request, context)) ??\n context.resolveFromConfig(request);\n const source = fallen.target?.source;\n return {\n ...fallen,\n target: {\n ...fallen.target,\n source: isInferenceTargetSource(source) ? source : 'app',\n },\n };\n },\n\n async mode(request) {\n const { tenantId, userId } = identityFor(request);\n if (!tenantId) return undefined;\n const chosen = explicitMode(await getLayers(tenantId, userId));\n if (!chosen) return undefined;\n return chosen.mode === 'off'\n ? { mode: 'off', unavailable: 'off' }\n : { mode: chosen.mode };\n },\n\n // Fusions are app-scope only (#4753): stored user and tenant configs\n // never define one, so the lookup is the fallback's (override rows,\n // #4800) or none (core then reads `inference.fusions`).\n async lookupFusion(key, request, context) {\n return options.fallback?.lookupFusion?.(key, request, context);\n },\n\n async resolveBindings(bindingOptions = {}) {\n const redact = bindingOptions.redact !== false;\n // Identity is ambient only: no caller-chosen tenant or user.\n const { tenantId, userId } = identityFor({});\n if (!tenantId) return { tiers: {} };\n const layers = await getLayers(tenantId, userId);\n const map: InferenceBindingMap = {\n tenantId,\n ...(userId ? { userId } : {}),\n tiers: {},\n };\n for (const layer of ['tenant', 'user'] as const) {\n const config = layers[layer];\n if (!config) continue;\n map[layer] = redact ? redactInferenceProviderConfig(config) : config;\n for (const [tier, target] of Object.entries(\n inferenceProviderTargets(config),\n )) {\n map.tiers[tier as InferenceTier] = {\n ...(redact ? redactInferenceTarget(target) : target),\n layer,\n };\n }\n }\n const chosen = explicitMode(layers);\n if (chosen) map.mode = chosen;\n return map;\n },\n\n async write(level, config) {\n const principal = writeScope(level);\n const parsed = parseInferenceProviderConfig(config);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n await inTenant(principal, () =>\n secrets.store(name, JSON.stringify(parsed), {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n }),\n );\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n async remove(level) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n const removed = await inTenant(principal, () => secrets.delete(name));\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n return removed;\n },\n\n async setMode(level, mode) {\n const principal = writeScope(level);\n const secrets = await getSecrets();\n const name = inferenceProviderConfigSecretName(\n level === 'user' ? { userId: principal.userId } : {},\n );\n // Read fresh (not the cache) so a concurrent binding write is kept.\n const current = await inTenant(principal, () =>\n readConfig(secrets, name),\n );\n const next: InferenceProviderConfig = { ...(current ?? {}) };\n if (mode === null) delete next.mode;\n else next.mode = parseInferenceProviderConfig({ mode }).mode;\n const empty = Object.keys(next).every(\n (key) =>\n key === 'version' || key === 'source' || key === 'gatewayKeyAlias',\n );\n await inTenant(principal, async () => {\n if (empty) {\n if (current) await secrets.delete(name);\n return;\n }\n await secrets.store(\n name,\n JSON.stringify(parseInferenceProviderConfig(next)),\n {\n category: INFERENCE_PROVIDER_CONFIG_SECRET_TYPE,\n description: `Inference provider config (${level})`,\n },\n );\n });\n invalidate(\n level === 'user'\n ? { tenantId: principal.tenantId, userId: principal.userId }\n : { tenantId: principal.tenantId },\n );\n },\n\n invalidate,\n };\n}\n\n/**\n * Register `resolver` behind smrt-inference's validating resolver (#4752):\n * chain config is validated first, then the per-user resolver binds, then\n * its `fallback` / the app config. Safe in either import order: a later\n * `registerInference()` keeps this registration.\n */\nexport function registerUserInferenceResolver(\n resolver: UserInferenceResolver,\n): UserInferenceResolver {\n setInferenceResolver(createInferenceResolver(resolver));\n return resolver;\n}\n"],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -19,6 +19,7 @@ export { buildInferenceOverrideLayers, type InferenceOverrideLayers, type Infere
19
19
  export { assertValidInferenceScope, InferenceOverrideAuthorizationError, type InferenceOverrideAuthorizer, InferenceOverrideService, type InferenceOverrideServiceOptions, type InferenceOverrideWriteRequest, InferenceTenantTargetNotAllowedError, type InferenceTenantTargetPolicy, type InferenceTenantTargetRequest, InvalidInferenceScopeError, } from './settings/override-service.js';
20
20
  export { CUSTOMIZE_INFERENCE_PERMISSION, createInferencePreferences, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, type InferencePreferenceIssue, type InferencePreferenceRead, type InferencePreferenceStoreLike, type InferencePreferences, type InferencePreferencesApi, inferencePreferenceKind, PERSONALIZE_INFERENCE_PERMISSION, validateInferencePreferences, } from './settings/preferences.js';
21
21
  export { createInferenceOverrideResolver, type InferenceOverrideResolverOptions, } from './settings/resolver.js';
22
- export { type InferenceAliasSetting, InferenceBindingInUseError, type InferenceConnectionInput, type InferenceConnectionResult, type InferenceDefaultTierSetting, type InferenceFusionSetting, type InferenceProviderOption, InferenceSettingsConfigError, type InferenceSettingsLevel, InferenceSettingsService, type InferenceSettingsServiceOptions, type InferenceTierSettingsRow, type RegisteredFusionLike, } from './settings/settings-service.js';
23
- export { DEFAULT_TIER_BINDING, describeInferenceTarget, INFERENCE_MODES, type InferenceBindingId, InferenceBindingValueError, type InferenceMode, inferenceTargetRef, isAllowedByList, matchesAllowPattern, parseInferenceBinding, stripKeyMembers, tierBinding, } from './settings/targets.js';
22
+ export { type InferenceAliasSetting, InferenceBindingInUseError, type InferenceConnectionInput, type InferenceConnectionResult, type InferenceDefaultTierSetting, type InferenceFusionSetting, type InferenceProviderOption, InferenceSettingsConfigError, type InferenceSettingsLevel, InferenceSettingsService, type InferenceSettingsServiceOptions, type InferenceTierSettingsRow, type InferenceTranscriptSettings, type InferenceUsageReportDimension, type RegisteredFusionLike, } from './settings/settings-service.js';
23
+ export { DEFAULT_TIER_BINDING, describeInferenceTarget, INFERENCE_MODES, type InferenceBindingId, InferenceBindingValueError, type InferenceMode, type InferenceTranscriptSettingsValue, inferenceTargetRef, isAllowedByList, matchesAllowPattern, normalizeTranscriptSettings, parseInferenceBinding, stripKeyMembers, TRANSCRIPTS_BINDING, tierBinding, } from './settings/targets.js';
24
+ export { applyStoredTranscriptSettings, loadInferenceTranscriptSettings, storedTranscriptSettings, } from './settings/transcripts.js';
24
25
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,eAAe,EACf,kBAAkB,EAClB,8BAA8B,EAC9B,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,2BAA2B,EAC3B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,oCAAoC,EACpC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,gCAAgC,EAChC,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,+BAA+B,EAC/B,KAAK,gCAAgC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,qBAAqB,EAC1B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,KAAK,kBAAkB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,WAAW,GACZ,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,eAAe,EACf,kBAAkB,EAClB,8BAA8B,EAC9B,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,2BAA2B,EAC3B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,mCAAmC,EACnC,KAAK,2BAA2B,EAChC,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,oCAAoC,EACpC,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,gCAAgC,EAChC,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,+BAA+B,EAC/B,KAAK,gCAAgC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,qBAAqB,EAC1B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,KAAK,kBAAkB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,gCAAgC,EACrC,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,2BAA2B,CAAC"}
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
- import { F as findInferenceConfigIssues, R as validateInferenceTarget, b as listFusions, y as getFusion } from "./chunks/provider-config-4Bx8N52X.js";
1
+ import { B as InferenceConfigError, I as findInferenceConfigIssues, N as findFusionDefinitionIssues, P as findFusionDefinitionPromptIssues, b as listFusions, y as getFusion, z as validateInferenceTarget } from "./chunks/provider-config-BSpIQRVM.js";
2
2
  import { checkInferenceBindings, inferenceProbeUrl, probeInferenceCandidate } from "./node.js";
3
- import { _ as normalizeTierTarget, a as INFERENCE_BINDING_NAME_PATTERN, b as tierBinding, c as describeInferenceTarget, d as isAllowedByList, f as matchesAllowPattern, g as normalizeFusionDefinition, h as normalizeDefaultTier, i as DEFAULT_TIER_BINDING, l as findKeyMember, m as normalizeAllowList, n as APP_INFERENCE_SCOPE_ID, o as INFERENCE_MODES, p as normalizeAliasDefinition, r as InferenceOverride, s as InferenceBindingValueError, t as InferenceOverrideCollection, u as inferenceTargetRef, v as parseInferenceBinding, x as getInferenceOverrideGeneration, y as stripKeyMembers } from "./chunks/InferenceOverrideCollection-wl7sI-3C.js";
4
- import { INFERENCE_PRESETS, INFERENCE_PRESET_IDS, INFERENCE_TIERS, getInferenceConfig, inferenceTargetKind, isInferencePresetId, isInferenceTier, mergeInferenceConfigs, reportAiUsage, resolveAutoTier } from "@happyvertical/smrt-core";
3
+ import { C as getInferenceOverrideGeneration, S as tierBinding, _ as normalizeFusionDefinition, a as INFERENCE_BINDING_NAME_PATTERN, b as parseInferenceBinding, c as TRANSCRIPTS_BINDING, d as inferenceTargetRef, f as isAllowedByList, g as normalizeDefaultTier, h as normalizeAllowList, i as DEFAULT_TIER_BINDING, l as describeInferenceTarget, m as normalizeAliasDefinition, n as APP_INFERENCE_SCOPE_ID, o as INFERENCE_MODES, p as matchesAllowPattern, r as InferenceOverride, s as InferenceBindingValueError, t as InferenceOverrideCollection, u as findKeyMember, v as normalizeTierTarget, x as stripKeyMembers, y as normalizeTranscriptSettings } from "./chunks/InferenceOverrideCollection-Dat4TrK5.js";
4
+ import { DEFAULT_RETENTION_POLICY, DEFAULT_TRANSCRIPT_MAX_BYTES, DEFAULT_TRANSCRIPT_SAMPLE, INFERENCE_PRESETS, INFERENCE_PRESET_IDS, INFERENCE_TIERS, config, getInferenceConfig, getInferenceTranscriptsConfig, inferenceTargetKind, isInferencePresetId, isInferenceTier, mergeInferenceConfigs, reportAiUsage, resolveAutoTier, setAiTranscriptAppSettings } from "@happyvertical/smrt-core";
5
5
  import { AI_PROVIDER_TYPES } from "@happyvertical/ai";
6
6
  import { getDefaultAIKeyEnvName } from "@happyvertical/smrt-config";
7
7
  //#region src/settings/keys.ts
@@ -160,6 +160,7 @@ function normalizeBindingValue(binding, value) {
160
160
  switch (parsed.kind) {
161
161
  case "tier": return normalizeTierTarget(binding, value);
162
162
  case "default-tier": return normalizeDefaultTier(binding, value);
163
+ case "transcripts": return normalizeTranscriptSettings(binding, value);
163
164
  case "alias": return normalizeAliasDefinition(binding, value);
164
165
  case "fusion": return normalizeFusionDefinition(binding, value);
165
166
  }
@@ -209,6 +210,7 @@ var InferenceOverrideService = class {
209
210
  const normalized = normalizeBindingValue(binding, value);
210
211
  const parsed = parseInferenceBinding(binding);
211
212
  if (scopeType === "tenant" && (parsed?.kind === "alias" || parsed?.kind === "fusion")) throw new InferenceBindingValueError(binding, "aliases and fusions are defined at the app scope only");
213
+ if (scopeType === "tenant" && parsed?.kind === "transcripts") throw new InferenceBindingValueError(binding, "the transcript setting is app scope only");
212
214
  await this.require({
213
215
  operation: normalized === null ? "remove" : "set",
214
216
  binding,
@@ -287,6 +289,9 @@ function layerFromRows(rows, include) {
287
289
  case "default-tier":
288
290
  layer.defaultTier = value;
289
291
  break;
292
+ case "transcripts":
293
+ layer.transcripts = value;
294
+ break;
290
295
  case "alias":
291
296
  layer.aliases ??= {};
292
297
  layer.aliases[parsed.name] = value;
@@ -299,6 +304,22 @@ function layerFromRows(rows, include) {
299
304
  }
300
305
  return layer;
301
306
  }
307
+ function isUnreadableFusionRow(row) {
308
+ if (!row || row.target === null || row.target === void 0) return false;
309
+ try {
310
+ row.getValue();
311
+ return false;
312
+ } catch {
313
+ return true;
314
+ }
315
+ }
316
+ function unreadableFusionIssue(name) {
317
+ return {
318
+ path: `fusion:${name}`,
319
+ code: "fusion-invalid",
320
+ message: `app row fusion:${name} cannot be read (not a JSON object, or a credential-shaped member); fix or remove it`
321
+ };
322
+ }
302
323
  async function readInferenceOverrideRows(overrides, tenantId) {
303
324
  const tenant = tenantId ?? null;
304
325
  const [appRows, tenantRows] = await Promise.all([overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID), tenant ? overrides.getScopeMap("tenant", tenant) : Promise.resolve(/* @__PURE__ */ new Map())]);
@@ -479,22 +500,61 @@ function createInferencePreferences(store, surfaceId = INFERENCE_PREFERENCE_SURF
479
500
  };
480
501
  }
481
502
  //#endregion
503
+ //#region src/settings/transcripts.ts
504
+ function storedTranscriptSettings(appRows) {
505
+ const row = appRows.get(TRANSCRIPTS_BINDING);
506
+ if (!row) return null;
507
+ try {
508
+ return row.getValue() ?? null;
509
+ } catch {
510
+ return null;
511
+ }
512
+ }
513
+ function applyStoredTranscriptSettings(appRows) {
514
+ const value = storedTranscriptSettings(appRows);
515
+ setAiTranscriptAppSettings(value);
516
+ return value;
517
+ }
518
+ async function loadInferenceTranscriptSettings(options = {}) {
519
+ return applyStoredTranscriptSettings(await (options.overrides ?? await InferenceOverrideCollection.create({
520
+ ...options.db ?? {},
521
+ defaultListLimit: void 0,
522
+ maxListLimit: void 0
523
+ })).getScopeMap("app", APP_INFERENCE_SCOPE_ID));
524
+ }
525
+ //#endregion
482
526
  //#region src/settings/resolver.ts
483
527
  var MAX_CACHED_TENANTS = 500;
484
528
  function expandStoredAliases(target, appAliases, configAliases) {
485
- if (inferenceTargetKind(target) !== "alias") return target;
486
- const name = target.alias;
487
- if (!Object.hasOwn(appAliases, name)) return target;
488
529
  const expand = (alias, seen) => {
489
530
  if (seen.has(alias)) return [];
490
531
  const next = new Set(seen).add(alias);
491
- return ((appAliases[alias] ?? configAliases[alias])?.candidates ?? []).flatMap((candidate) => candidate.alias && Object.hasOwn(appAliases, candidate.alias) ? expand(candidate.alias, next) : [candidate]);
532
+ const definition = appAliases[alias] ?? configAliases[alias];
533
+ return inline(definition?.candidates ?? [], next);
534
+ };
535
+ const inline = (candidates, seen) => candidates.flatMap((candidate) => candidate.alias && Object.hasOwn(appAliases, candidate.alias) ? expand(candidate.alias, seen) : [candidate]);
536
+ const kind = inferenceTargetKind(target);
537
+ if (kind === "candidates") return {
538
+ ...target,
539
+ candidates: inline(target.candidates ?? [], /* @__PURE__ */ new Set())
492
540
  };
541
+ if (kind !== "alias") return target;
542
+ const name = target.alias;
543
+ if (!Object.hasOwn(appAliases, name)) return target;
493
544
  return {
494
545
  kind: "candidates",
495
546
  candidates: expand(name, /* @__PURE__ */ new Set())
496
547
  };
497
548
  }
549
+ function expandStoredFusionAliases(definition, appAliases, configAliases) {
550
+ return {
551
+ ...definition,
552
+ seats: definition.seats.map((seat) => typeof seat.target === "string" ? { ...seat } : {
553
+ ...seat,
554
+ target: expandStoredAliases({ ...seat.target }, appAliases, configAliases)
555
+ })
556
+ };
557
+ }
498
558
  function createInferenceOverrideResolver(options = {}) {
499
559
  const ttlMs = options.ttlMs ?? 3e4;
500
560
  const cache = /* @__PURE__ */ new Map();
@@ -526,28 +586,44 @@ function createInferenceOverrideResolver(options = {}) {
526
586
  }
527
587
  return rows;
528
588
  };
529
- return { async resolve(request, context) {
530
- const tenantId = typeof request.tenantId === "string" && request.tenantId ? request.tenantId : null;
531
- const layers = await buildInferenceOverrideLayers(await rowsFor(tenantId), options.tenantTargetPolicy);
532
- const merged = mergeSettingsConfigs(context.config, layers.app, layers.tenant);
533
- const tier = request.tier === "auto" ? resolveAutoTier(merged) : request.tier;
534
- const stored = layers.tenant.tiers?.[tier] ?? layers.app.tiers?.[tier];
535
- if (!stored) {
536
- const next = {
537
- ...request,
538
- tier
589
+ return {
590
+ async resolve(request, context) {
591
+ const tenantId = typeof request.tenantId === "string" && request.tenantId ? request.tenantId : null;
592
+ const rows = await rowsFor(tenantId);
593
+ applyStoredTranscriptSettings(rows.appRows);
594
+ const layers = await buildInferenceOverrideLayers(rows, options.tenantTargetPolicy);
595
+ const merged = mergeSettingsConfigs(context.config, layers.app, layers.tenant);
596
+ const tier = request.tier === "auto" ? resolveAutoTier(merged) : request.tier;
597
+ const stored = layers.tenant.tiers?.[tier] ?? layers.app.tiers?.[tier];
598
+ if (!stored) {
599
+ const next = {
600
+ ...request,
601
+ tier
602
+ };
603
+ const deferred = options.base ? await options.base.resolve(next, context) : void 0;
604
+ if (deferred !== void 0) return deferred;
605
+ return request.tier === "auto" ? context.resolveFromConfig(next) : void 0;
606
+ }
607
+ validateInferenceTarget(stored, `inference.tiers.${tier}`, merged);
608
+ return {
609
+ tierResolved: tier,
610
+ target: expandStoredAliases({ ...stored }, layers.app.aliases ?? {}, context.config?.aliases ?? {}),
611
+ via: "resolver"
539
612
  };
540
- const deferred = options.base ? await options.base.resolve(next, context) : void 0;
541
- if (deferred !== void 0) return deferred;
542
- return request.tier === "auto" ? context.resolveFromConfig(next) : void 0;
613
+ },
614
+ async lookupFusion(key, request, context) {
615
+ const rows = await rowsFor(null);
616
+ if (isUnreadableFusionRow(rows.appRows.get(`fusion:${key}`))) throw new InferenceConfigError([unreadableFusionIssue(key)]);
617
+ const app = (await buildInferenceOverrideLayers(rows)).app;
618
+ const stored = Object.hasOwn(app.fusions ?? {}, key) ? app.fusions?.[key] : void 0;
619
+ if (!stored) return options.base?.lookupFusion ? options.base.lookupFusion(key, request, context) : void 0;
620
+ const merged = mergeSettingsConfigs(context.config, app);
621
+ const path = `fusion:${key}`;
622
+ const issues = [...findFusionDefinitionIssues(stored, path, merged), ...findFusionDefinitionPromptIssues(stored, path)];
623
+ if (issues.length > 0) throw new InferenceConfigError(issues);
624
+ return expandStoredFusionAliases(stored, app.aliases ?? {}, context.config?.aliases ?? {});
543
625
  }
544
- validateInferenceTarget(stored, `inference.tiers.${tier}`, merged);
545
- return {
546
- tierResolved: tier,
547
- target: expandStoredAliases({ ...stored }, layers.app.aliases ?? {}, context.config?.aliases ?? {}),
548
- via: "resolver"
549
- };
550
- } };
626
+ };
551
627
  }
552
628
  //#endregion
553
629
  //#region src/settings/settings-service.ts
@@ -619,6 +695,10 @@ function bindingRefersTo(binding, value, kind, name) {
619
695
  if (kind === "alias" && binding.startsWith("alias:") && binding !== `alias:${name}`) return Array.isArray(value.candidates) && value.candidates.some((candidate) => isRecord(candidate) && candidate.alias === name);
620
696
  return false;
621
697
  }
698
+ function transcriptRetentionDays(policy) {
699
+ if (policy?.enabled === false || policy?.aiTranscripts === false) return null;
700
+ return policy?.aiTranscripts?.maxAgeDays ?? DEFAULT_RETENTION_POLICY.aiTranscripts.maxAgeDays ?? null;
701
+ }
622
702
  var PRESET_LABELS = {
623
703
  openrouter: "OpenRouter",
624
704
  lemonade: "Lemonade (local)",
@@ -739,41 +819,65 @@ var InferenceSettingsService = class InferenceSettingsService {
739
819
  /**
740
820
  * Fusions from the host's registry (code, read-only), the config's
741
821
  * `inference.fusions`, and app rows (editable JSON). A later source
742
- * shadows an earlier one by name.
822
+ * shadows an earlier one by name, the executor's precedence (#4800): app
823
+ * row, then config, then registry. Each entry carries the issues that
824
+ * would make the executor refuse it, so the list shows what would run.
743
825
  */
744
826
  async listInferenceFusions() {
745
827
  const layers = await this.loadLayers(null);
828
+ const config = this.readConfig();
829
+ const merged = mergeSettingsConfigs(config, layers.app);
746
830
  const out = /* @__PURE__ */ new Map();
831
+ const put = (setting, raw, path, against) => {
832
+ const previous = out.get(setting.name);
833
+ const shadows = previous ? [...previous.shadows ?? [], previous.source] : [];
834
+ const issues = [...findFusionDefinitionIssues(raw, path, against), ...findFusionDefinitionPromptIssues(raw, path)];
835
+ if (shadows.length > 0) setting.shadows = shadows;
836
+ if (issues.length > 0) setting.issues = issues;
837
+ out.set(setting.name, setting);
838
+ };
747
839
  const registry = this.options.registryFusions ?? defaultRegistryFusions;
748
840
  for (const fusion of registry()) {
749
841
  if (!fusion || typeof fusion.key !== "string") continue;
750
842
  const definition = stripKeyMembers(JSON.parse(JSON.stringify(fusion)));
751
- out.set(fusion.key, {
843
+ const { key: _key, ...raw } = fusion;
844
+ put({
752
845
  name: fusion.key,
753
846
  source: "registry",
754
847
  definition,
755
848
  description: typeof fusion.description === "string" ? fusion.description : void 0,
756
849
  editable: false
757
- });
850
+ }, raw, `fusions.${fusion.key}`, config);
758
851
  }
759
- const configFusions = this.readConfig()?.fusions;
760
- if (isRecord(configFusions)) for (const [name, definition] of Object.entries(configFusions)) {
852
+ if (isRecord(config?.fusions)) for (const [name, definition] of Object.entries(config.fusions)) {
761
853
  if (!isRecord(definition)) continue;
762
- out.set(name, {
854
+ put({
763
855
  name,
764
856
  source: "config",
765
857
  definition: stripKeyMembers(definition),
766
858
  description: typeof definition.description === "string" ? definition.description : void 0,
767
859
  editable: false
768
- });
860
+ }, definition, `inference.fusions.${name}`, config);
769
861
  }
770
- for (const [name, definition] of Object.entries(layers.app.fusions ?? {})) out.set(name, {
862
+ for (const [name, definition] of Object.entries(layers.app.fusions ?? {})) put({
771
863
  name,
772
864
  source: "app",
773
865
  definition,
774
866
  description: typeof definition.description === "string" ? definition.description : void 0,
775
867
  editable: true
776
- });
868
+ }, definition, `fusion:${name}`, merged);
869
+ for (const [binding, row] of layers.appRows) {
870
+ if (!binding.startsWith("fusion:") || !isUnreadableFusionRow(row)) continue;
871
+ const name = binding.slice(7);
872
+ put({
873
+ name,
874
+ source: "app",
875
+ definition: {},
876
+ editable: true
877
+ }, void 0, binding, merged);
878
+ const entry = out.get(name);
879
+ if (entry) entry.issues = [unreadableFusionIssue(name)];
880
+ }
777
881
  return [...out.values()].sort((a, b) => a.name.localeCompare(b.name));
778
882
  }
779
883
  /**
@@ -805,6 +909,10 @@ var InferenceSettingsService = class InferenceSettingsService {
805
909
  async setFusion(request) {
806
910
  const binding = `fusion:${request.name}`;
807
911
  if (!INFERENCE_BINDING_NAME_PATTERN.test(String(request.name))) throw new InferenceBindingValueError(binding, "invalid fusion name");
912
+ if (request.definition !== null) {
913
+ const normalized = normalizeFusionDefinition(binding, request.definition);
914
+ await this.assertConfigStaysValid("app", APP_INFERENCE_SCOPE_ID, { fusions: { [request.name]: normalized } });
915
+ }
808
916
  await this.writes.setValue(binding, "app", APP_INFERENCE_SCOPE_ID, request.definition, { beforeWrite: (normalized) => normalized === null ? this.assertNotReferenced("fusion", request.name) : Promise.resolve() });
809
917
  }
810
918
  /**
@@ -815,7 +923,7 @@ var InferenceSettingsService = class InferenceSettingsService {
815
923
  */
816
924
  async assertNotReferenced(kind, name) {
817
925
  const config = this.readConfig();
818
- if (kind === "alias" ? Object.hasOwn(config?.aliases ?? {}, name) : Object.hasOwn(config?.fusions ?? {}, name) || [...this.options.registryFusions?.() ?? []].some((fusion) => fusion?.key === name)) return;
926
+ if (kind === "alias" ? Object.hasOwn(config?.aliases ?? {}, name) : Object.hasOwn(config?.fusions ?? {}, name) || [...(this.options.registryFusions ?? defaultRegistryFusions)()].some((fusion) => fusion?.key === name)) return;
819
927
  const referrers = [];
820
928
  for (const [tier, target] of Object.entries(config?.tiers ?? {})) if (bindingRefersTo(`tier:${tier}`, target, kind, name)) referrers.push(`config tiers.${tier}`);
821
929
  for (const [alias, definition] of Object.entries(config?.aliases ?? {})) if (bindingRefersTo(`alias:${alias}`, definition, kind, name)) referrers.push(`config aliases.${alias}`);
@@ -920,9 +1028,54 @@ var InferenceSettingsService = class InferenceSettingsService {
920
1028
  });
921
1029
  }
922
1030
  /**
923
- * The Usage tab: `reportAiUsage()` (#4751) grouped by model, tier and/or
924
- * prompt key. Pass `by: ['model']` where tier and prompt tags do not exist
925
- * yet; NULL dimensions are their own bucket, never dropped.
1031
+ * The app's transcript setting (#4781): effective values, the stored app
1032
+ * row, its revert preview, the env override and the retention window.
1033
+ * The same for every tenant (no tenant override).
1034
+ */
1035
+ async getTranscriptSettings() {
1036
+ const appValue = storedTranscriptSettings(await this.overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID));
1037
+ const merged = this.options.transcriptsConfig ?? getInferenceTranscriptsConfig;
1038
+ const resolved = (layer) => {
1039
+ const value = merged(layer);
1040
+ return {
1041
+ enabled: value?.enabled === true,
1042
+ sample: value?.sample ?? DEFAULT_TRANSCRIPT_SAMPLE,
1043
+ maxBytes: value?.maxBytes ?? DEFAULT_TRANSCRIPT_MAX_BYTES
1044
+ };
1045
+ };
1046
+ const effective = resolved(appValue);
1047
+ const appRevert = resolved(null);
1048
+ const forcedOn = resolved({ enabled: true }).enabled;
1049
+ const envEnabled = forcedOn === resolved({ enabled: false }).enabled ? forcedOn : null;
1050
+ const config$1 = merged(null);
1051
+ const supplyingLevel = envEnabled !== null ? "env" : appValue?.enabled !== void 0 ? "app" : config$1?.enabled !== void 0 ? "config" : "default";
1052
+ return {
1053
+ binding: TRANSCRIPTS_BINDING,
1054
+ ...effective,
1055
+ supplyingLevel,
1056
+ appValue,
1057
+ appRevert,
1058
+ envEnabled,
1059
+ retentionDays: transcriptRetentionDays((this.options.retention ?? (() => config.toJSON().retention))()),
1060
+ promptOverrides: Object.keys(config$1?.prompts ?? {}).sort()
1061
+ };
1062
+ }
1063
+ /**
1064
+ * Store (or, with `null`, remove) the app's transcript setting. Always
1065
+ * the app row: there is no tenant scope. Goes through the authorizer
1066
+ * (request `{ binding: 'transcripts', scopeType: 'app' }`); on success the
1067
+ * value is installed as core's app settings layer in this process.
1068
+ */
1069
+ async setTranscriptSettings(request) {
1070
+ await this.writes.setValue(TRANSCRIPTS_BINDING, "app", APP_INFERENCE_SCOPE_ID, request.settings);
1071
+ applyStoredTranscriptSettings(await this.overrides.getScopeMap("app", APP_INFERENCE_SCOPE_ID));
1072
+ }
1073
+ /**
1074
+ * The Usage tab: `reportAiUsage()` (#4751) grouped by model, tier, prompt
1075
+ * key, fusion and/or template hash. Pass `by: ['model']` where tier and
1076
+ * prompt tags do not exist yet; NULL dimensions are their own bucket,
1077
+ * never dropped. `transcripts: true` adds per-bucket transcript counts
1078
+ * (#4781; ids only, never content).
926
1079
  */
927
1080
  usage(options = {}) {
928
1081
  return reportAiUsage(this.overrides.db, {
@@ -980,6 +1133,6 @@ function findKeyLike(candidate) {
980
1133
  return typeof candidate.apiKey === "string" && candidate.apiKey.length > 0;
981
1134
  }
982
1135
  //#endregion
983
- export { APP_INFERENCE_SCOPE_ID, CUSTOMIZE_INFERENCE_PERMISSION, DEFAULT_TIER_BINDING, INFERENCE_MODES, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, InferenceBindingInUseError, InferenceBindingValueError, InferenceKeyOriginError, InferenceOverride, InferenceOverrideAuthorizationError, InferenceOverrideCollection, InferenceOverrideService, InferenceSettingsConfigError, InferenceSettingsService, InferenceTenantTargetNotAllowedError, InvalidInferenceScopeError, PERSONALIZE_INFERENCE_PERMISSION, REDACTED_KEY, assertValidInferenceScope, bindInferenceKey, buildInferenceOverrideLayers, createInferenceOverrideResolver, createInferencePreferences, describeInferenceTarget, inferenceKeyFor, inferenceKeyOrigin, inferencePreferenceKind, inferenceProviderConfigFromKey, inferenceTargetRef, isAllowedByList, loadInferenceOverrideLayers, matchesAllowPattern, mergeSettingsConfigs, parseInferenceBinding, readInferenceOverrideRows, redactInferenceSecret, stripKeyMembers, tierBinding, validateInferencePreferences };
1136
+ export { APP_INFERENCE_SCOPE_ID, CUSTOMIZE_INFERENCE_PERMISSION, DEFAULT_TIER_BINDING, INFERENCE_MODES, INFERENCE_PERMISSION_DEFINITIONS, INFERENCE_PREFERENCE_KIND, INFERENCE_PREFERENCE_SURFACE, InferenceBindingInUseError, InferenceBindingValueError, InferenceKeyOriginError, InferenceOverride, InferenceOverrideAuthorizationError, InferenceOverrideCollection, InferenceOverrideService, InferenceSettingsConfigError, InferenceSettingsService, InferenceTenantTargetNotAllowedError, InvalidInferenceScopeError, PERSONALIZE_INFERENCE_PERMISSION, REDACTED_KEY, TRANSCRIPTS_BINDING, applyStoredTranscriptSettings, assertValidInferenceScope, bindInferenceKey, buildInferenceOverrideLayers, createInferenceOverrideResolver, createInferencePreferences, describeInferenceTarget, inferenceKeyFor, inferenceKeyOrigin, inferencePreferenceKind, inferenceProviderConfigFromKey, inferenceTargetRef, isAllowedByList, loadInferenceOverrideLayers, loadInferenceTranscriptSettings, matchesAllowPattern, mergeSettingsConfigs, normalizeTranscriptSettings, parseInferenceBinding, readInferenceOverrideRows, redactInferenceSecret, storedTranscriptSettings, stripKeyMembers, tierBinding, validateInferencePreferences };
984
1137
 
985
1138
  //# sourceMappingURL=server.js.map