{"version":3,"file":"index.cjs","names":["getRegisteredProviders","parseModelString","isProviderRegistered"],"sources":["../../../src/agent-builder/ee/types.ts","../../../src/agent-builder/ee/errors.ts","../../../src/agent-builder/ee/normalize-candidate.ts","../../../src/agent-builder/ee/allowlist.ts","../../../src/agent-builder/ee/policy.ts","../../../src/agent-builder/ee/picker.ts"],"sourcesContent":["import type { Provider, ModelForProvider } from '../../llm/model';\nimport type { SerializedMemoryConfig } from '../../memory/types';\nimport type { StorageBrowserRef, StorageWorkspaceRef } from '../../storage/types';\n\n/**\n * Allowlist entry for a known provider (one of the generated `Provider` strings).\n * `modelId` narrows to the union of model ids declared for that provider.\n * Omitting `modelId` is the **provider wildcard** — every model under the provider is allowed.\n */\nexport type KnownProviderEntry = {\n  [P in Provider]: { provider: P; modelId?: ModelForProvider<P> };\n}[Provider];\n\n/**\n * Allowlist entry for a custom / gateway provider that isn't in the generated registry.\n *\n * The `kind: 'custom'` discriminant is **required** — without it, an arbitrary string\n * provider would silently bypass the typo protection that `KnownProviderEntry` gives.\n * `modelId` is `string` (with the `& {}` escape hatch keeping autocomplete usable).\n */\nexport type CustomProviderEntry = {\n  kind: 'custom';\n  provider: string;\n  // string & {} preserves IDE autocomplete on call sites that still pass known model\n  // strings, while making the type accept arbitrary gateway model ids.\n  modelId?: string & {};\n};\n\n/**\n * Allowlist entry. Either a typed known-provider entry, or a tagged custom-provider entry.\n */\nexport type ProviderModelEntry = KnownProviderEntry | CustomProviderEntry;\n\n/**\n * Default model entry. Same shape as {@link ProviderModelEntry} but `modelId` is required —\n * a default needs to point at a specific model, not a whole provider.\n */\nexport type DefaultModelEntry =\n  | { [P in Provider]: { provider: P; modelId: ModelForProvider<P> } }[Provider]\n  | { kind: 'custom'; provider: string; modelId: string & {} };\n\n/**\n * Admin-controlled model policy for the Agent Builder.\n * Owned here; re-exported from `@mastra/core/agent-builder/ee` and the SDK.\n *\n * Invariants (enforced in Phase 4):\n * - `active: false` → all other fields ignored.\n * - `active: true` + `pickerVisible: false` (locked) → `default` MUST be set.\n * - When `allowed` is non-empty, `default` (if set) MUST satisfy `isModelAllowed(allowed, default)`.\n */\nexport interface BuilderModelPolicy {\n  active: boolean;\n  pickerVisible?: boolean;\n  allowed?: ProviderModelEntry[];\n  default?: DefaultModelEntry;\n}\n\n/**\n * Default values for agents created via the builder.\n * Used as fallbacks when the user doesn't specify a value.\n */\nexport interface BuilderAgentDefaults extends Record<string, unknown> {\n  /** Default memory configuration for new agents */\n  memory?: SerializedMemoryConfig;\n  /** Default workspace reference for new agents */\n  workspace?: StorageWorkspaceRef;\n  /** Default browser configuration for new agents */\n  browser?: StorageBrowserRef;\n  /**\n   * Admin-controlled model allowlist + default applied to new agents.\n   * `allowed` empty/undefined ⇒ no restriction. `default` (if set) is preselected on create.\n   * See parent RFC for full semantics (wildcards, custom gateways, deny-by-default).\n   */\n  models?: {\n    allowed?: ProviderModelEntry[];\n    default?: DefaultModelEntry;\n  };\n  /**\n   * Admin-controlled allowlist of tool IDs visible in the builder tools picker.\n   *\n   * Semantics:\n   * - omitted (`undefined`) ⇒ unrestricted; show all registered tools.\n   * - `allowed: []` ⇒ empty picker (explicit lockdown).\n   * - `allowed: [...ids]` ⇒ show only the listed tool IDs.\n   *\n   * IDs are `tool.id` (preferred — what you see in the UI, URLs and traces)\n   * but the registration key (the property name under `Mastra({ tools: {…} })`)\n   * is also accepted as an alias. Matched against the registered tools at\n   * request time. Unknown IDs are dropped and surfaced as warnings.\n   */\n  tools?: {\n    allowed?: string[];\n  };\n  /**\n   * Admin-controlled allowlist of agent IDs visible in the builder sub-agents picker.\n   *\n   * Semantics:\n   * - omitted (`undefined`) ⇒ unrestricted; show all registered agents.\n   * - `allowed: []` ⇒ empty picker (explicit lockdown).\n   * - `allowed: [...ids]` ⇒ show only the listed agent IDs.\n   *\n   * IDs are `Agent.id` (preferred — what you see in the UI, URLs and traces)\n   * but the registration key (the property name under `Mastra({ agents: {…} })`)\n   * is also accepted as an alias. Matched against the registered agents at\n   * request time. Unknown IDs are dropped and surfaced as warnings.\n   */\n  agents?: {\n    allowed?: string[];\n  };\n  /**\n   * Admin-controlled allowlist of workflow IDs visible in the builder workflows picker.\n   *\n   * Semantics:\n   * - omitted (`undefined`) ⇒ unrestricted; show all registered workflows.\n   * - `allowed: []` ⇒ empty picker (explicit lockdown).\n   * - `allowed: [...ids]` ⇒ show only the listed workflow IDs.\n   *\n   * IDs are `workflow.id` (preferred — what you see in the UI, URLs and traces)\n   * but the registration key (the property name under `Mastra({ workflows: {…} })`)\n   * is also accepted as an alias. Matched against the registered workflows at\n   * request time. Unknown IDs are dropped and surfaced as warnings.\n   */\n  workflows?: {\n    allowed?: string[];\n  };\n}\n\n/**\n * Feature toggles for the agent editor surface.\n * Each key controls visibility of that section in the builder UI.\n *\n * **Semantic: omitted = true (allowlist model — features default ON)**\n * - omitted or `true` — feature is visible to users\n * - `false` — feature is hidden\n *\n * Admins opt OUT of features by setting them to `false`. The raw\n * `AgentFeatures` shape carries `undefined`/`true`/`false`; the resolved\n * shape produced by {@link resolveAgentFeatures} is fully populated and is\n * what consumers (server handlers, UI hooks) actually read via\n * `IAgentBuilder.getFeatures()`.\n *\n * Consumer code should use strict equality on the resolved shape:\n * ```ts\n * const showTools = builder.getFeatures()?.agent?.tools === true;\n * ```\n *\n * Special case — `browser`: defaults to `true` ONLY when a valid\n * `configuration.agent.browser` (with `config.provider`) is provided.\n * Without a provider, the toggle has no backend, so the resolved value is\n * `false` regardless of the omitted-default. An explicit `browser: true`\n * with missing/invalid config is downgraded to `false` and surfaced as a\n * warning by `EditorAgentBuilder` (admin error feedback).\n */\nexport interface AgentFeatures {\n  tools?: boolean;\n  agents?: boolean;\n  workflows?: boolean;\n  scorers?: boolean;\n  skills?: boolean;\n  memory?: boolean;\n  variables?: boolean;\n  /** Favorite agents and skills with per-user state and aggregate counts. */\n  favorites?: boolean;\n  avatarUpload?: boolean;\n  /**\n   * Allow end-users to enable browser access for their agents.\n   * Defaults to `true` only when a valid browser provider is configured;\n   * otherwise resolves to `false`. See doc above for the full rule.\n   */\n  browser?: boolean;\n  /**\n   * Whether the model picker is visible to end-users in the Agent Builder.\n   * Omitted ⇒ picker visible (default-on). `false` ⇒ picker hidden (locked\n   * mode); admin's `models.default` is applied.\n   * When visible, choices are filtered by `models.allowed` if set.\n   */\n  model?: boolean;\n}\n\n/**\n * Default-on values for {@link AgentFeatures}. `browser` is defaulted\n * dynamically by {@link resolveAgentFeatures} based on configuration; it is\n * intentionally absent here so the shape mirrors what `resolveAgentFeatures`\n * unconditionally fills in.\n */\nexport const BUILDER_FEATURE_DEFAULTS: Required<Omit<AgentFeatures, 'browser'>> = {\n  tools: true,\n  agents: true,\n  workflows: true,\n  scorers: true,\n  skills: true,\n  memory: true,\n  variables: true,\n  favorites: true,\n  avatarUpload: true,\n  model: true,\n};\n\n/**\n * Context required to resolve {@link AgentFeatures} defaults. Lives separately\n * from the raw config so this helper stays pure and synchronous.\n */\nexport interface ResolveAgentFeaturesContext {\n  /**\n   * Whether `configuration.agent.browser` declares a valid provider.\n   * Drives the default-on/off decision for `features.agent.browser`.\n   */\n  hasBrowserConfig: boolean;\n}\n\n/**\n * Pure normalization of the raw {@link AgentFeatures} into a fully-populated\n * shape with default-on semantics applied.\n *\n * Rules:\n * - Explicit `false` always wins (admin opt-out).\n * - Explicit `true` wins for non-`browser` keys.\n * - Omitted keys resolve to `true` (except `browser`, see below).\n * - `browser`:\n *   - explicit `false` ⇒ `false`.\n *   - explicit `true` + `hasBrowserConfig: false` ⇒ `false` (caller is\n *     responsible for emitting a warning; this helper does not throw).\n *   - explicit `true` + `hasBrowserConfig: true` ⇒ `true`.\n *   - omitted ⇒ `hasBrowserConfig` (default-on only when prerequisite met).\n */\nexport function resolveAgentFeatures(\n  raw: AgentFeatures | undefined,\n  ctx: ResolveAgentFeaturesContext,\n): Required<AgentFeatures> {\n  const pick = <K extends keyof typeof BUILDER_FEATURE_DEFAULTS>(key: K): boolean => {\n    const explicit = raw?.[key];\n    return explicit === undefined ? BUILDER_FEATURE_DEFAULTS[key] : explicit;\n  };\n\n  const resolveBrowser = (): boolean => {\n    const explicit = raw?.browser;\n    if (explicit === false) return false;\n    if (explicit === true) return ctx.hasBrowserConfig;\n    return ctx.hasBrowserConfig;\n  };\n\n  return {\n    tools: pick('tools'),\n    agents: pick('agents'),\n    workflows: pick('workflows'),\n    scorers: pick('scorers'),\n    skills: pick('skills'),\n    memory: pick('memory'),\n    variables: pick('variables'),\n    favorites: pick('favorites'),\n    avatarUpload: pick('avatarUpload'),\n    model: pick('model'),\n    browser: resolveBrowser(),\n  };\n}\n\n/**\n * Configuration for the Agent Builder EE feature.\n * Passed to `MastraEditorConfig.builder`.\n *\n * All fields are optional. JSON-safe (no functions, no class instances).\n */\nexport interface AgentBuilderOptions {\n  /**\n   * Whether the builder is enabled. Default: true.\n   * Set to false to disable the builder without removing the config.\n   */\n  enabled?: boolean;\n\n  /**\n   * Deployment-level feature toggles.\n   * Key presence means \"this surface exists for this deployment.\"\n   */\n  features?: {\n    agent?: AgentFeatures;\n  };\n\n  /**\n   * Admin-pinned values applied to every artifact the builder produces.\n   * Not overridable by end-users.\n   *\n   * Known fields are typed explicitly; additional fields allowed for extensibility.\n   */\n  configuration?: {\n    agent?: BuilderAgentDefaults;\n  };\n\n  /**\n   * Skill registries the Agent Builder is allowed to browse and install from.\n   *\n   * Each registry is opt-in. When no registries are enabled, the Builder hides\n   * registry browse UI entirely. When at least one is enabled, the Builder\n   * shows a \"Browse registries\" entry alongside \"Create skill\".\n   */\n  registries?: {\n    /**\n     * The public skills.sh registry (https://skills.sh).\n     * Off by default — admins must explicitly opt in.\n     */\n    skillsSh?: {\n      /** When true, the Builder may browse and install from skills.sh. */\n      enabled: boolean;\n    };\n  };\n}\n\n/**\n * Public interface for the Agent Builder.\n * Implemented by EditorAgentBuilder in @mastra/editor/ee.\n */\nexport interface IAgentBuilder {\n  readonly enabled: boolean;\n  getFeatures(): AgentBuilderOptions['features'];\n  getConfiguration(): AgentBuilderOptions['configuration'];\n  /**\n   * The opt-in skill registries this Builder is allowed to browse and install\n   * from. Returns `undefined` when the admin has not configured any registries.\n   */\n  getRegistries?(): AgentBuilderOptions['registries'];\n  /**\n   * Optional warnings produced during construction-time validation\n   * (e.g. allowlist entries with unknown providers that lack `kind: 'custom'`).\n   * Surfaced via `GET /editor/builder/settings.modelPolicyWarnings` for admin UI display.\n   */\n  getModelPolicyWarnings?(): string[];\n}\n","import type { ModelCandidate } from './normalize-candidate';\nimport type { ProviderModelEntry } from './types';\n\nexport const MODEL_NOT_ALLOWED_CODE = 'MODEL_NOT_ALLOWED' as const;\n\n/**\n * Thrown by `enforceModelAllowlist` call sites when a write attempts to persist\n * a model that the active builder allowlist does not permit.\n *\n * Lives in `@mastra/core` so editor and server layers can both throw it\n * without crossing package boundaries. The server adapter\n * (`packages/server/src/server/handlers/error.ts`) maps this to HTTP 422 with\n * a structured JSON body of the same shape.\n */\nexport class ModelNotAllowedError extends Error {\n  readonly code = MODEL_NOT_ALLOWED_CODE;\n  readonly allowed: ProviderModelEntry[] | undefined;\n  readonly attempted: ModelCandidate;\n  readonly offendingLabel: string;\n\n  constructor(args: {\n    allowed: ProviderModelEntry[] | undefined;\n    attempted: ModelCandidate;\n    offendingLabel: string;\n    message?: string;\n  }) {\n    const message =\n      args.message ??\n      `Model \"${args.attempted.provider}/${args.attempted.modelId}\" (${args.offendingLabel}) is not in the configured allowlist.`;\n    super(message);\n    this.name = 'ModelNotAllowedError';\n    this.allowed = args.allowed;\n    this.attempted = args.attempted;\n    this.offendingLabel = args.offendingLabel;\n  }\n}\n\nexport function isModelNotAllowedError(error: unknown): error is ModelNotAllowedError {\n  return error instanceof Error && (error as { code?: unknown }).code === MODEL_NOT_ALLOWED_CODE;\n}\n","import { getRegisteredProviders, parseModelString } from '../../llm/model/provider-registry.js';\nimport type { MastraModelConfig } from '../../llm/model/shared.types.js';\nimport type { StorageConditionalField, StorageConditionalVariant, StorageModelConfig } from '../../storage/types.js';\n\nexport type ModelCandidateOrigin =\n  | 'static'\n  | 'conditional-variant'\n  | 'conditional-default'\n  | 'runtime'\n  | 'list'\n  | 'sdk-instance'\n  | 'openai-compatible';\n\n/**\n * A single normalized provider/model candidate extracted from one of the many\n * shapes a model can be expressed in across the codebase.\n *\n * `origin` records which dispatch branch produced the candidate, mainly for\n * error messages on conditional variants.\n *\n * `label` is a short human-friendly description (variant index / SDK provider id\n * / etc.) used by `enforceModelAllowlist` when reporting the offending entry.\n */\nexport interface ModelCandidate {\n  provider: string;\n  modelId: string;\n  origin: ModelCandidateOrigin;\n  label?: string;\n}\n\n/**\n * Anything we accept as input to {@link toModelCandidates}. Kept open so call\n * sites can pass arbitrary stored or runtime model values without manual coercion.\n */\nexport type ModelCandidateInput =\n  | string\n  | MastraModelConfig\n  | StorageModelConfig\n  | StorageConditionalField<StorageModelConfig>\n  | StorageConditionalVariant<string>[]\n  | { provider?: unknown; modelId?: unknown; name?: unknown; id?: unknown; providerId?: unknown }\n  | ((...args: unknown[]) => unknown)\n  | null\n  | undefined;\n\n/**\n * Gateway-aware split of a runtime model string. `parseModelString` only splits\n * on the first slash, which fails for gateway provider IDs that themselves\n * contain a slash (e.g. `acme/custom/foo-1`). We try the longest registered\n * provider prefix first and fall back to the first-slash split when no match\n * is found in the registry.\n */\nfunction splitRuntimeModelString(input: string): { provider: string; modelId: string } | undefined {\n  const providers = getRegisteredProviders().sort((a, b) => b.length - a.length);\n  for (const providerId of providers) {\n    const prefix = `${providerId}/`;\n    if (input.startsWith(prefix)) {\n      const modelId = input.slice(prefix.length);\n      if (modelId.length > 0) return { provider: providerId, modelId };\n    }\n  }\n  const parsed = parseModelString(input);\n  if (parsed.provider && parsed.modelId) {\n    return { provider: parsed.provider, modelId: parsed.modelId };\n  }\n  return undefined;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction fromObject(value: Record<string, unknown>, origin: ModelCandidateOrigin, label?: string): ModelCandidate[] {\n  // SDK instance: AI SDK language models expose `provider` + `modelId`.\n  const providerField = value.provider;\n  const modelIdField = value.modelId;\n  const nameField = value.name;\n  const idField = value.id;\n  const providerIdField = value.providerId;\n\n  // OpenAICompatibleConfig `{ id: 'provider/model' }` — must be checked before\n  // `{ provider, modelId }` so AI SDK instances (which also have `provider`)\n  // don't get pre-empted by a stale `id` lookup.\n  if (typeof idField === 'string' && idField.includes('/') && providerField === undefined) {\n    const split = splitRuntimeModelString(idField);\n    if (split) {\n      return [{ ...split, origin: 'openai-compatible', label: label ?? idField }];\n    }\n  }\n\n  // OpenAICompatibleConfig `{ providerId, modelId }`\n  if (typeof providerIdField === 'string' && typeof modelIdField === 'string') {\n    return [\n      {\n        provider: providerIdField,\n        modelId: modelIdField,\n        origin: 'openai-compatible',\n        label: label ?? `${providerIdField}/${modelIdField}`,\n      },\n    ];\n  }\n\n  // AI SDK language model instance: `{ provider, modelId, ... doGenerate }`\n  if (typeof providerField === 'string' && typeof modelIdField === 'string') {\n    const isSdkInstance = typeof (value as { doGenerate?: unknown }).doGenerate === 'function';\n    return [\n      {\n        provider: providerField,\n        modelId: modelIdField,\n        origin: isSdkInstance ? 'sdk-instance' : origin,\n        label: label ?? `${providerField}/${modelIdField}`,\n      },\n    ];\n  }\n\n  // Stored static `{ provider, name }`\n  if (typeof providerField === 'string' && typeof nameField === 'string') {\n    return [\n      {\n        provider: providerField,\n        modelId: nameField,\n        origin,\n        label: label ?? `${providerField}/${nameField}`,\n      },\n    ];\n  }\n\n  return [];\n}\n\n/**\n * Convert any supported model expression into a flat list of `{ provider, modelId }`\n * candidates. Empty array means \"could not statically determine\" — callers\n * should treat that as unenforced at this level (runtime defense in Phase 7\n * picks it up).\n *\n * Dispatch order:\n * 1. `null` / `undefined` / `function` → `[]` (dynamic, defer to runtime)\n * 2. `string` → gateway-aware split\n * 3. Conditional variants array → walk each variant\n * 4. Object → openai-compatible / SDK instance / stored static, see {@link fromObject}\n */\nexport function toModelCandidates(input: ModelCandidateInput): ModelCandidate[] {\n  if (input === null || input === undefined) return [];\n\n  if (typeof input === 'function') return [];\n\n  if (typeof input === 'string') {\n    const split = splitRuntimeModelString(input);\n    if (!split) return [];\n    return [{ ...split, origin: 'runtime', label: input }];\n  }\n\n  if (Array.isArray(input)) {\n    const candidates: ModelCandidate[] = [];\n    input.forEach((variant, index) => {\n      if (!isPlainObject(variant)) return;\n      const value = (variant as { value?: unknown }).value ?? variant;\n      const hasRules = isPlainObject(variant) && 'rules' in variant && (variant as { rules?: unknown }).rules != null;\n      const origin: ModelCandidateOrigin = hasRules ? 'conditional-variant' : 'conditional-default';\n      const label = hasRules ? `variant[${index}]` : `variant[${index}] (default)`;\n      if (typeof value === 'string') {\n        const split = splitRuntimeModelString(value);\n        if (split) {\n          candidates.push({ ...split, origin, label });\n        }\n        return;\n      }\n      if (isPlainObject(value)) {\n        candidates.push(...fromObject(value, origin, label));\n      }\n    });\n    return candidates;\n  }\n\n  if (isPlainObject(input)) {\n    return fromObject(input, 'static');\n  }\n\n  return [];\n}\n","import { isProviderRegistered } from '../../llm/model/provider-registry.js';\nimport { ModelNotAllowedError } from './errors.js';\nimport { toModelCandidates } from './normalize-candidate.js';\nimport type { ModelCandidate, ModelCandidateInput } from './normalize-candidate.js';\nimport type { ProviderModelEntry } from './types.js';\n\n/**\n * Candidate model to check against the allowlist.\n * Caller is responsible for normalizing the source shape via {@link toModelCandidates}\n * (in `./normalize-candidate.ts`) before reaching this matcher.\n */\nexport interface ModelMatchCandidate {\n  provider: string;\n  modelId: string;\n}\n\n/**\n * Single-entry match: provider equality (case-sensitive). When the entry omits\n * `modelId` it matches every model under that provider (provider wildcard).\n *\n * Custom (`kind: 'custom'`) entries match by exact provider string. Known-provider\n * entries match by exact provider string too — the typed surface is purely a\n * compile-time guard.\n */\nexport function matchesProvider(entry: ProviderModelEntry, candidate: ModelMatchCandidate): boolean {\n  if (entry.provider !== candidate.provider) return false;\n  if (!entry.modelId) return true; // wildcard\n  return entry.modelId === candidate.modelId;\n}\n\n/**\n * Returns `true` if the candidate is allowed under the given allowlist.\n *\n * Rules:\n * - `undefined` allowlist ⇒ unrestricted (always `true`).\n * - `[]` empty allowlist ⇒ unrestricted (always `true`).\n * - Non-empty allowlist where **every** entry's provider is unknown to the\n *   runtime registry AND not tagged `kind: 'custom'` ⇒ deny everything. This\n *   prevents typos (e.g. `openaii`) from acting as an unintended deny-all that\n *   silently allows anything else; it is the documented \"deny vs ignore\" rule.\n */\nexport function isModelAllowed(allowed: ProviderModelEntry[] | undefined, candidate: ModelMatchCandidate): boolean {\n  if (allowed === undefined) return true;\n  if (allowed.length === 0) return true;\n\n  const activeEntries = allowed.filter(entry => {\n    if ('kind' in entry && entry.kind === 'custom') return true;\n    return isProviderRegistered(entry.provider);\n  });\n\n  if (activeEntries.length === 0) return false;\n\n  return activeEntries.some(entry => matchesProvider(entry, candidate));\n}\n\n/**\n * Result of an allowlist enforcement check.\n *\n * `attempted` is the candidate (or list of candidates) that triggered the\n * decision; `offendingLabel` (when set) names the specific failing entry so\n * callers can surface it in error messages — particularly useful for\n * conditional model variants.\n */\nexport type EnforceModelAllowlistResult =\n  | { ok: true }\n  | {\n      ok: false;\n      attempted: ModelCandidate;\n      offendingLabel: string;\n    };\n\n/**\n * Apply an allowlist to any supported model expression. Normalizes via\n * `toModelCandidates`, then runs `isModelAllowed` per candidate. Returns the\n * **first** failing candidate so error messages can pinpoint which variant of\n * a conditional / fallback list violated the policy.\n *\n * If `toModelCandidates` returns no candidates (dynamic function, unparsable\n * shape) this passes — runtime defense (Phase 7) handles those cases.\n */\nexport function enforceModelAllowlist(\n  allowed: ProviderModelEntry[] | undefined,\n  input: ModelCandidateInput,\n): EnforceModelAllowlistResult {\n  const candidates = toModelCandidates(input);\n  for (const candidate of candidates) {\n    if (!isModelAllowed(allowed, candidate)) {\n      return {\n        ok: false,\n        attempted: candidate,\n        offendingLabel: candidate.label ?? candidate.origin,\n      };\n    }\n  }\n  return { ok: true };\n}\n\n/**\n * Convenience wrapper around `enforceModelAllowlist` that throws\n * `ModelNotAllowedError` on rejection. Use at write call sites so the server\n * adapter can translate into HTTP 422 + structured body.\n */\nexport function assertModelAllowed(allowed: ProviderModelEntry[] | undefined, input: ModelCandidateInput): void {\n  const result = enforceModelAllowlist(allowed, input);\n  if (result.ok) return;\n  throw new ModelNotAllowedError({\n    allowed,\n    attempted: result.attempted,\n    offendingLabel: result.offendingLabel,\n  });\n}\n","import type { IAgentBuilder, BuilderModelPolicy, DefaultModelEntry, ProviderModelEntry } from './types';\n\n/**\n * Inputs for the shared {@link isBuilderModelPolicyActive} predicate.\n *\n * Lives separately from {@link BuilderModelPolicy} because we need to ask the\n * \"is the model slice active?\" question at config-validation time, *before*\n * a `BuilderModelPolicy` has been built.\n */\nexport interface BuilderModelPolicyInputs {\n  /** `AgentBuilderOptions.enabled` (defaulted: missing = `true`). */\n  enabled: boolean;\n  /** `features.agent.model` — `true` means picker visible. */\n  pickerVisible: boolean;\n  /** `configuration.agent.models.allowed`. */\n  allowed?: ProviderModelEntry[];\n  /** `configuration.agent.models.default`. */\n  default?: DefaultModelEntry;\n}\n\n/**\n * Single source of truth for whether the admin has actually configured a model\n * policy. Reused by:\n * - {@link builderToModelPolicy} (UI / runtime derivation)\n * - `EditorAgentBuilder` config validation (Phase 4)\n * - Server-side enforcement gate (Phase 6)\n *\n * \"Active\" means the admin opted into the model slice in some way:\n * - the picker is visible (open-mode), OR\n * - an allowlist was set, OR\n * - a default model was set.\n *\n * If the builder is `enabled: false`, the slice is never active.\n */\nexport function isBuilderModelPolicyActive(inputs: BuilderModelPolicyInputs): boolean {\n  if (!inputs.enabled) return false;\n  if (inputs.pickerVisible) return true;\n  if (inputs.allowed !== undefined) return true;\n  if (inputs.default !== undefined) return true;\n  return false;\n}\n\n/**\n * Pure derivation of the {@link BuilderModelPolicy} from an `IAgentBuilder`.\n * No `Mastra` / `IEditor` dependency — server and editor wrappers feed it\n * a builder instance through their own resolution paths.\n *\n * Returns `{ active: false }` when:\n * - the builder is missing,\n * - the builder is disabled, or\n * - none of the model-slice signals are present.\n *\n * In every active case, `allowed` and `default` are passed through verbatim\n * so locked-mode UI still has the data it needs to render the chosen model.\n */\nexport function builderToModelPolicy(builder: IAgentBuilder | undefined): BuilderModelPolicy {\n  if (!builder || !builder.enabled) {\n    return { active: false };\n  }\n\n  const features = builder.getFeatures();\n  const configuration = builder.getConfiguration();\n  const pickerVisible = features?.agent?.model === true;\n  const models = configuration?.agent?.models;\n  const allowed = models?.allowed;\n  const defaultModel = models?.default;\n\n  const active = isBuilderModelPolicyActive({\n    enabled: builder.enabled,\n    pickerVisible,\n    allowed,\n    default: defaultModel,\n  });\n\n  if (!active) {\n    return { active: false };\n  }\n\n  return {\n    active: true,\n    pickerVisible,\n    ...(allowed !== undefined ? { allowed } : {}),\n    ...(defaultModel !== undefined ? { default: defaultModel } : {}),\n  };\n}\n","import type { BuilderAgentDefaults } from './types';\n\n/**\n * Resolved picker visibility for the Agent Builder configure panel.\n *\n * One field per kind (tools / agents / workflows).\n * - `null` ⇒ unrestricted (show all registered entries).\n * - `string[]` ⇒ explicit allowlist (may be empty to show none).\n */\nexport interface ResolvedPickerVisibility {\n  visibleTools: string[] | null;\n  visibleAgents: string[] | null;\n  visibleWorkflows: string[] | null;\n  /** Non-fatal warnings (e.g. unknown IDs in any allowlist). */\n  warnings: string[];\n}\n\nexport interface ResolvePickerVisibilityInputs {\n  /** The `agent` slice of `AgentBuilderOptions['configuration']`. */\n  config: BuilderAgentDefaults | undefined;\n  /** All tool IDs currently registered with the Mastra instance. */\n  registeredToolIds: readonly string[];\n  /** All agent IDs currently registered with the Mastra instance. */\n  registeredAgentIds: readonly string[];\n  /** All workflow IDs currently registered with the Mastra instance. */\n  registeredWorkflowIds: readonly string[];\n}\n\ninterface ResolveOneResult {\n  visible: string[] | null;\n  warnings: string[];\n}\n\nfunction resolveOne(\n  allowlist: string[] | undefined,\n  registered: readonly string[],\n  kindLabel: string,\n  configPath: string,\n): ResolveOneResult {\n  if (allowlist === undefined) {\n    return { visible: null, warnings: [] };\n  }\n\n  const known = new Set(registered);\n  const seen = new Set<string>();\n  const visible: string[] = [];\n  const warnings: string[] = [];\n\n  for (const id of allowlist) {\n    if (seen.has(id)) continue;\n    seen.add(id);\n    if (known.has(id)) {\n      visible.push(id);\n    } else {\n      warnings.push(\n        `${configPath} references unknown ${kindLabel} \"${id}\" — no ${kindLabel} with this ID is registered. It will be hidden from the builder picker.`,\n      );\n    }\n  }\n\n  return { visible, warnings };\n}\n\n/**\n * Pure derivation of {@link ResolvedPickerVisibility} from admin config and\n * the registered tool/agent/workflow sets.\n *\n * Per kind:\n * - allowlist undefined ⇒ `null` (unrestricted), no warnings.\n * - allowlist provided ⇒ filter to known IDs; emit one warning per unknown ID.\n *\n * Stable order: each visible list preserves admin-provided order with unknowns\n * dropped. Duplicates are de-duplicated.\n */\nexport function resolvePickerVisibility({\n  config,\n  registeredToolIds,\n  registeredAgentIds,\n  registeredWorkflowIds,\n}: ResolvePickerVisibilityInputs): ResolvedPickerVisibility {\n  const tools = resolveOne(config?.tools?.allowed, registeredToolIds, 'tool', 'configuration.agent.tools.allowed');\n  const agents = resolveOne(config?.agents?.allowed, registeredAgentIds, 'agent', 'configuration.agent.agents.allowed');\n  const workflows = resolveOne(\n    config?.workflows?.allowed,\n    registeredWorkflowIds,\n    'workflow',\n    'configuration.agent.workflows.allowed',\n  );\n\n  return {\n    visibleTools: tools.visible,\n    visibleAgents: agents.visible,\n    visibleWorkflows: workflows.visible,\n    warnings: [...tools.warnings, ...agents.warnings, ...workflows.warnings],\n  };\n}\n"],"mappings":"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"}