@open-mercato/ai-assistant 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +12 -29
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.js +80 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.js +124 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.js +30 -0
- package/dist/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/chat/route.js +7 -0
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/moderation-flags/route.js +121 -0
- package/dist/modules/ai_assistant/api/moderation-flags/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js +10 -10
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/settings/route.js +34 -13
- package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +121 -15
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js +139 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.js +26 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +4 -4
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/data/entities.js +42 -0
- package/dist/modules/ai_assistant/data/entities.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +3 -1
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiModerationFlagRepository.js +44 -0
- package/dist/modules/ai_assistant/data/repositories/AiModerationFlagRepository.js.map +7 -0
- package/dist/modules/ai_assistant/di.js +6 -2
- package/dist/modules/ai_assistant/di.js.map +2 -2
- package/dist/modules/ai_assistant/events.js +6 -0
- package/dist/modules/ai_assistant/events.js.map +2 -2
- package/dist/modules/ai_assistant/i18n/de.json +42 -0
- package/dist/modules/ai_assistant/i18n/en.json +42 -0
- package/dist/modules/ai_assistant/i18n/es.json +42 -0
- package/dist/modules/ai_assistant/i18n/ko.json +527 -0
- package/dist/modules/ai_assistant/i18n/pl.json +42 -0
- package/dist/modules/ai_assistant/lib/agent-runtime.js +158 -5
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +2 -2
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
- package/dist/modules/ai_assistant/lib/auth.js +6 -7
- package/dist/modules/ai_assistant/lib/auth.js.map +2 -2
- package/dist/modules/ai_assistant/lib/entity-graph.js +3 -3
- package/dist/modules/ai_assistant/lib/entity-graph.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js +3 -0
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/openai.js +8 -1
- package/dist/modules/ai_assistant/lib/llm-adapters/openai.js.map +2 -2
- package/dist/modules/ai_assistant/lib/model-factory.js +6 -1
- package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
- package/dist/modules/ai_assistant/lib/moderation-flag-recorder.js +43 -0
- package/dist/modules/ai_assistant/lib/moderation-flag-recorder.js.map +7 -0
- package/dist/modules/ai_assistant/lib/moderation-policy.js +24 -0
- package/dist/modules/ai_assistant/lib/moderation-policy.js.map +7 -0
- package/dist/modules/ai_assistant/lib/moderation.js +109 -0
- package/dist/modules/ai_assistant/lib/moderation.js.map +7 -0
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +2 -0
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-search.js +4 -12
- package/dist/modules/ai_assistant/lib/tool-search.js.map +2 -2
- package/dist/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.js +19 -0
- package/dist/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.js.map +7 -0
- package/generated/entities/ai_agent_runtime_override/index.ts +1 -0
- package/generated/entities/ai_moderation_flag/index.ts +9 -0
- package/generated/entities.ids.generated.ts +2 -1
- package/generated/entity-fields-registry.ts +13 -1
- package/package.json +13 -12
- package/src/modules/ai_assistant/__integration__/TC-AI-MODERATION-008-settings-and-audit.spec.ts +126 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-MODERATION-009-ui.spec.ts +161 -0
- package/src/modules/ai_assistant/__integration__/helpers/aiAssistantFixtures.ts +48 -0
- package/src/modules/ai_assistant/api/ai/chat/route.ts +10 -0
- package/src/modules/ai_assistant/api/moderation-flags/__tests__/route.test.ts +122 -0
- package/src/modules/ai_assistant/api/moderation-flags/route.ts +137 -0
- package/src/modules/ai_assistant/api/settings/__tests__/route.test.ts +58 -8
- package/src/modules/ai_assistant/api/settings/allowlist/route.ts +10 -12
- package/src/modules/ai_assistant/api/settings/route.ts +33 -10
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +161 -15
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +2 -2
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/AiModerationFlagsPageClient.tsx +186 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.meta.ts +24 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/moderation-flags/page.tsx +12 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +4 -4
- package/src/modules/ai_assistant/data/entities.ts +56 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +4 -0
- package/src/modules/ai_assistant/data/repositories/AiModerationFlagRepository.ts +79 -0
- package/src/modules/ai_assistant/di.ts +5 -1
- package/src/modules/ai_assistant/events.ts +21 -0
- package/src/modules/ai_assistant/i18n/de.json +42 -0
- package/src/modules/ai_assistant/i18n/en.json +42 -0
- package/src/modules/ai_assistant/i18n/es.json +42 -0
- package/src/modules/ai_assistant/i18n/ko.json +527 -0
- package/src/modules/ai_assistant/i18n/pl.json +42 -0
- package/src/modules/ai_assistant/lib/__tests__/auth.test.ts +2 -5
- package/src/modules/ai_assistant/lib/__tests__/input-moderation-gate.test.ts +121 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-anthropic.test.ts +13 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-openai.test.ts +41 -0
- package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +60 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation-flag-recorder.test.ts +76 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation-policy.test.ts +86 -0
- package/src/modules/ai_assistant/lib/__tests__/moderation.test.ts +114 -0
- package/src/modules/ai_assistant/lib/agent-runtime.ts +259 -0
- package/src/modules/ai_assistant/lib/ai-agent-definition.ts +10 -0
- package/src/modules/ai_assistant/lib/auth.ts +6 -15
- package/src/modules/ai_assistant/lib/entity-graph.ts +3 -3
- package/src/modules/ai_assistant/lib/llm-adapters/anthropic.ts +10 -0
- package/src/modules/ai_assistant/lib/llm-adapters/openai.ts +32 -1
- package/src/modules/ai_assistant/lib/model-factory.ts +33 -0
- package/src/modules/ai_assistant/lib/moderation-flag-recorder.ts +75 -0
- package/src/modules/ai_assistant/lib/moderation-policy.ts +77 -0
- package/src/modules/ai_assistant/lib/moderation.ts +179 -0
- package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +2 -0
- package/src/modules/ai_assistant/lib/tool-search.ts +4 -17
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +388 -16
- package/src/modules/ai_assistant/migrations/Migration20260610134045_ai_assistant.ts +21 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/ai_assistant/api/settings/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { existsSync, readFileSync } from 'node:fs'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { findApiKeyBySecret } from '@open-mercato/core/modules/api_keys/services/apiKeyService'\nimport { llmProviderRegistry } from '@open-mercato/shared/lib/ai/llm-provider-registry'\nimport { joinProviderModel } from '@open-mercato/shared/lib/ai/model-id'\nimport {\n OPEN_CODE_PROVIDER_IDS,\n OPEN_CODE_PROVIDERS,\n getOpenCodeProviderConfiguredEnvKey,\n isOpenCodeProviderConfigured,\n} from '@open-mercato/shared/lib/ai/opencode-provider'\nimport { AiAgentRuntimeOverrideRepository, AiAgentRuntimeOverrideValidationError } from '../../data/repositories/AiAgentRuntimeOverrideRepository'\nimport { AiTenantModelAllowlistRepository } from '../../data/repositories/AiTenantModelAllowlistRepository'\nimport { isBaseurlAllowlisted, readBaseurlAllowlist } from '../../lib/baseurl-allowlist'\nimport { loadAgentRegistry, listAgents } from '../../lib/agent-registry'\nimport { createModelFactory, resolveAllowRuntimeOverride } from '../../lib/model-factory'\nimport {\n agentOverrideModelAllowlistEnvVarName,\n agentOverrideProviderAllowlistEnvVarName,\n canonicalProviderId,\n hasAllowlistSnapshotRestrictions,\n intersectEffectiveAllowlistWithSnapshot,\n intersectAllowlists,\n isProviderAllowed,\n isProviderAllowedInEffective,\n isProviderModelAllowed,\n isProviderModelAllowedInEffective,\n modelAllowlistEnvVarName,\n readAgentRuntimeOverrideAllowlist,\n readAllowedModels,\n readAllowedProviders,\n readAllowlistConfig,\n type TenantAllowlistSnapshot,\n} from '../../lib/model-allowlist'\n\nconst logger = createLogger('ai_assistant')\n\nfunction modelCatalogWithAllowlistFallback(\n models: ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }>,\n allowlistModelIds: string[] | undefined,\n): ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }> {\n if (models.length > 0) return models\n return (allowlistModelIds ?? []).map((id) => ({ id, name: id }))\n}\n\nconst runtimeOverrideUpsertSchema = z.object({\n providerId: z.string().min(1).max(64).nullable().optional(),\n modelId: z.string().min(1).max(256).nullable().optional(),\n baseURL: z.string().url().max(2048).nullable().optional(),\n agentId: z.string().min(1).max(128).nullable().optional(),\n allowedOverrideProviders: z.array(z.string().min(1).max(64)).nullable().optional(),\n allowedOverrideModelsByProvider: z\n .record(z.string().min(1).max(64), z.array(z.string().min(1).max(256)))\n .optional(),\n})\n\nconst runtimeOverrideClearSchema = z.object({\n agentId: z.string().min(1).max(128).nullable().optional(),\n})\n\nexport type RuntimeOverrideUpsertBody = z.infer<typeof runtimeOverrideUpsertSchema>\nexport type RuntimeOverrideClearBody = z.infer<typeof runtimeOverrideClearSchema>\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'AI assistant settings',\n methods: {\n GET: { summary: 'Get AI provider configuration' },\n PUT: {\n summary: 'Upsert per-tenant AI runtime override',\n description:\n 'Creates or updates the per-tenant AI runtime override (provider, model, baseURL). ' +\n 'Optionally scoped to a specific agent via `agentId`. ' +\n 'Gated by `ai_assistant.settings.manage`. ' +\n 'baseURL must match AI_RUNTIME_BASEURL_ALLOWLIST when set.',\n requestBody: {\n contentType: 'application/json',\n description: 'Override payload. All fields nullable/optional; null explicitly clears the axis.',\n schema: runtimeOverrideUpsertSchema,\n },\n responses: [\n { status: 200, description: 'Override saved. Returns the saved row.' },\n ],\n errors: [\n { status: 400, description: 'Validation error: unknown provider, invalid URL, or baseURL not allowlisted.' },\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks ai_assistant.settings.manage.' },\n ],\n },\n DELETE: {\n summary: 'Clear per-tenant AI runtime override',\n description:\n 'Soft-deletes the active per-tenant runtime override. ' +\n 'Pass `agentId` to clear only the agent-specific row; omit to clear the tenant-wide default. ' +\n 'Gated by `ai_assistant.settings.manage`. Idempotent \u2014 returns 200 with `cleared: false` when no active row existed.',\n requestBody: {\n contentType: 'application/json',\n description: 'Optional agentId to scope the delete.',\n schema: runtimeOverrideClearSchema,\n },\n responses: [\n { status: 200, description: 'Returns `{ cleared: boolean }` indicating whether a row was found and removed.' },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks ai_assistant.settings.manage.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },\n PUT: { requireAuth: true, requireFeatures: ['ai_assistant.settings.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['ai_assistant.settings.manage'] },\n}\n\n/**\n * GET /api/ai_assistant/settings\n *\n * Returns the current OpenCode provider configuration from environment variables\n * plus the Phase 4a additive fields: resolvedDefault, tenantOverride, agents[],\n * and availableProviders[].defaultModels.\n */\nexport async function GET(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n try {\n const env = process.env as Record<string, string | undefined>\n const configuredProviderHint = env.OM_AI_PROVIDER?.trim() || env.OPENCODE_PROVIDER?.trim() || null\n const registryProviders = llmProviderRegistry.list()\n const knownProviderIdsForAllowlist: string[] = [\n ...OPEN_CODE_PROVIDER_IDS,\n ...registryProviders\n .map((p) => p.id)\n .filter((id) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(id)),\n ]\n const registryProviderId = configuredProviderHint\n ? canonicalProviderId(configuredProviderHint, registryProviders.map((provider) => provider.id))\n : null\n const registryProvider = registryProviderId ? llmProviderRegistry.get(registryProviderId) : null\n const fallbackOpenCodeProviderId = (\n (configuredProviderHint\n ? canonicalProviderId(configuredProviderHint, OPEN_CODE_PROVIDER_IDS as readonly string[])\n : null) ?? 'openai'\n ) as keyof typeof OPEN_CODE_PROVIDERS\n const fallbackOpenCodeProvider = OPEN_CODE_PROVIDERS[fallbackOpenCodeProviderId]\n\n const providerId = registryProvider?.id ?? fallbackOpenCodeProviderId\n const providerName = registryProvider?.name ?? fallbackOpenCodeProvider?.name ?? providerId\n const defaultProviderModel = registryProvider?.defaultModel ?? fallbackOpenCodeProvider?.defaultModel ?? ''\n const configuredModelHint = env.OM_AI_MODEL?.trim() || env.OPENCODE_MODEL?.trim() || defaultProviderModel\n const fallbackModelWithProvider = joinProviderModel(providerId, configuredModelHint)\n const apiKeyConfigured = registryProvider\n ? registryProvider.isConfigured(env)\n : fallbackOpenCodeProvider\n ? isOpenCodeProviderConfigured(fallbackOpenCodeProviderId)\n : false\n const displayEnvKey = registryProvider\n ? registryProvider.getConfiguredEnvKey(env)\n : fallbackOpenCodeProvider\n ? getOpenCodeProviderConfiguredEnvKey(fallbackOpenCodeProviderId)\n : null\n\n // Check if the MCP API key is configured (required for MCP authentication):\n // either directly via MCP_SERVER_API_KEY or via the shared key file that\n // the containerized mcp service provisions (MCP_SERVER_API_KEY_FILE).\n // The file variant is validated against the api_keys table \u2014 the file\n // outlives DB resets and key deletion, so mere existence would report a\n // green badge while OpenCode -> MCP auth is actually broken.\n const mcpKeyFilePath = process.env.MCP_SERVER_API_KEY_FILE?.trim()\n let mcpKeyConfigured = !!process.env.MCP_SERVER_API_KEY?.trim()\n if (!mcpKeyConfigured && mcpKeyFilePath && existsSync(mcpKeyFilePath)) {\n try {\n const fileSecret = readFileSync(mcpKeyFilePath, 'utf8').trim()\n if (fileSecret.startsWith('omk_')) {\n const keyContainer = await createRequestContainer()\n const keyEm = keyContainer.resolve<EntityManager>('em')\n mcpKeyConfigured = !!(await findApiKeyBySecret(keyEm, fileSecret))\n }\n } catch {\n mcpKeyConfigured = false\n }\n }\n\n // Phase 4a: resolve tenant override row and per-agent resolution matrix\n let tenantOverride: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n agentId: string | null\n updatedAt: string\n } | null = null\n\n let agentResolutions: Array<{\n agentId: string\n moduleId: string\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride: boolean\n codeDefaultProviderId: string | null\n codeDefaultModelId: string | null\n override: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n updatedAt: string\n } | null\n runtimeOverrideAllowlist: {\n env: TenantAllowlistSnapshot | null\n tenant: TenantAllowlistSnapshot | null\n effective: ReturnType<typeof intersectAllowlists>\n envVarNames: {\n providers: string\n modelsByProvider: Record<string, string>\n }\n }\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n }> = []\n\n let resolvedDefault: {\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n } | null = null\n\n let tenantAllowlistSnapshot: TenantAllowlistSnapshot | null = null\n\n try {\n const container = await createRequestContainer()\n const tenantId = auth.tenantId ?? null\n const organizationId = auth.orgId ?? null\n\n if (tenantId) {\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const overrideRow = await repo.getDefault({ tenantId, organizationId, agentId: null })\n if (overrideRow) {\n tenantOverride = {\n providerId: overrideRow.providerId ?? null,\n modelId: overrideRow.modelId ?? null,\n baseURL: overrideRow.baseUrl ?? null,\n agentId: overrideRow.agentId ?? null,\n updatedAt: overrideRow.updatedAt.toISOString(),\n }\n }\n\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n tenantAllowlistSnapshot = await allowlistRepo.getSnapshot({\n tenantId,\n organizationId,\n })\n\n const factory = createModelFactory(container)\n const defaultResolution = factory.resolveModel({\n tenantAllowlist: tenantAllowlistSnapshot,\n tenantOverride: tenantOverride\n ? { providerId: tenantOverride.providerId, modelId: tenantOverride.modelId, baseURL: tenantOverride.baseURL }\n : undefined,\n })\n resolvedDefault = {\n providerId: defaultResolution.providerId,\n modelId: defaultResolution.modelId,\n baseURL: defaultResolution.baseURL ?? null,\n source: defaultResolution.source,\n }\n\n await loadAgentRegistry()\n const agents = listAgents()\n const agentResolutionPromises = agents.map(async (agent) => {\n const agentOverrideRow = await repo.getExact({\n tenantId,\n organizationId,\n agentId: agent.id,\n })\n const agentTenantOverride = agentOverrideRow\n ? {\n providerId: agentOverrideRow.providerId ?? null,\n modelId: agentOverrideRow.modelId ?? null,\n baseURL: agentOverrideRow.baseUrl ?? null,\n }\n : (tenantOverride ?? undefined)\n const agentResolution = factory.resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n tenantOverride: agentTenantOverride,\n tenantAllowlist: tenantAllowlistSnapshot,\n })\n const agentEnvAllowlist = readAgentRuntimeOverrideAllowlist(\n env,\n agent.id,\n knownProviderIdsForAllowlist,\n )\n const agentTenantAllowlist = agentOverrideRow\n ? {\n allowedProviders: agentOverrideRow.allowedOverrideProviders ?? null,\n allowedModelsByProvider: agentOverrideRow.allowedOverrideModelsByProvider ?? {},\n }\n : null\n const baseEffectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIdsForAllowlist,\n tenantAllowlistSnapshot,\n )\n const agentEffectiveAllowlist = intersectEffectiveAllowlistWithSnapshot(\n intersectEffectiveAllowlistWithSnapshot(\n baseEffectiveAllowlist,\n knownProviderIdsForAllowlist,\n agentEnvAllowlist,\n ),\n knownProviderIdsForAllowlist,\n agentTenantAllowlist,\n )\n const agentModelEnvVars = Object.fromEntries(\n knownProviderIdsForAllowlist.map((providerId) => [\n providerId,\n agentOverrideModelAllowlistEnvVarName(agent.id, providerId),\n ]),\n )\n return {\n agentId: agent.id,\n moduleId: agent.moduleId,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n allowRuntimeModelOverride: resolveAllowRuntimeOverride(agent),\n codeDefaultProviderId: agent.defaultProvider ?? null,\n codeDefaultModelId: agent.defaultModel ?? null,\n override: agentOverrideRow\n ? {\n providerId: agentOverrideRow.providerId ?? null,\n modelId: agentOverrideRow.modelId ?? null,\n baseURL: agentOverrideRow.baseUrl ?? null,\n updatedAt: agentOverrideRow.updatedAt.toISOString(),\n }\n : null,\n runtimeOverrideAllowlist: {\n env: agentEnvAllowlist,\n tenant: hasAllowlistSnapshotRestrictions(agentTenantAllowlist)\n ? agentTenantAllowlist\n : null,\n effective: agentEffectiveAllowlist,\n envVarNames: {\n providers: agentOverrideProviderAllowlistEnvVarName(agent.id),\n modelsByProvider: agentModelEnvVars,\n },\n },\n providerId: agentResolution.providerId,\n modelId: agentResolution.modelId,\n baseURL: agentResolution.baseURL ?? null,\n source: agentResolution.source,\n }\n })\n agentResolutions = await Promise.all(agentResolutionPromises)\n }\n } catch (overrideError) {\n // Phase 4a fields are best-effort \u2014 log and continue returning the base response\n logger.warn('AI Settings \u2014 Failed to compute Phase 4a override fields', { err: overrideError })\n }\n\n // Build availableProviders with Phase 4a defaultModels, then clip to the\n // EFFECTIVE allowlist \u2014 env intersected with the per-tenant snapshot.\n // The env allowlist is the OUTER constraint; the tenant allowlist (Phase\n // 1780-6) narrows it further. The settings UI must never offer a value\n // the runtime would refuse to honor.\n const allowlistConfig = readAllowlistConfig(env, knownProviderIdsForAllowlist)\n const effectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIdsForAllowlist,\n tenantAllowlistSnapshot,\n )\n\n const allRawProviders = [\n ...OPEN_CODE_PROVIDER_IDS.map((id) => {\n const info = OPEN_CODE_PROVIDERS[id]\n const registryProvider = llmProviderRegistry.get(id)\n return {\n id,\n name: info.name,\n defaultModel: info.defaultModel,\n envKey: getOpenCodeProviderConfiguredEnvKey(id),\n configured: isOpenCodeProviderConfigured(id),\n defaultModels: registryProvider?.defaultModels ?? [],\n }\n }),\n // Also surface any llmProviderRegistry providers not in OPEN_CODE_PROVIDER_IDS\n ...llmProviderRegistry.list()\n .filter((p) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(p.id))\n .map((p) => ({\n id: p.id,\n name: p.name,\n defaultModel: p.defaultModels[0]?.id ?? '',\n envKey: null,\n configured: p.isConfigured(),\n defaultModels: p.defaultModels,\n })),\n ]\n\n const availableProviders = allRawProviders\n .filter((p) => isProviderAllowedInEffective(effectiveAllowlist, p.id))\n .map((p) => {\n const effectiveModelsList = effectiveAllowlist.modelsByProvider[p.id]\n const catalogModels = modelCatalogWithAllowlistFallback(\n p.defaultModels,\n effectiveModelsList,\n )\n const clippedDefaults = effectiveModelsList !== undefined\n ? catalogModels.filter((m) => effectiveModelsList.includes(m.id))\n : catalogModels\n return {\n ...p,\n defaultModel: effectiveModelsList && !effectiveModelsList.includes(p.defaultModel)\n ? (effectiveModelsList[0] ?? p.defaultModel)\n : p.defaultModel || clippedDefaults[0]?.id || '',\n defaultModels: clippedDefaults,\n }\n })\n\n const allowlistProviders = allRawProviders\n .filter((p) => isProviderAllowed(env, p.id))\n .map((p) => {\n const envModelsList = allowlistConfig.modelsByProvider[p.id]\n const catalogModels = modelCatalogWithAllowlistFallback(\n p.defaultModels,\n envModelsList,\n )\n const envClippedDefaults = envModelsList !== undefined\n ? catalogModels.filter((m) => envModelsList.includes(m.id))\n : catalogModels\n return {\n ...p,\n defaultModel: envModelsList && !envModelsList.includes(p.defaultModel)\n ? (envModelsList[0] ?? p.defaultModel)\n : p.defaultModel || envClippedDefaults[0]?.id || '',\n defaultModels: envClippedDefaults,\n }\n })\n\n return NextResponse.json({\n provider: {\n id: providerId,\n name: providerName,\n model: resolvedDefault\n ? joinProviderModel(resolvedDefault.providerId, resolvedDefault.modelId)\n : fallbackModelWithProvider,\n defaultModel: defaultProviderModel,\n envKey: displayEnvKey,\n configured: apiKeyConfigured,\n },\n availableProviders,\n // Editable universe for the tenant allowlist page. This is clipped only\n // by env so tenant-hidden models remain visible and can be re-enabled.\n allowlistProviders,\n // Snapshot of the env-driven allowlist so the UI can render hints like\n // \"limited to: openai, anthropic\" without re-implementing the parser.\n allowlist: allowlistConfig,\n // Per-tenant allowlist snapshot (Phase 1780-6). `null` when no row has\n // been persisted yet \u2014 the runtime then falls back to env-only\n // enforcement. The UI uses this to drive the editable MultiSelect.\n tenantAllowlist: tenantAllowlistSnapshot,\n // Effective allowlist after intersecting env with tenant. The UI uses\n // this to render the \"what the runtime will actually accept\" summary\n // and to clip pickers without re-implementing the intersection.\n effectiveAllowlist,\n mcpKeyConfigured,\n resolvedDefault,\n tenantOverride,\n agents: agentResolutions,\n })\n } catch (error) {\n logger.error('AI Settings \u2014 GET error', { err: error })\n return NextResponse.json({ error: 'Failed to fetch settings' }, { status: 500 })\n }\n}\n\n/**\n * PUT /api/ai_assistant/settings\n *\n * Upserts the per-tenant AI runtime override (Phase 4a). Requires\n * `ai_assistant.settings.manage`. The body is Zod-validated; a `baseURL`\n * must match `AI_RUNTIME_BASEURL_ALLOWLIST` when that env var is set.\n */\nexport async function PUT(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Request body must be valid JSON.', code: 'validation_error' }, { status: 400 })\n }\n\n const bodyResult = runtimeOverrideUpsertSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return NextResponse.json(\n { error: 'Invalid request body.', code: 'validation_error', issues: bodyResult.error.issues },\n { status: 400 },\n )\n }\n\n const { providerId: requestedProviderId, modelId, baseURL, agentId } = bodyResult.data\n const knownProviderIdsForRequest = llmProviderRegistry.list().map((p) => p.id)\n const providerId = requestedProviderId\n ? canonicalProviderId(requestedProviderId, knownProviderIdsForRequest) ?? requestedProviderId\n : requestedProviderId\n\n if (baseURL && baseURL.trim().length > 0) {\n const allowlist = readBaseurlAllowlist()\n if (!isBaseurlAllowlisted(baseURL.trim(), allowlist)) {\n return NextResponse.json(\n {\n error: `baseURL \"${baseURL}\" is not in AI_RUNTIME_BASEURL_ALLOWLIST.`,\n code: 'baseurl_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n\n const allowedOverrideProviders = bodyResult.data.allowedOverrideProviders === undefined\n ? undefined\n : bodyResult.data.allowedOverrideProviders?.map((id) =>\n canonicalProviderId(id, knownProviderIdsForRequest) ?? id,\n ) ?? null\n const allowedOverrideModelsByProvider = bodyResult.data.allowedOverrideModelsByProvider === undefined\n ? undefined\n : Object.fromEntries(\n Object.entries(bodyResult.data.allowedOverrideModelsByProvider ?? {}).map(([id, models]) => [\n canonicalProviderId(id, knownProviderIdsForRequest) ?? id,\n models,\n ]),\n )\n const hasRuntimeOverrideAllowlistWrite =\n allowedOverrideProviders !== undefined || allowedOverrideModelsByProvider !== undefined\n\n // Env-driven provider/model allowlist (Phase 1780-5) intersected with the\n // per-tenant allowlist (Phase 1780-6): the EFFECTIVE allowlist clips which\n // (provider, model) pairs the runtime accepts. Reject settings PUT requests\n // for pairs outside that effective set so the settings UI never persists a\n // value the runtime would later refuse. Tenant-allowlist enforcement is\n // best-effort here: if the snapshot lookup fails we fall back to env-only\n // checks (the runtime still re-clips at resolution time).\n let putEffectiveAllowlist: ReturnType<typeof intersectAllowlists> | null = null\n if (providerId || hasRuntimeOverrideAllowlistWrite) {\n try {\n const previewContainer = await createRequestContainer()\n const knownIdsForCheck = [\n ...OPEN_CODE_PROVIDER_IDS,\n ...llmProviderRegistry\n .list()\n .map((p) => p.id)\n .filter((id) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(id)),\n ]\n let snapshot: TenantAllowlistSnapshot | null = null\n if (auth.tenantId) {\n try {\n const em = previewContainer.resolve<EntityManager>('em')\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n snapshot = await allowlistRepo.getSnapshot({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n } catch {\n snapshot = null\n }\n }\n putEffectiveAllowlist = intersectAllowlists(\n process.env as Record<string, string | undefined>,\n knownIdsForCheck,\n snapshot,\n )\n } catch {\n putEffectiveAllowlist = null\n }\n\n if (providerId && putEffectiveAllowlist) {\n if (!isProviderAllowedInEffective(putEffectiveAllowlist, providerId)) {\n const source = putEffectiveAllowlist.tenantOverridesActive\n ? 'the effective allowlist (env \u2229 tenant)'\n : 'OM_AI_AVAILABLE_PROVIDERS'\n return NextResponse.json(\n {\n error: `Provider \"${providerId}\" is not in ${source}.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n if (modelId && !isProviderModelAllowedInEffective(putEffectiveAllowlist, providerId, modelId)) {\n const source = putEffectiveAllowlist.tenantOverridesActive\n ? `the effective allowlist (env \u2229 tenant) for \"${providerId}\"`\n : modelAllowlistEnvVarName(providerId)\n return NextResponse.json(\n {\n error: `Model \"${modelId}\" is not in ${source}.`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n } else if (providerId) {\n if (!isProviderAllowed(process.env, providerId)) {\n return NextResponse.json(\n {\n error: `Provider \"${requestedProviderId}\" is not in OM_AI_AVAILABLE_PROVIDERS.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n if (modelId && !isProviderModelAllowed(process.env, providerId, modelId)) {\n return NextResponse.json(\n {\n error: `Model \"${modelId}\" is not in ${modelAllowlistEnvVarName(providerId)}.`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n\n if (hasRuntimeOverrideAllowlistWrite && !agentId) {\n return NextResponse.json(\n {\n error: 'agentId is required when saving chat override allowlist settings.',\n code: 'agent_required',\n },\n { status: 400 },\n )\n }\n\n if (Array.isArray(allowedOverrideProviders)) {\n for (const id of allowedOverrideProviders) {\n if (putEffectiveAllowlist && !isProviderAllowedInEffective(putEffectiveAllowlist, id)) {\n return NextResponse.json(\n {\n error: `Provider \"${id}\" is not in the effective tenant allowlist; per-agent chat override choices may not widen it.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n if (allowedOverrideModelsByProvider) {\n for (const [id, models] of Object.entries(allowedOverrideModelsByProvider)) {\n if (putEffectiveAllowlist && !isProviderAllowedInEffective(putEffectiveAllowlist, id)) {\n return NextResponse.json(\n {\n error: `Provider \"${id}\" is not in the effective tenant allowlist; cannot save per-agent model choices for it.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n for (const allowedModelId of models) {\n if (\n putEffectiveAllowlist &&\n !isProviderModelAllowedInEffective(putEffectiveAllowlist, id, allowedModelId)\n ) {\n return NextResponse.json(\n {\n error: `Model \"${allowedModelId}\" is not in the effective tenant allowlist for \"${id}\".`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n }\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n const canManage = acl.isSuperAdmin || acl.features.includes('ai_assistant.settings.manage')\n if (!canManage) {\n return NextResponse.json({ error: 'Forbidden', code: 'forbidden' }, { status: 403 })\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const upsertInput = {\n agentId: agentId ?? null,\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'providerId')\n ? { providerId: providerId ?? null }\n : {}),\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'modelId')\n ? { modelId: modelId ?? null }\n : {}),\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'baseURL')\n ? { baseURL: baseURL ?? null }\n : {}),\n ...(allowedOverrideProviders !== undefined\n ? { allowedOverrideProviders }\n : {}),\n ...(allowedOverrideModelsByProvider !== undefined\n ? { allowedOverrideModelsByProvider }\n : {}),\n }\n const row = await repo.upsertDefault(\n upsertInput,\n { tenantId: auth.tenantId ?? '', organizationId: auth.orgId ?? null, userId: auth.sub },\n )\n return NextResponse.json({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n agentId: row.agentId,\n providerId: row.providerId,\n modelId: row.modelId,\n baseURL: row.baseUrl,\n allowedOverrideProviders: row.allowedOverrideProviders ?? null,\n allowedOverrideModelsByProvider: row.allowedOverrideModelsByProvider ?? {},\n updatedAt: row.updatedAt,\n })\n } catch (error) {\n if (error instanceof AiAgentRuntimeOverrideValidationError) {\n return NextResponse.json({ error: error.message, code: 'provider_unknown' }, { status: 400 })\n }\n logger.error('AI Settings \u2014 PUT error', { err: error })\n return NextResponse.json({ error: 'Failed to save runtime override.' }, { status: 500 })\n }\n}\n\n/**\n * DELETE /api/ai_assistant/settings\n *\n * Soft-deletes the active per-tenant AI runtime override (Phase 4a). Requires\n * `ai_assistant.settings.manage`. Pass `agentId` in the body to clear only\n * the agent-specific row; omit (or null) to clear the tenant-wide default.\n * Idempotent \u2014 returns `{ cleared: false }` when no active row was found.\n */\nexport async function DELETE(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let parsedBody: unknown = {}\n try {\n parsedBody = await req.json()\n } catch {\n // Body is optional for DELETE \u2014 empty body is fine\n }\n\n const bodyResult = runtimeOverrideClearSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return NextResponse.json(\n { error: 'Invalid request body.', code: 'validation_error', issues: bodyResult.error.issues },\n { status: 400 },\n )\n }\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n const canManage = acl.isSuperAdmin || acl.features.includes('ai_assistant.settings.manage')\n if (!canManage) {\n return NextResponse.json({ error: 'Forbidden', code: 'forbidden' }, { status: 403 })\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const cleared = await repo.clearDefault({\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n agentId: bodyResult.data.agentId ?? null,\n })\n return NextResponse.json({ cleared })\n } catch (error) {\n logger.error('AI Settings \u2014 DELETE error', { err: error })\n return NextResponse.json({ error: 'Failed to clear runtime override.' }, { status: 500 })\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { existsSync, readFileSync } from 'node:fs'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { findApiKeyBySecret } from '@open-mercato/core/modules/api_keys/services/apiKeyService'\nimport { llmProviderRegistry } from '@open-mercato/shared/lib/ai/llm-provider-registry'\nimport { joinProviderModel } from '@open-mercato/shared/lib/ai/model-id'\nimport {\n OPEN_CODE_PROVIDER_IDS,\n OPEN_CODE_PROVIDERS,\n getOpenCodeProviderConfiguredEnvKey,\n isOpenCodeProviderConfigured,\n} from '@open-mercato/shared/lib/ai/opencode-provider'\nimport { AiAgentRuntimeOverrideRepository, AiAgentRuntimeOverrideValidationError } from '../../data/repositories/AiAgentRuntimeOverrideRepository'\nimport { resolveModerationPolicy } from '../../lib/moderation-policy'\nimport { AiTenantModelAllowlistRepository } from '../../data/repositories/AiTenantModelAllowlistRepository'\nimport { isBaseurlAllowlisted, readBaseurlAllowlist } from '../../lib/baseurl-allowlist'\nimport { loadAgentRegistry, listAgents } from '../../lib/agent-registry'\nimport { createModelFactory, resolveAllowRuntimeOverride } from '../../lib/model-factory'\nimport {\n agentOverrideModelAllowlistEnvVarName,\n agentOverrideProviderAllowlistEnvVarName,\n canonicalProviderId,\n hasAllowlistSnapshotRestrictions,\n intersectEffectiveAllowlistWithSnapshot,\n intersectAllowlists,\n isProviderAllowed,\n isProviderAllowedInEffective,\n isProviderModelAllowed,\n isProviderModelAllowedInEffective,\n modelAllowlistEnvVarName,\n readAgentRuntimeOverrideAllowlist,\n readAllowedModels,\n readAllowedProviders,\n readAllowlistConfig,\n type TenantAllowlistSnapshot,\n} from '../../lib/model-allowlist'\n\nconst logger = createLogger('ai_assistant')\n\nfunction modelCatalogWithAllowlistFallback(\n models: ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }>,\n allowlistModelIds: string[] | undefined,\n): ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }> {\n if (models.length > 0) return models\n return (allowlistModelIds ?? []).map((id) => ({ id, name: id }))\n}\n\nconst runtimeOverrideUpsertSchema = z.object({\n providerId: z.string().min(1).max(64).nullable().optional(),\n modelId: z.string().min(1).max(256).nullable().optional(),\n baseURL: z.string().url().max(2048).nullable().optional(),\n agentId: z.string().min(1).max(128).nullable().optional(),\n allowedOverrideProviders: z.array(z.string().min(1).max(64)).nullable().optional(),\n allowedOverrideModelsByProvider: z\n .record(z.string().min(1).max(64), z.array(z.string().min(1).max(256)))\n .optional(),\n // Input moderation override: true = on, false = off, null = inherit.\n // Spec 2026-06-04-ai-input-moderation-and-safety-identifiers.\n inputModeration: z.boolean().nullable().optional(),\n})\n\nconst runtimeOverrideClearSchema = z.object({\n agentId: z.string().min(1).max(128).nullable().optional(),\n})\n\nexport type RuntimeOverrideUpsertBody = z.infer<typeof runtimeOverrideUpsertSchema>\nexport type RuntimeOverrideClearBody = z.infer<typeof runtimeOverrideClearSchema>\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'AI assistant settings',\n methods: {\n GET: { summary: 'Get AI provider configuration' },\n PUT: {\n summary: 'Upsert per-tenant AI runtime override',\n description:\n 'Creates or updates the per-tenant AI runtime override (provider, model, baseURL). ' +\n 'Optionally scoped to a specific agent via `agentId`. ' +\n 'Gated by `ai_assistant.settings.manage`. ' +\n 'baseURL must match AI_RUNTIME_BASEURL_ALLOWLIST when set.',\n requestBody: {\n contentType: 'application/json',\n description: 'Override payload. All fields nullable/optional; null explicitly clears the axis.',\n schema: runtimeOverrideUpsertSchema,\n },\n responses: [\n { status: 200, description: 'Override saved. Returns the saved row.' },\n ],\n errors: [\n { status: 400, description: 'Validation error: unknown provider, invalid URL, or baseURL not allowlisted.' },\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks ai_assistant.settings.manage.' },\n ],\n },\n DELETE: {\n summary: 'Clear per-tenant AI runtime override',\n description:\n 'Soft-deletes the active per-tenant runtime override. ' +\n 'Pass `agentId` to clear only the agent-specific row; omit to clear the tenant-wide default. ' +\n 'Gated by `ai_assistant.settings.manage`. Idempotent \u2014 returns 200 with `cleared: false` when no active row existed.',\n requestBody: {\n contentType: 'application/json',\n description: 'Optional agentId to scope the delete.',\n schema: runtimeOverrideClearSchema,\n },\n responses: [\n { status: 200, description: 'Returns `{ cleared: boolean }` indicating whether a row was found and removed.' },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks ai_assistant.settings.manage.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },\n PUT: { requireAuth: true, requireFeatures: ['ai_assistant.settings.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['ai_assistant.settings.manage'] },\n}\n\n/**\n * GET /api/ai_assistant/settings\n *\n * Returns the current OpenCode provider configuration from environment variables\n * plus the Phase 4a additive fields: resolvedDefault, tenantOverride, agents[],\n * and availableProviders[].defaultModels.\n */\nexport async function GET(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n try {\n const env = process.env as Record<string, string | undefined>\n const configuredProviderHint = env.OM_AI_PROVIDER?.trim() || env.OPENCODE_PROVIDER?.trim() || null\n const registryProviders = llmProviderRegistry.list()\n const knownProviderIdsForAllowlist: string[] = [\n ...OPEN_CODE_PROVIDER_IDS,\n ...registryProviders\n .map((p) => p.id)\n .filter((id) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(id)),\n ]\n const registryProviderId = configuredProviderHint\n ? canonicalProviderId(configuredProviderHint, registryProviders.map((provider) => provider.id))\n : null\n const registryProvider = registryProviderId ? llmProviderRegistry.get(registryProviderId) : null\n const fallbackOpenCodeProviderId = (\n (configuredProviderHint\n ? canonicalProviderId(configuredProviderHint, OPEN_CODE_PROVIDER_IDS as readonly string[])\n : null) ?? 'openai'\n ) as keyof typeof OPEN_CODE_PROVIDERS\n const fallbackOpenCodeProvider = OPEN_CODE_PROVIDERS[fallbackOpenCodeProviderId]\n\n const providerId = registryProvider?.id ?? fallbackOpenCodeProviderId\n const providerName = registryProvider?.name ?? fallbackOpenCodeProvider?.name ?? providerId\n const defaultProviderModel = registryProvider?.defaultModel ?? fallbackOpenCodeProvider?.defaultModel ?? ''\n const configuredModelHint = env.OM_AI_MODEL?.trim() || env.OPENCODE_MODEL?.trim() || defaultProviderModel\n const fallbackModelWithProvider = joinProviderModel(providerId, configuredModelHint)\n const apiKeyConfigured = registryProvider\n ? registryProvider.isConfigured(env)\n : fallbackOpenCodeProvider\n ? isOpenCodeProviderConfigured(fallbackOpenCodeProviderId)\n : false\n const displayEnvKey = registryProvider\n ? registryProvider.getConfiguredEnvKey(env)\n : fallbackOpenCodeProvider\n ? getOpenCodeProviderConfiguredEnvKey(fallbackOpenCodeProviderId)\n : null\n\n // Check if the MCP API key is configured (required for MCP authentication):\n // either directly via MCP_SERVER_API_KEY or via the shared key file that\n // the containerized mcp service provisions (MCP_SERVER_API_KEY_FILE).\n // The file variant is validated against the api_keys table \u2014 the file\n // outlives DB resets and key deletion, so mere existence would report a\n // green badge while OpenCode -> MCP auth is actually broken.\n const mcpKeyFilePath = process.env.MCP_SERVER_API_KEY_FILE?.trim()\n let mcpKeyConfigured = !!process.env.MCP_SERVER_API_KEY?.trim()\n if (!mcpKeyConfigured && mcpKeyFilePath && existsSync(mcpKeyFilePath)) {\n try {\n const fileSecret = readFileSync(mcpKeyFilePath, 'utf8').trim()\n if (fileSecret.startsWith('omk_')) {\n const keyContainer = await createRequestContainer()\n const keyEm = keyContainer.resolve<EntityManager>('em')\n mcpKeyConfigured = !!(await findApiKeyBySecret(keyEm, fileSecret))\n }\n } catch {\n mcpKeyConfigured = false\n }\n }\n\n // Phase 4a: resolve tenant override row and per-agent resolution matrix\n let tenantOverride: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n agentId: string | null\n updatedAt: string\n } | null = null\n\n let agentResolutions: Array<{\n agentId: string\n moduleId: string\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride: boolean\n codeDefaultProviderId: string | null\n codeDefaultModelId: string | null\n override: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n updatedAt: string\n } | null\n runtimeOverrideAllowlist: {\n env: TenantAllowlistSnapshot | null\n tenant: TenantAllowlistSnapshot | null\n effective: ReturnType<typeof intersectAllowlists>\n envVarNames: {\n providers: string\n modelsByProvider: Record<string, string>\n }\n }\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n }> = []\n\n let resolvedDefault: {\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n } | null = null\n\n let tenantAllowlistSnapshot: TenantAllowlistSnapshot | null = null\n\n try {\n const container = await createRequestContainer()\n const tenantId = auth.tenantId ?? null\n const organizationId = auth.orgId ?? null\n\n if (tenantId) {\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const overrideRow = await repo.getDefault({ tenantId, organizationId, agentId: null })\n const tenantWideInputModeration = overrideRow?.inputModeration ?? null\n if (overrideRow) {\n tenantOverride = {\n providerId: overrideRow.providerId ?? null,\n modelId: overrideRow.modelId ?? null,\n baseURL: overrideRow.baseUrl ?? null,\n agentId: overrideRow.agentId ?? null,\n updatedAt: overrideRow.updatedAt.toISOString(),\n }\n }\n\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n tenantAllowlistSnapshot = await allowlistRepo.getSnapshot({\n tenantId,\n organizationId,\n })\n\n const factory = createModelFactory(container)\n const defaultResolution = factory.resolveModel({\n tenantAllowlist: tenantAllowlistSnapshot,\n tenantOverride: tenantOverride\n ? { providerId: tenantOverride.providerId, modelId: tenantOverride.modelId, baseURL: tenantOverride.baseURL }\n : undefined,\n })\n resolvedDefault = {\n providerId: defaultResolution.providerId,\n modelId: defaultResolution.modelId,\n baseURL: defaultResolution.baseURL ?? null,\n source: defaultResolution.source,\n }\n\n await loadAgentRegistry()\n const agents = listAgents()\n const agentResolutionPromises = agents.map(async (agent) => {\n const agentOverrideRow = await repo.getExact({\n tenantId,\n organizationId,\n agentId: agent.id,\n })\n const agentTenantOverride = agentOverrideRow\n ? {\n providerId: agentOverrideRow.providerId ?? null,\n modelId: agentOverrideRow.modelId ?? null,\n baseURL: agentOverrideRow.baseUrl ?? null,\n }\n : (tenantOverride ?? undefined)\n const agentResolution = factory.resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n tenantOverride: agentTenantOverride,\n tenantAllowlist: tenantAllowlistSnapshot,\n })\n const agentEnvAllowlist = readAgentRuntimeOverrideAllowlist(\n env,\n agent.id,\n knownProviderIdsForAllowlist,\n )\n const agentTenantAllowlist = agentOverrideRow\n ? {\n allowedProviders: agentOverrideRow.allowedOverrideProviders ?? null,\n allowedModelsByProvider: agentOverrideRow.allowedOverrideModelsByProvider ?? {},\n }\n : null\n const baseEffectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIdsForAllowlist,\n tenantAllowlistSnapshot,\n )\n const agentEffectiveAllowlist = intersectEffectiveAllowlistWithSnapshot(\n intersectEffectiveAllowlistWithSnapshot(\n baseEffectiveAllowlist,\n knownProviderIdsForAllowlist,\n agentEnvAllowlist,\n ),\n knownProviderIdsForAllowlist,\n agentTenantAllowlist,\n )\n const agentModelEnvVars = Object.fromEntries(\n knownProviderIdsForAllowlist.map((providerId) => [\n providerId,\n agentOverrideModelAllowlistEnvVarName(agent.id, providerId),\n ]),\n )\n return {\n agentId: agent.id,\n moduleId: agent.moduleId,\n allowRuntimeOverride: resolveAllowRuntimeOverride(agent),\n allowRuntimeModelOverride: resolveAllowRuntimeOverride(agent),\n codeDefaultProviderId: agent.defaultProvider ?? null,\n codeDefaultModelId: agent.defaultModel ?? null,\n override: agentOverrideRow\n ? {\n providerId: agentOverrideRow.providerId ?? null,\n modelId: agentOverrideRow.modelId ?? null,\n baseURL: agentOverrideRow.baseUrl ?? null,\n updatedAt: agentOverrideRow.updatedAt.toISOString(),\n }\n : null,\n runtimeOverrideAllowlist: {\n env: agentEnvAllowlist,\n tenant: hasAllowlistSnapshotRestrictions(agentTenantAllowlist)\n ? agentTenantAllowlist\n : null,\n effective: agentEffectiveAllowlist,\n envVarNames: {\n providers: agentOverrideProviderAllowlistEnvVarName(agent.id),\n modelsByProvider: agentModelEnvVars,\n },\n },\n providerId: agentResolution.providerId,\n modelId: agentResolution.modelId,\n baseURL: agentResolution.baseURL ?? null,\n source: agentResolution.source,\n moderation: {\n // `untrustedInput` agents enforce moderation; the UI renders a\n // non-editable \"Enforced\" badge for them.\n enforced: agent.untrustedInput === true,\n // Per-agent control value: null = inherit, true = on, false = off.\n override: agentOverrideRow?.inputModeration ?? null,\n // Resolved runtime policy (enforced | on | off) after precedence.\n effective: resolveModerationPolicy({\n untrustedInput: agent.untrustedInput,\n perAgentOverride: agentOverrideRow?.inputModeration ?? null,\n tenantWideOverride: tenantWideInputModeration,\n env,\n }),\n },\n }\n })\n agentResolutions = await Promise.all(agentResolutionPromises)\n }\n } catch (overrideError) {\n // Phase 4a fields are best-effort \u2014 log and continue returning the base response\n logger.warn('AI Settings \u2014 Failed to compute Phase 4a override fields', { err: overrideError })\n }\n\n // Build availableProviders with Phase 4a defaultModels, then clip to the\n // EFFECTIVE allowlist \u2014 env intersected with the per-tenant snapshot.\n // The env allowlist is the OUTER constraint; the tenant allowlist (Phase\n // 1780-6) narrows it further. The settings UI must never offer a value\n // the runtime would refuse to honor.\n const allowlistConfig = readAllowlistConfig(env, knownProviderIdsForAllowlist)\n const effectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIdsForAllowlist,\n tenantAllowlistSnapshot,\n )\n\n const allRawProviders = [\n ...OPEN_CODE_PROVIDER_IDS.map((id) => {\n const info = OPEN_CODE_PROVIDERS[id]\n const registryProvider = llmProviderRegistry.get(id)\n return {\n id,\n name: info.name,\n defaultModel: info.defaultModel,\n envKey: getOpenCodeProviderConfiguredEnvKey(id),\n configured: isOpenCodeProviderConfigured(id),\n defaultModels: registryProvider?.defaultModels ?? [],\n }\n }),\n // Also surface any llmProviderRegistry providers not in OPEN_CODE_PROVIDER_IDS\n ...llmProviderRegistry.list()\n .filter((p) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(p.id))\n .map((p) => ({\n id: p.id,\n name: p.name,\n defaultModel: p.defaultModels[0]?.id ?? '',\n envKey: null,\n configured: p.isConfigured(),\n defaultModels: p.defaultModels,\n })),\n ]\n\n const availableProviders = allRawProviders\n .filter((p) => isProviderAllowedInEffective(effectiveAllowlist, p.id))\n .map((p) => {\n const effectiveModelsList = effectiveAllowlist.modelsByProvider[p.id]\n const catalogModels = modelCatalogWithAllowlistFallback(\n p.defaultModels,\n effectiveModelsList,\n )\n const clippedDefaults = effectiveModelsList !== undefined\n ? catalogModels.filter((m) => effectiveModelsList.includes(m.id))\n : catalogModels\n return {\n ...p,\n defaultModel: effectiveModelsList && !effectiveModelsList.includes(p.defaultModel)\n ? (effectiveModelsList[0] ?? p.defaultModel)\n : p.defaultModel || clippedDefaults[0]?.id || '',\n defaultModels: clippedDefaults,\n }\n })\n\n const allowlistProviders = allRawProviders\n .filter((p) => isProviderAllowed(env, p.id))\n .map((p) => {\n const envModelsList = allowlistConfig.modelsByProvider[p.id]\n const catalogModels = modelCatalogWithAllowlistFallback(\n p.defaultModels,\n envModelsList,\n )\n const envClippedDefaults = envModelsList !== undefined\n ? catalogModels.filter((m) => envModelsList.includes(m.id))\n : catalogModels\n return {\n ...p,\n defaultModel: envModelsList && !envModelsList.includes(p.defaultModel)\n ? (envModelsList[0] ?? p.defaultModel)\n : p.defaultModel || envClippedDefaults[0]?.id || '',\n defaultModels: envClippedDefaults,\n }\n })\n\n return NextResponse.json({\n provider: {\n id: providerId,\n name: providerName,\n model: resolvedDefault\n ? joinProviderModel(resolvedDefault.providerId, resolvedDefault.modelId)\n : fallbackModelWithProvider,\n defaultModel: defaultProviderModel,\n envKey: displayEnvKey,\n configured: apiKeyConfigured,\n },\n availableProviders,\n // Editable universe for the tenant allowlist page. This is clipped only\n // by env so tenant-hidden models remain visible and can be re-enabled.\n allowlistProviders,\n // Snapshot of the env-driven allowlist so the UI can render hints like\n // \"limited to: openai, anthropic\" without re-implementing the parser.\n allowlist: allowlistConfig,\n // Per-tenant allowlist snapshot (Phase 1780-6). `null` when no row has\n // been persisted yet \u2014 the runtime then falls back to env-only\n // enforcement. The UI uses this to drive the editable MultiSelect.\n tenantAllowlist: tenantAllowlistSnapshot,\n // Effective allowlist after intersecting env with tenant. The UI uses\n // this to render the \"what the runtime will actually accept\" summary\n // and to clip pickers without re-implementing the intersection.\n effectiveAllowlist,\n mcpKeyConfigured,\n resolvedDefault,\n tenantOverride,\n agents: agentResolutions,\n })\n } catch (error) {\n logger.error('AI Settings \u2014 GET error', { err: error })\n return NextResponse.json({ error: 'Failed to fetch settings' }, { status: 500 })\n }\n}\n\n/**\n * PUT /api/ai_assistant/settings\n *\n * Upserts the per-tenant AI runtime override (Phase 4a). Requires\n * `ai_assistant.settings.manage`. The body is Zod-validated; a `baseURL`\n * must match `AI_RUNTIME_BASEURL_ALLOWLIST` when that env var is set.\n */\nexport async function PUT(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return NextResponse.json({ error: 'Request body must be valid JSON.', code: 'validation_error' }, { status: 400 })\n }\n\n const bodyResult = runtimeOverrideUpsertSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return NextResponse.json(\n { error: 'Invalid request body.', code: 'validation_error', issues: bodyResult.error.issues },\n { status: 400 },\n )\n }\n\n const { providerId: requestedProviderId, modelId, baseURL, agentId } = bodyResult.data\n const knownProviderIdsForRequest = llmProviderRegistry.list().map((p) => p.id)\n const providerId = requestedProviderId\n ? canonicalProviderId(requestedProviderId, knownProviderIdsForRequest) ?? requestedProviderId\n : requestedProviderId\n\n if (baseURL && baseURL.trim().length > 0) {\n const allowlist = readBaseurlAllowlist()\n if (!isBaseurlAllowlisted(baseURL.trim(), allowlist)) {\n return NextResponse.json(\n {\n error: `baseURL \"${baseURL}\" is not in AI_RUNTIME_BASEURL_ALLOWLIST.`,\n code: 'baseurl_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n\n const allowedOverrideProviders = bodyResult.data.allowedOverrideProviders === undefined\n ? undefined\n : bodyResult.data.allowedOverrideProviders?.map((id) =>\n canonicalProviderId(id, knownProviderIdsForRequest) ?? id,\n ) ?? null\n const allowedOverrideModelsByProvider = bodyResult.data.allowedOverrideModelsByProvider === undefined\n ? undefined\n : Object.fromEntries(\n Object.entries(bodyResult.data.allowedOverrideModelsByProvider ?? {}).map(([id, models]) => [\n canonicalProviderId(id, knownProviderIdsForRequest) ?? id,\n models,\n ]),\n )\n const hasRuntimeOverrideAllowlistWrite =\n allowedOverrideProviders !== undefined || allowedOverrideModelsByProvider !== undefined\n\n // Env-driven provider/model allowlist (Phase 1780-5) intersected with the\n // per-tenant allowlist (Phase 1780-6): the EFFECTIVE allowlist clips which\n // (provider, model) pairs the runtime accepts. Reject settings PUT requests\n // for pairs outside that effective set so the settings UI never persists a\n // value the runtime would later refuse. Tenant-allowlist enforcement is\n // best-effort here: if the snapshot lookup fails we fall back to env-only\n // checks (the runtime still re-clips at resolution time).\n let putEffectiveAllowlist: ReturnType<typeof intersectAllowlists> | null = null\n if (providerId || hasRuntimeOverrideAllowlistWrite) {\n try {\n const previewContainer = await createRequestContainer()\n const knownIdsForCheck = [\n ...OPEN_CODE_PROVIDER_IDS,\n ...llmProviderRegistry\n .list()\n .map((p) => p.id)\n .filter((id) => !(OPEN_CODE_PROVIDER_IDS as readonly string[]).includes(id)),\n ]\n let snapshot: TenantAllowlistSnapshot | null = null\n if (auth.tenantId) {\n try {\n const em = previewContainer.resolve<EntityManager>('em')\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n snapshot = await allowlistRepo.getSnapshot({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n } catch {\n snapshot = null\n }\n }\n putEffectiveAllowlist = intersectAllowlists(\n process.env as Record<string, string | undefined>,\n knownIdsForCheck,\n snapshot,\n )\n } catch {\n putEffectiveAllowlist = null\n }\n\n if (providerId && putEffectiveAllowlist) {\n if (!isProviderAllowedInEffective(putEffectiveAllowlist, providerId)) {\n const source = putEffectiveAllowlist.tenantOverridesActive\n ? 'the effective allowlist (env \u2229 tenant)'\n : 'OM_AI_AVAILABLE_PROVIDERS'\n return NextResponse.json(\n {\n error: `Provider \"${providerId}\" is not in ${source}.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n if (modelId && !isProviderModelAllowedInEffective(putEffectiveAllowlist, providerId, modelId)) {\n const source = putEffectiveAllowlist.tenantOverridesActive\n ? `the effective allowlist (env \u2229 tenant) for \"${providerId}\"`\n : modelAllowlistEnvVarName(providerId)\n return NextResponse.json(\n {\n error: `Model \"${modelId}\" is not in ${source}.`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n } else if (providerId) {\n if (!isProviderAllowed(process.env, providerId)) {\n return NextResponse.json(\n {\n error: `Provider \"${requestedProviderId}\" is not in OM_AI_AVAILABLE_PROVIDERS.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n if (modelId && !isProviderModelAllowed(process.env, providerId, modelId)) {\n return NextResponse.json(\n {\n error: `Model \"${modelId}\" is not in ${modelAllowlistEnvVarName(providerId)}.`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n\n if (hasRuntimeOverrideAllowlistWrite && !agentId) {\n return NextResponse.json(\n {\n error: 'agentId is required when saving chat override allowlist settings.',\n code: 'agent_required',\n },\n { status: 400 },\n )\n }\n\n if (Array.isArray(allowedOverrideProviders)) {\n for (const id of allowedOverrideProviders) {\n if (putEffectiveAllowlist && !isProviderAllowedInEffective(putEffectiveAllowlist, id)) {\n return NextResponse.json(\n {\n error: `Provider \"${id}\" is not in the effective tenant allowlist; per-agent chat override choices may not widen it.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n if (allowedOverrideModelsByProvider) {\n for (const [id, models] of Object.entries(allowedOverrideModelsByProvider)) {\n if (putEffectiveAllowlist && !isProviderAllowedInEffective(putEffectiveAllowlist, id)) {\n return NextResponse.json(\n {\n error: `Provider \"${id}\" is not in the effective tenant allowlist; cannot save per-agent model choices for it.`,\n code: 'provider_not_allowlisted',\n },\n { status: 400 },\n )\n }\n for (const allowedModelId of models) {\n if (\n putEffectiveAllowlist &&\n !isProviderModelAllowedInEffective(putEffectiveAllowlist, id, allowedModelId)\n ) {\n return NextResponse.json(\n {\n error: `Model \"${allowedModelId}\" is not in the effective tenant allowlist for \"${id}\".`,\n code: 'model_not_allowlisted',\n },\n { status: 400 },\n )\n }\n }\n }\n }\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const canManage = await rbacService.userHasAllFeatures(\n auth.sub,\n ['ai_assistant.settings.manage'],\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!canManage) {\n return NextResponse.json({ error: 'Forbidden', code: 'forbidden' }, { status: 403 })\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const upsertInput = {\n agentId: agentId ?? null,\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'providerId')\n ? { providerId: providerId ?? null }\n : {}),\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'modelId')\n ? { modelId: modelId ?? null }\n : {}),\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'baseURL')\n ? { baseURL: baseURL ?? null }\n : {}),\n ...(allowedOverrideProviders !== undefined\n ? { allowedOverrideProviders }\n : {}),\n ...(allowedOverrideModelsByProvider !== undefined\n ? { allowedOverrideModelsByProvider }\n : {}),\n ...(Object.prototype.hasOwnProperty.call(bodyResult.data, 'inputModeration')\n ? { inputModeration: bodyResult.data.inputModeration ?? null }\n : {}),\n }\n const row = await repo.upsertDefault(\n upsertInput,\n { tenantId: auth.tenantId ?? '', organizationId: auth.orgId ?? null, userId: auth.sub },\n )\n return NextResponse.json({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n agentId: row.agentId,\n providerId: row.providerId,\n modelId: row.modelId,\n baseURL: row.baseUrl,\n allowedOverrideProviders: row.allowedOverrideProviders ?? null,\n allowedOverrideModelsByProvider: row.allowedOverrideModelsByProvider ?? {},\n inputModeration: row.inputModeration ?? null,\n updatedAt: row.updatedAt,\n })\n } catch (error) {\n if (error instanceof AiAgentRuntimeOverrideValidationError) {\n return NextResponse.json({ error: error.message, code: 'provider_unknown' }, { status: 400 })\n }\n logger.error('AI Settings \u2014 PUT error', { err: error })\n return NextResponse.json({ error: 'Failed to save runtime override.' }, { status: 500 })\n }\n}\n\n/**\n * DELETE /api/ai_assistant/settings\n *\n * Soft-deletes the active per-tenant AI runtime override (Phase 4a). Requires\n * `ai_assistant.settings.manage`. Pass `agentId` in the body to clear only\n * the agent-specific row; omit (or null) to clear the tenant-wide default.\n * Idempotent \u2014 returns `{ cleared: false }` when no active row was found.\n */\nexport async function DELETE(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n let parsedBody: unknown = {}\n try {\n parsedBody = await req.json()\n } catch {\n // Body is optional for DELETE \u2014 empty body is fine\n }\n\n const bodyResult = runtimeOverrideClearSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return NextResponse.json(\n { error: 'Invalid request body.', code: 'validation_error', issues: bodyResult.error.issues },\n { status: 400 },\n )\n }\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const canManage = await rbacService.userHasAllFeatures(\n auth.sub,\n ['ai_assistant.settings.manage'],\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!canManage) {\n return NextResponse.json({ error: 'Forbidden', code: 'forbidden' }, { status: 403 })\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const cleared = await repo.clearDefault({\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? null,\n agentId: bodyResult.data.agentId ?? null,\n })\n return NextResponse.json({ cleared })\n } catch (error) {\n logger.error('AI Settings \u2014 DELETE error', { err: error })\n return NextResponse.json({ error: 'Failed to clear runtime override.' }, { status: 500 })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["providerId", "registryProvider"]
|
|
7
7
|
}
|
package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js
CHANGED
|
@@ -499,7 +499,7 @@ function MutationPolicySection({ agent }) {
|
|
|
499
499
|
/* @__PURE__ */ jsxs(
|
|
500
500
|
Alert,
|
|
501
501
|
{
|
|
502
|
-
|
|
502
|
+
status: "information",
|
|
503
503
|
icon: /* @__PURE__ */ jsx(ShieldAlert, { "aria-hidden": "true" }),
|
|
504
504
|
"data-ai-agent-mutation-policy-notice": true,
|
|
505
505
|
children: [
|
|
@@ -522,7 +522,7 @@ function MutationPolicySection({ agent }) {
|
|
|
522
522
|
"Loading mutation policy..."
|
|
523
523
|
)
|
|
524
524
|
}
|
|
525
|
-
) : query.isError ? /* @__PURE__ */ jsxs(Alert, {
|
|
525
|
+
) : query.isError ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-mutation-policy-load-error": true, children: [
|
|
526
526
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
527
527
|
"ai_assistant.agents.mutation_policy.loadErrorTitle",
|
|
528
528
|
"Failed to load mutation policy"
|
|
@@ -594,11 +594,11 @@ function MutationPolicySection({ agent }) {
|
|
|
594
594
|
})
|
|
595
595
|
}
|
|
596
596
|
),
|
|
597
|
-
state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, {
|
|
597
|
+
state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { status: "success", "data-ai-agent-mutation-policy-state": "success", children: [
|
|
598
598
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.mutation_policy.savedTitle", "Mutation policy updated") }),
|
|
599
599
|
/* @__PURE__ */ jsx(AlertDescription, { children: state.message })
|
|
600
600
|
] }) : null,
|
|
601
|
-
state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, {
|
|
601
|
+
state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-mutation-policy-state": "error", children: [
|
|
602
602
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
603
603
|
"ai_assistant.agents.mutation_policy.errorTitle",
|
|
604
604
|
"Failed to update mutation policy"
|
|
@@ -641,6 +641,111 @@ function MutationPolicySection({ agent }) {
|
|
|
641
641
|
}
|
|
642
642
|
);
|
|
643
643
|
}
|
|
644
|
+
function ModerationSection({ agent }) {
|
|
645
|
+
const t = useT();
|
|
646
|
+
const queryClient = useQueryClient();
|
|
647
|
+
const settingsQuery = useQuery({
|
|
648
|
+
queryKey: ["ai_assistant", "agent_settings", "runtime_settings"],
|
|
649
|
+
queryFn: fetchRuntimeSettings,
|
|
650
|
+
retry: false
|
|
651
|
+
});
|
|
652
|
+
const agentResolution = settingsQuery.data?.agents.find((entry) => entry.agentId === agent.id) ?? null;
|
|
653
|
+
const moderation = agentResolution?.moderation ?? null;
|
|
654
|
+
const enforced = moderation?.enforced === true;
|
|
655
|
+
const currentValue = moderation?.override === true ? "on" : moderation?.override === false ? "off" : "inherit";
|
|
656
|
+
const [isSaving, setIsSaving] = React.useState(false);
|
|
657
|
+
const { runMutation } = useGuardedMutation({ contextId: `ai-agent-moderation-${agent.id}` });
|
|
658
|
+
const save = React.useCallback(
|
|
659
|
+
async (value) => {
|
|
660
|
+
if (isSaving || enforced) return;
|
|
661
|
+
setIsSaving(true);
|
|
662
|
+
try {
|
|
663
|
+
const inputModeration = value === "inherit" ? null : value === "on";
|
|
664
|
+
await runMutation({
|
|
665
|
+
operation: async () => {
|
|
666
|
+
const { ok, status, result } = await apiCall(
|
|
667
|
+
"/api/ai_assistant/settings",
|
|
668
|
+
{
|
|
669
|
+
method: "PUT",
|
|
670
|
+
headers: { "content-type": "application/json" },
|
|
671
|
+
credentials: "include",
|
|
672
|
+
body: JSON.stringify({ agentId: agent.id, inputModeration })
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
if (!ok) {
|
|
676
|
+
throw new Error(result?.error ?? `[internal] Failed to save moderation setting (${status}).`);
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
context: {}
|
|
680
|
+
});
|
|
681
|
+
flash(t("ai_assistant.agents.moderation.saved", "Input moderation setting saved."), "success");
|
|
682
|
+
await queryClient.invalidateQueries({
|
|
683
|
+
queryKey: ["ai_assistant", "agent_settings", "runtime_settings"]
|
|
684
|
+
});
|
|
685
|
+
} catch (err) {
|
|
686
|
+
flash(err instanceof Error ? err.message : String(err), "error");
|
|
687
|
+
} finally {
|
|
688
|
+
setIsSaving(false);
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
[agent.id, enforced, isSaving, queryClient, runMutation, t]
|
|
692
|
+
);
|
|
693
|
+
return /* @__PURE__ */ jsxs(
|
|
694
|
+
"section",
|
|
695
|
+
{
|
|
696
|
+
className: "rounded-lg border border-border bg-background p-4",
|
|
697
|
+
"data-ai-agent-moderation": agent.id,
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
|
|
700
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
|
|
701
|
+
/* @__PURE__ */ jsx(ShieldAlert, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
|
|
702
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
703
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.moderation.title", "Input moderation") }),
|
|
704
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
|
|
705
|
+
"ai_assistant.agents.moderation.subtitle",
|
|
706
|
+
"Screen user input for unsafe content before the model runs. Applies only when the resolved provider supports moderation."
|
|
707
|
+
) })
|
|
708
|
+
] })
|
|
709
|
+
] }),
|
|
710
|
+
moderation ? /* @__PURE__ */ jsx(
|
|
711
|
+
StatusBadge,
|
|
712
|
+
{
|
|
713
|
+
variant: moderation.effective === "off" ? "neutral" : "info",
|
|
714
|
+
dot: true,
|
|
715
|
+
"data-ai-agent-moderation-effective": true,
|
|
716
|
+
children: moderation.effective === "enforced" ? t("ai_assistant.agents.moderation.effectiveEnforced", "Enforced") : moderation.effective === "on" ? t("ai_assistant.agents.moderation.effectiveOn", "On") : t("ai_assistant.agents.moderation.effectiveOff", "Off")
|
|
717
|
+
}
|
|
718
|
+
) : null
|
|
719
|
+
] }),
|
|
720
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: enforced ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", "data-ai-agent-moderation-enforced": true, children: [
|
|
721
|
+
/* @__PURE__ */ jsx(StatusBadge, { variant: "warning", dot: true, children: t("ai_assistant.agents.moderation.enforcedBadge", "Enforced") }),
|
|
722
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
|
|
723
|
+
"ai_assistant.agents.moderation.enforcedHint",
|
|
724
|
+
"This agent accepts untrusted input, so moderation is always on and cannot be disabled here."
|
|
725
|
+
) })
|
|
726
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "max-w-xs", children: [
|
|
727
|
+
/* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.moderation.controlLabel", "Moderation") }),
|
|
728
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsxs(
|
|
729
|
+
Select,
|
|
730
|
+
{
|
|
731
|
+
value: currentValue,
|
|
732
|
+
onValueChange: (value) => save(value),
|
|
733
|
+
disabled: isSaving || !agentResolution,
|
|
734
|
+
children: [
|
|
735
|
+
/* @__PURE__ */ jsx(SelectTrigger, { "aria-label": t("ai_assistant.agents.moderation.controlLabel", "Moderation"), children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
736
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
737
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "inherit", children: t("ai_assistant.agents.moderation.inheritLabel", "Inherit") }),
|
|
738
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "on", children: t("ai_assistant.agents.moderation.onLabel", "On") }),
|
|
739
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "off", children: t("ai_assistant.agents.moderation.offLabel", "Off") })
|
|
740
|
+
] })
|
|
741
|
+
]
|
|
742
|
+
}
|
|
743
|
+
) })
|
|
744
|
+
] }) })
|
|
745
|
+
]
|
|
746
|
+
}
|
|
747
|
+
);
|
|
748
|
+
}
|
|
644
749
|
function AgentModelOverrideSection({ agent }) {
|
|
645
750
|
const t = useT();
|
|
646
751
|
const queryClient = useQueryClient();
|
|
@@ -921,7 +1026,7 @@ function AgentModelOverrideSection({ agent }) {
|
|
|
921
1026
|
"Loading provider catalog..."
|
|
922
1027
|
)
|
|
923
1028
|
}
|
|
924
|
-
) : settingsQuery.isError ? /* @__PURE__ */ jsxs(Alert, {
|
|
1029
|
+
) : settingsQuery.isError ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-model-override-load-error": true, children: [
|
|
925
1030
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
926
1031
|
"ai_assistant.agents.model_override.loadErrorTitle",
|
|
927
1032
|
"Failed to load provider catalog"
|
|
@@ -1099,8 +1204,8 @@ function AgentModelOverrideSection({ agent }) {
|
|
|
1099
1204
|
)
|
|
1100
1205
|
] })
|
|
1101
1206
|
] }) : null,
|
|
1102
|
-
state.kind === "success" ? /* @__PURE__ */ jsx(Alert, {
|
|
1103
|
-
state.kind === "error" ? /* @__PURE__ */ jsx(Alert, {
|
|
1207
|
+
state.kind === "success" ? /* @__PURE__ */ jsx(Alert, { status: "success", "data-ai-agent-model-override-state": "success", children: /* @__PURE__ */ jsx(AlertDescription, { children: state.message }) }) : null,
|
|
1208
|
+
state.kind === "error" ? /* @__PURE__ */ jsx(Alert, { status: "error", "data-ai-agent-model-override-state": "error", children: /* @__PURE__ */ jsx(AlertDescription, { children: state.message }) }) : null,
|
|
1104
1209
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
|
|
1105
1210
|
/* @__PURE__ */ jsxs(
|
|
1106
1211
|
Button,
|
|
@@ -1294,7 +1399,7 @@ function LoopPolicySection({ agent }) {
|
|
|
1294
1399
|
{
|
|
1295
1400
|
message: t("ai_assistant.agents.loop_policy.loading", "Loading loop policy...")
|
|
1296
1401
|
}
|
|
1297
|
-
) : query.isError ? /* @__PURE__ */ jsxs(Alert, {
|
|
1402
|
+
) : query.isError ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-loop-policy-load-error": true, children: [
|
|
1298
1403
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.loop_policy.loadErrorTitle", "Failed to load loop policy") }),
|
|
1299
1404
|
/* @__PURE__ */ jsx(AlertDescription, { children: query.error instanceof Error ? query.error.message : String(query.error) })
|
|
1300
1405
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1386,11 +1491,11 @@ function LoopPolicySection({ agent }) {
|
|
|
1386
1491
|
)
|
|
1387
1492
|
] })
|
|
1388
1493
|
] }),
|
|
1389
|
-
state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, {
|
|
1494
|
+
state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { status: "success", "data-ai-agent-loop-policy-state": "success", children: [
|
|
1390
1495
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.loop_policy.savedTitle", "Loop policy updated") }),
|
|
1391
1496
|
/* @__PURE__ */ jsx(AlertDescription, { children: state.message })
|
|
1392
1497
|
] }) : null,
|
|
1393
|
-
state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, {
|
|
1498
|
+
state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-loop-policy-state": "error", children: [
|
|
1394
1499
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
1395
1500
|
"ai_assistant.agents.loop_policy.errorTitle",
|
|
1396
1501
|
"Failed to update loop policy"
|
|
@@ -1639,6 +1744,7 @@ function AgentDetailPanel({ agent }) {
|
|
|
1639
1744
|
] })
|
|
1640
1745
|
] }),
|
|
1641
1746
|
/* @__PURE__ */ jsx(AgentModelOverrideSection, { agent }),
|
|
1747
|
+
/* @__PURE__ */ jsx(ModerationSection, { agent }),
|
|
1642
1748
|
/* @__PURE__ */ jsx(MutationPolicySection, { agent }),
|
|
1643
1749
|
/* @__PURE__ */ jsx(LoopPolicySection, { agent }),
|
|
1644
1750
|
/* @__PURE__ */ jsxs(
|
|
@@ -1673,7 +1779,7 @@ function AgentDetailPanel({ agent }) {
|
|
|
1673
1779
|
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
1674
1780
|
Alert,
|
|
1675
1781
|
{
|
|
1676
|
-
|
|
1782
|
+
status: "information",
|
|
1677
1783
|
icon: /* @__PURE__ */ jsx(Wand2, { "aria-hidden": "true" }),
|
|
1678
1784
|
"data-ai-agent-prompt-notice": true,
|
|
1679
1785
|
children: [
|
|
@@ -1685,11 +1791,11 @@ function AgentDetailPanel({ agent }) {
|
|
|
1685
1791
|
]
|
|
1686
1792
|
}
|
|
1687
1793
|
) }),
|
|
1688
|
-
saveState.kind === "success" ? /* @__PURE__ */ jsxs(Alert, {
|
|
1794
|
+
saveState.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { status: "success", className: "mt-3", "data-ai-agent-prompt-state": "success", children: [
|
|
1689
1795
|
/* @__PURE__ */ jsx(AlertTitle, { children: saveState.version > 0 ? t("ai_assistant.agents.override.savedTitle", "Prompt override saved") : t("ai_assistant.agents.prompt.pendingTitle", "Overrides accepted") }),
|
|
1690
1796
|
/* @__PURE__ */ jsx(AlertDescription, { children: saveState.version > 0 ? `${saveState.message} ${t("ai_assistant.agents.override.versionLabel", "Version")} ${saveState.version}.` : saveState.message })
|
|
1691
1797
|
] }) : null,
|
|
1692
|
-
saveState.kind === "error" ? /* @__PURE__ */ jsxs(Alert, {
|
|
1798
|
+
saveState.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { status: "error", className: "mt-3", "data-ai-agent-prompt-state": "error", children: [
|
|
1693
1799
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.override.errorTitle", "Failed to save prompt override") }),
|
|
1694
1800
|
/* @__PURE__ */ jsx(AlertDescription, { children: saveState.message })
|
|
1695
1801
|
] }) : null,
|
|
@@ -1777,7 +1883,7 @@ function AgentDetailPanel({ agent }) {
|
|
|
1777
1883
|
"Loading override history..."
|
|
1778
1884
|
)
|
|
1779
1885
|
}
|
|
1780
|
-
) : overrideQuery.isError ? /* @__PURE__ */ jsxs(Alert, {
|
|
1886
|
+
) : overrideQuery.isError ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-override-history-error": true, children: [
|
|
1781
1887
|
/* @__PURE__ */ jsx(AlertTitle, { children: t(
|
|
1782
1888
|
"ai_assistant.agents.override.history.errorTitle",
|
|
1783
1889
|
"Failed to load override history"
|
|
@@ -1868,7 +1974,7 @@ function AiAgentSettingsPageClient() {
|
|
|
1868
1974
|
);
|
|
1869
1975
|
}
|
|
1870
1976
|
if (isError) {
|
|
1871
|
-
return /* @__PURE__ */ jsxs(Alert, {
|
|
1977
|
+
return /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-agent-settings-error": true, children: [
|
|
1872
1978
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.loadErrorTitle", "Failed to load AI agents") }),
|
|
1873
1979
|
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
1874
1980
|
/* @__PURE__ */ jsx("span", { children: error instanceof Error ? error.message : String(error) }),
|