@open-mercato/ai-assistant 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7-develop.6869.1.d1ce9999ea

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.
@@ -73,7 +73,7 @@ function AiTenantAllowlistPageClient() {
73
73
  if (settingsQuery.isError || !settingsQuery.data) {
74
74
  return /* @__PURE__ */ jsxs("div", { className: "flex max-w-3xl flex-col gap-4", children: [
75
75
  pageHeader,
76
- /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
76
+ /* @__PURE__ */ jsxs(Alert, { status: "error", children: [
77
77
  /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.allowlist.loadError.title", "Failed to load allowlist") }),
78
78
  /* @__PURE__ */ jsx(AlertDescription, { children: settingsQuery.error instanceof Error ? settingsQuery.error.message : t("ai_assistant.allowlist.loadError.body", "Try refreshing the page.") })
79
79
  ] })
@@ -229,7 +229,7 @@ function AiTenantAllowlistPageClient() {
229
229
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 max-w-3xl", children: [
230
230
  pageHeader,
231
231
  envBanner,
232
- feedback ? /* @__PURE__ */ jsx(Alert, { variant: feedback.kind === "error" ? "destructive" : void 0, children: /* @__PURE__ */ jsx(AlertDescription, { children: feedback.text }) }) : null,
232
+ feedback ? /* @__PURE__ */ jsx(Alert, { status: feedback.kind === "error" ? "error" : void 0, children: /* @__PURE__ */ jsx(AlertDescription, { children: feedback.text }) }) : null,
233
233
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-6 space-y-6", children: [
234
234
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
235
235
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Loader2, Save, Shield, Trash2, ShieldCheck } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype EnvAllowlistConfig = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n}\n\ntype TenantAllowlist = {\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\ntype EffectiveAllowlist = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n tenantOverridesActive: boolean\n}\n\ntype ProviderEntry = {\n id: string\n name: string\n defaultModel: string\n envKey: string | null\n configured: boolean\n defaultModels: Array<{ id: string; name: string; contextWindow?: number; tags?: string[] }>\n}\n\ntype SettingsResponse = {\n availableProviders: ProviderEntry[]\n allowlistProviders?: ProviderEntry[]\n allowlist: EnvAllowlistConfig\n tenantAllowlist: TenantAllowlist | null\n effectiveAllowlist: EffectiveAllowlist\n}\n\nasync function fetchSettings(): Promise<SettingsResponse> {\n const { result, status } = await apiCallOrThrow<SettingsResponse>(\n '/api/ai_assistant/settings',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load AI settings' },\n )\n if (!result) throw new Error(`Failed to load settings (${status})`)\n return result\n}\n\ntype EditState = {\n /** null = \"no tenant restriction (inherit env)\"; array = explicit tenant pick */\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\nfunction snapshotToEditState(snapshot: TenantAllowlist | null): EditState {\n return {\n allowedProviders: snapshot?.allowedProviders ?? null,\n allowedModelsByProvider: { ...(snapshot?.allowedModelsByProvider ?? {}) },\n }\n}\n\nexport function AiTenantAllowlistPageClient(): React.JSX.Element {\n const t = useT()\n const queryClient = useQueryClient()\n const settingsQuery = useQuery({ queryKey: ['ai_assistant', 'settings'], queryFn: fetchSettings, staleTime: 0 })\n\n const [editState, setEditState] = React.useState<EditState>({\n allowedProviders: null,\n allowedModelsByProvider: {},\n })\n const [dirty, setDirty] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [clearing, setClearing] = React.useState(false)\n const [feedback, setFeedback] = React.useState<{ kind: 'ok' | 'error'; text: string } | null>(null)\n const { runMutation: runSaveAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-save',\n })\n const { runMutation: runClearAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-clear',\n })\n\n React.useEffect(() => {\n if (settingsQuery.data) {\n setEditState(snapshotToEditState(settingsQuery.data.tenantAllowlist))\n setDirty(false)\n }\n }, [settingsQuery.data])\n\n const pageHeader = (\n <div className=\"space-y-1\">\n <h1 className=\"flex items-center gap-2 text-2xl font-bold\">\n <Shield className=\"size-6\" />\n {t('ai_assistant.allowlist.title', 'AI provider & model allowlist')}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.subtitle',\n 'Limit which providers and models the runtime, settings, and chat picker may use for this tenant. The env allowlist is the outer constraint \u2014 tenant picks narrow it further.',\n )}\n </p>\n </div>\n )\n\n if (settingsQuery.isLoading) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <div className=\"flex w-fit items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground\" role=\"status\">\n <Loader2 className=\"size-4 animate-spin\" />\n {t('ai_assistant.allowlist.loading', 'Loading allowlist\u2026')}\n </div>\n </div>\n )\n }\n\n if (settingsQuery.isError || !settingsQuery.data) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <Alert variant=\"destructive\">\n <AlertTitle>{t('ai_assistant.allowlist.loadError.title', 'Failed to load allowlist')}</AlertTitle>\n <AlertDescription>\n {settingsQuery.error instanceof Error\n ? settingsQuery.error.message\n : t('ai_assistant.allowlist.loadError.body', 'Try refreshing the page.')}\n </AlertDescription>\n </Alert>\n </div>\n )\n }\n\n const settings = settingsQuery.data\n const envAllowedProviders = settings.allowlist.providers\n const envModelsByProvider = settings.allowlist.modelsByProvider\n\n // Provider universe to render: env-allowed providers (or all configured if env unset).\n const editableProviders = settings.allowlistProviders ?? settings.availableProviders\n const candidateProviders = editableProviders.filter((p) => {\n if (envAllowedProviders === null) return true\n return envAllowedProviders.some((id) => id.toLowerCase() === p.id.toLowerCase())\n })\n\n const tenantPickedProviders = editState.allowedProviders\n const isProviderEnabled = (id: string): boolean => {\n if (tenantPickedProviders === null) return true\n return tenantPickedProviders.includes(id)\n }\n\n const toggleProvider = (id: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n setEditState((prev) => {\n const current = prev.allowedProviders\n if (next) {\n const list = current === null ? [id] : Array.from(new Set([...current, id]))\n return { ...prev, allowedProviders: list }\n }\n const list = current === null\n ? candidateProviders.map((p) => p.id).filter((pid) => pid !== id)\n : current.filter((pid) => pid !== id)\n return { ...prev, allowedProviders: list }\n })\n }\n\n const isModelEnabled = (providerId: string, modelId: string): boolean => {\n const list = editState.allowedModelsByProvider[providerId]\n if (list === undefined) return true\n return list.includes(modelId)\n }\n\n const toggleModel = (providerId: string, modelId: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n const provider = candidateProviders.find((p) => p.id === providerId)\n const allModelIds = provider?.defaultModels.map((m) => m.id) ?? []\n setEditState((prev) => {\n const current = prev.allowedModelsByProvider[providerId]\n const allowedModelsByProvider = { ...prev.allowedModelsByProvider }\n if (next) {\n const list = current === undefined ? [modelId] : Array.from(new Set([...current, modelId]))\n allowedModelsByProvider[providerId] = list\n } else {\n const baseline = current === undefined ? allModelIds : current\n const list = baseline.filter((id) => id !== modelId)\n allowedModelsByProvider[providerId] = list\n }\n return { ...prev, allowedModelsByProvider }\n })\n }\n\n const resetTenantPicks = (): void => {\n setDirty(true)\n setFeedback(null)\n setEditState({ allowedProviders: null, allowedModelsByProvider: {} })\n }\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setFeedback(null)\n try {\n await runSaveAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; code?: string }>(\n '/api/ai_assistant/settings/allowlist',\n {\n method: 'PUT',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n allowedProviders: editState.allowedProviders,\n allowedModelsByProvider: editState.allowedModelsByProvider,\n }),\n },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.save.error', `Save failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t('ai_assistant.allowlist.save.success', 'Allowlist saved.')\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n }\n\n const handleClear = async (): Promise<void> => {\n setClearing(true)\n setFeedback(null)\n try {\n await runClearAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; cleared?: boolean }>(\n '/api/ai_assistant/settings/allowlist',\n { method: 'DELETE', credentials: 'include' },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.clear.error', `Clear failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t(\n 'ai_assistant.allowlist.clear.success',\n 'Tenant allowlist cleared. Env-only enforcement applies.',\n )\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setClearing(false)\n }\n }\n\n const envBanner = envAllowedProviders === null && Object.keys(envModelsByProvider).length === 0\n ? null\n : (\n <Alert>\n <AlertTitle>{t('ai_assistant.allowlist.envBanner.title', 'Env allowlist is in effect')}</AlertTitle>\n <AlertDescription className=\"space-y-1\">\n {envAllowedProviders ? (\n <div>\n {t('ai_assistant.allowlist.envBanner.providers', 'OM_AI_AVAILABLE_PROVIDERS')}: <code className=\"font-mono text-xs\">{envAllowedProviders.join(', ')}</code>\n </div>\n ) : null}\n {Object.keys(envModelsByProvider).map((pid) => (\n <div key={pid}>\n <code className=\"font-mono text-xs\">OM_AI_AVAILABLE_MODELS_{pid.toUpperCase()}</code>: {envModelsByProvider[pid].join(', ')}\n </div>\n ))}\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('ai_assistant.allowlist.envBanner.note', 'Tenant picks may not widen the env list \u2014 values outside it are hidden.')}\n </p>\n </AlertDescription>\n </Alert>\n )\n\n return (\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {pageHeader}\n\n {envBanner}\n\n {feedback ? (\n <Alert variant={feedback.kind === 'error' ? 'destructive' : undefined}>\n <AlertDescription>{feedback.text}</AlertDescription>\n </Alert>\n ) : null}\n\n <div className=\"rounded-lg border bg-card p-6 space-y-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('ai_assistant.allowlist.providers.title', 'Providers')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.providers.help',\n 'Untick to forbid the runtime from using a provider for this tenant. Tick all to inherit the env allowlist.',\n )}\n </p>\n </div>\n <span\n className={\n settings.effectiveAllowlist.tenantOverridesActive\n ? 'inline-flex size-8 items-center justify-center rounded-md text-status-success-icon'\n : 'inline-flex size-8 items-center justify-center rounded-md text-status-warning-icon'\n }\n role=\"img\"\n aria-label={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n title={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n >\n {settings.effectiveAllowlist.tenantOverridesActive ? (\n <ShieldCheck className=\"size-5\" aria-hidden />\n ) : (\n <Shield className=\"size-5\" aria-hidden />\n )}\n </span>\n </div>\n\n {candidateProviders.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.empty', 'No configured providers within the env allowlist.')}\n </p>\n ) : (\n <div className=\"space-y-4\">\n {candidateProviders.map((provider) => {\n const enabled = isProviderEnabled(provider.id)\n const envModels = envModelsByProvider[provider.id]\n const candidateModels = envModels\n ? provider.defaultModels.filter((m) => envModels.includes(m.id))\n : provider.defaultModels\n return (\n <div key={provider.id} className=\"rounded-md border p-4 space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id={`provider-${provider.id}`}\n checked={enabled}\n onCheckedChange={(value) => toggleProvider(provider.id, value === true)}\n />\n <Label htmlFor={`provider-${provider.id}`} className=\"font-medium\">\n {provider.name}\n </Label>\n {provider.configured ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.providers.configured', 'configured')}\n </Badge>\n ) : (\n <Badge variant=\"outline\" className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.notConfigured', 'not configured')}\n </Badge>\n )}\n </div>\n </div>\n\n {enabled && candidateModels.length > 0 ? (\n <div className=\"ml-7 space-y-2\">\n <div className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.models.help', 'Tick the models tenants may pick. Empty = no model restriction (inherit env).')}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {candidateModels.map((model) => {\n const checked = isModelEnabled(provider.id, model.id)\n return (\n <label\n key={`${provider.id}-${model.id}`}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Checkbox\n checked={checked}\n onCheckedChange={(value) => toggleModel(provider.id, model.id, value === true)}\n />\n <span className=\"font-mono text-xs\">{model.id}</span>\n {model.id === provider.defaultModel ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.models.default', 'default')}\n </Badge>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={() => void handleSave()} disabled={!dirty || saving} className=\"gap-2\">\n {saving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.save', 'Save allowlist')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={resetTenantPicks}\n disabled={saving || clearing}\n className=\"gap-2\"\n >\n {t('ai_assistant.allowlist.actions.reset', 'Reset to env defaults')}\n </Button>\n <Button\n variant=\"ghost\"\n onClick={() => void handleClear()}\n disabled={clearing || saving || !settings.tenantAllowlist}\n className=\"gap-2\"\n >\n {clearing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.clearStored', 'Clear stored allowlist')}\n </Button>\n </div>\n </div>\n )\n}\n\nexport default AiTenantAllowlistPageClient\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { Loader2, Save, Shield, Trash2, ShieldCheck } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\ntype EnvAllowlistConfig = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n}\n\ntype TenantAllowlist = {\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\ntype EffectiveAllowlist = {\n providers: string[] | null\n modelsByProvider: Record<string, string[]>\n hasRestrictions: boolean\n tenantOverridesActive: boolean\n}\n\ntype ProviderEntry = {\n id: string\n name: string\n defaultModel: string\n envKey: string | null\n configured: boolean\n defaultModels: Array<{ id: string; name: string; contextWindow?: number; tags?: string[] }>\n}\n\ntype SettingsResponse = {\n availableProviders: ProviderEntry[]\n allowlistProviders?: ProviderEntry[]\n allowlist: EnvAllowlistConfig\n tenantAllowlist: TenantAllowlist | null\n effectiveAllowlist: EffectiveAllowlist\n}\n\nasync function fetchSettings(): Promise<SettingsResponse> {\n const { result, status } = await apiCallOrThrow<SettingsResponse>(\n '/api/ai_assistant/settings',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load AI settings' },\n )\n if (!result) throw new Error(`Failed to load settings (${status})`)\n return result\n}\n\ntype EditState = {\n /** null = \"no tenant restriction (inherit env)\"; array = explicit tenant pick */\n allowedProviders: string[] | null\n allowedModelsByProvider: Record<string, string[]>\n}\n\nfunction snapshotToEditState(snapshot: TenantAllowlist | null): EditState {\n return {\n allowedProviders: snapshot?.allowedProviders ?? null,\n allowedModelsByProvider: { ...(snapshot?.allowedModelsByProvider ?? {}) },\n }\n}\n\nexport function AiTenantAllowlistPageClient(): React.JSX.Element {\n const t = useT()\n const queryClient = useQueryClient()\n const settingsQuery = useQuery({ queryKey: ['ai_assistant', 'settings'], queryFn: fetchSettings, staleTime: 0 })\n\n const [editState, setEditState] = React.useState<EditState>({\n allowedProviders: null,\n allowedModelsByProvider: {},\n })\n const [dirty, setDirty] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [clearing, setClearing] = React.useState(false)\n const [feedback, setFeedback] = React.useState<{ kind: 'ok' | 'error'; text: string } | null>(null)\n const { runMutation: runSaveAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-save',\n })\n const { runMutation: runClearAllowlistMutation } = useGuardedMutation({\n contextId: 'ai-tenant-allowlist-clear',\n })\n\n React.useEffect(() => {\n if (settingsQuery.data) {\n setEditState(snapshotToEditState(settingsQuery.data.tenantAllowlist))\n setDirty(false)\n }\n }, [settingsQuery.data])\n\n const pageHeader = (\n <div className=\"space-y-1\">\n <h1 className=\"flex items-center gap-2 text-2xl font-bold\">\n <Shield className=\"size-6\" />\n {t('ai_assistant.allowlist.title', 'AI provider & model allowlist')}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.subtitle',\n 'Limit which providers and models the runtime, settings, and chat picker may use for this tenant. The env allowlist is the outer constraint \u2014 tenant picks narrow it further.',\n )}\n </p>\n </div>\n )\n\n if (settingsQuery.isLoading) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <div className=\"flex w-fit items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm text-muted-foreground\" role=\"status\">\n <Loader2 className=\"size-4 animate-spin\" />\n {t('ai_assistant.allowlist.loading', 'Loading allowlist\u2026')}\n </div>\n </div>\n )\n }\n\n if (settingsQuery.isError || !settingsQuery.data) {\n return (\n <div className=\"flex max-w-3xl flex-col gap-4\">\n {pageHeader}\n <Alert status=\"error\">\n <AlertTitle>{t('ai_assistant.allowlist.loadError.title', 'Failed to load allowlist')}</AlertTitle>\n <AlertDescription>\n {settingsQuery.error instanceof Error\n ? settingsQuery.error.message\n : t('ai_assistant.allowlist.loadError.body', 'Try refreshing the page.')}\n </AlertDescription>\n </Alert>\n </div>\n )\n }\n\n const settings = settingsQuery.data\n const envAllowedProviders = settings.allowlist.providers\n const envModelsByProvider = settings.allowlist.modelsByProvider\n\n // Provider universe to render: env-allowed providers (or all configured if env unset).\n const editableProviders = settings.allowlistProviders ?? settings.availableProviders\n const candidateProviders = editableProviders.filter((p) => {\n if (envAllowedProviders === null) return true\n return envAllowedProviders.some((id) => id.toLowerCase() === p.id.toLowerCase())\n })\n\n const tenantPickedProviders = editState.allowedProviders\n const isProviderEnabled = (id: string): boolean => {\n if (tenantPickedProviders === null) return true\n return tenantPickedProviders.includes(id)\n }\n\n const toggleProvider = (id: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n setEditState((prev) => {\n const current = prev.allowedProviders\n if (next) {\n const list = current === null ? [id] : Array.from(new Set([...current, id]))\n return { ...prev, allowedProviders: list }\n }\n const list = current === null\n ? candidateProviders.map((p) => p.id).filter((pid) => pid !== id)\n : current.filter((pid) => pid !== id)\n return { ...prev, allowedProviders: list }\n })\n }\n\n const isModelEnabled = (providerId: string, modelId: string): boolean => {\n const list = editState.allowedModelsByProvider[providerId]\n if (list === undefined) return true\n return list.includes(modelId)\n }\n\n const toggleModel = (providerId: string, modelId: string, next: boolean): void => {\n setDirty(true)\n setFeedback(null)\n const provider = candidateProviders.find((p) => p.id === providerId)\n const allModelIds = provider?.defaultModels.map((m) => m.id) ?? []\n setEditState((prev) => {\n const current = prev.allowedModelsByProvider[providerId]\n const allowedModelsByProvider = { ...prev.allowedModelsByProvider }\n if (next) {\n const list = current === undefined ? [modelId] : Array.from(new Set([...current, modelId]))\n allowedModelsByProvider[providerId] = list\n } else {\n const baseline = current === undefined ? allModelIds : current\n const list = baseline.filter((id) => id !== modelId)\n allowedModelsByProvider[providerId] = list\n }\n return { ...prev, allowedModelsByProvider }\n })\n }\n\n const resetTenantPicks = (): void => {\n setDirty(true)\n setFeedback(null)\n setEditState({ allowedProviders: null, allowedModelsByProvider: {} })\n }\n\n const handleSave = async (): Promise<void> => {\n setSaving(true)\n setFeedback(null)\n try {\n await runSaveAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; code?: string }>(\n '/api/ai_assistant/settings/allowlist',\n {\n method: 'PUT',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n allowedProviders: editState.allowedProviders,\n allowedModelsByProvider: editState.allowedModelsByProvider,\n }),\n },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.save.error', `Save failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t('ai_assistant.allowlist.save.success', 'Allowlist saved.')\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n }\n\n const handleClear = async (): Promise<void> => {\n setClearing(true)\n setFeedback(null)\n try {\n await runClearAllowlistMutation({\n operation: async () => {\n const { ok, status, result } = await apiCall<{ error?: string; cleared?: boolean }>(\n '/api/ai_assistant/settings/allowlist',\n { method: 'DELETE', credentials: 'include' },\n )\n if (!ok) {\n throw new Error(\n result?.error ?? t('ai_assistant.allowlist.clear.error', `Clear failed (${status})`),\n )\n }\n },\n context: {},\n })\n const successText = t(\n 'ai_assistant.allowlist.clear.success',\n 'Tenant allowlist cleared. Env-only enforcement applies.',\n )\n setFeedback({ kind: 'ok', text: successText })\n flash(successText, 'success')\n setDirty(false)\n await queryClient.invalidateQueries({ queryKey: ['ai_assistant', 'settings'] })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n setFeedback({ kind: 'error', text: message })\n flash(message, 'error')\n } finally {\n setClearing(false)\n }\n }\n\n const envBanner = envAllowedProviders === null && Object.keys(envModelsByProvider).length === 0\n ? null\n : (\n <Alert>\n <AlertTitle>{t('ai_assistant.allowlist.envBanner.title', 'Env allowlist is in effect')}</AlertTitle>\n <AlertDescription className=\"space-y-1\">\n {envAllowedProviders ? (\n <div>\n {t('ai_assistant.allowlist.envBanner.providers', 'OM_AI_AVAILABLE_PROVIDERS')}: <code className=\"font-mono text-xs\">{envAllowedProviders.join(', ')}</code>\n </div>\n ) : null}\n {Object.keys(envModelsByProvider).map((pid) => (\n <div key={pid}>\n <code className=\"font-mono text-xs\">OM_AI_AVAILABLE_MODELS_{pid.toUpperCase()}</code>: {envModelsByProvider[pid].join(', ')}\n </div>\n ))}\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('ai_assistant.allowlist.envBanner.note', 'Tenant picks may not widen the env list \u2014 values outside it are hidden.')}\n </p>\n </AlertDescription>\n </Alert>\n )\n\n return (\n <div className=\"flex flex-col gap-6 max-w-3xl\">\n {pageHeader}\n\n {envBanner}\n\n {feedback ? (\n <Alert status={feedback.kind === 'error' ? 'error' : undefined}>\n <AlertDescription>{feedback.text}</AlertDescription>\n </Alert>\n ) : null}\n\n <div className=\"rounded-lg border bg-card p-6 space-y-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('ai_assistant.allowlist.providers.title', 'Providers')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.allowlist.providers.help',\n 'Untick to forbid the runtime from using a provider for this tenant. Tick all to inherit the env allowlist.',\n )}\n </p>\n </div>\n <span\n className={\n settings.effectiveAllowlist.tenantOverridesActive\n ? 'inline-flex size-8 items-center justify-center rounded-md text-status-success-icon'\n : 'inline-flex size-8 items-center justify-center rounded-md text-status-warning-icon'\n }\n role=\"img\"\n aria-label={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n title={\n settings.effectiveAllowlist.tenantOverridesActive\n ? t('ai_assistant.allowlist.badge.active', 'Tenant rules active')\n : t('ai_assistant.allowlist.badge.envOnly', 'Env-only')\n }\n >\n {settings.effectiveAllowlist.tenantOverridesActive ? (\n <ShieldCheck className=\"size-5\" aria-hidden />\n ) : (\n <Shield className=\"size-5\" aria-hidden />\n )}\n </span>\n </div>\n\n {candidateProviders.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.empty', 'No configured providers within the env allowlist.')}\n </p>\n ) : (\n <div className=\"space-y-4\">\n {candidateProviders.map((provider) => {\n const enabled = isProviderEnabled(provider.id)\n const envModels = envModelsByProvider[provider.id]\n const candidateModels = envModels\n ? provider.defaultModels.filter((m) => envModels.includes(m.id))\n : provider.defaultModels\n return (\n <div key={provider.id} className=\"rounded-md border p-4 space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <Checkbox\n id={`provider-${provider.id}`}\n checked={enabled}\n onCheckedChange={(value) => toggleProvider(provider.id, value === true)}\n />\n <Label htmlFor={`provider-${provider.id}`} className=\"font-medium\">\n {provider.name}\n </Label>\n {provider.configured ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.providers.configured', 'configured')}\n </Badge>\n ) : (\n <Badge variant=\"outline\" className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.providers.notConfigured', 'not configured')}\n </Badge>\n )}\n </div>\n </div>\n\n {enabled && candidateModels.length > 0 ? (\n <div className=\"ml-7 space-y-2\">\n <div className=\"text-xs text-muted-foreground\">\n {t('ai_assistant.allowlist.models.help', 'Tick the models tenants may pick. Empty = no model restriction (inherit env).')}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {candidateModels.map((model) => {\n const checked = isModelEnabled(provider.id, model.id)\n return (\n <label\n key={`${provider.id}-${model.id}`}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Checkbox\n checked={checked}\n onCheckedChange={(value) => toggleModel(provider.id, model.id, value === true)}\n />\n <span className=\"font-mono text-xs\">{model.id}</span>\n {model.id === provider.defaultModel ? (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('ai_assistant.allowlist.models.default', 'default')}\n </Badge>\n ) : null}\n </label>\n )\n })}\n </div>\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button onClick={() => void handleSave()} disabled={!dirty || saving} className=\"gap-2\">\n {saving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.save', 'Save allowlist')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={resetTenantPicks}\n disabled={saving || clearing}\n className=\"gap-2\"\n >\n {t('ai_assistant.allowlist.actions.reset', 'Reset to env defaults')}\n </Button>\n <Button\n variant=\"ghost\"\n onClick={() => void handleClear()}\n disabled={clearing || saving || !settings.tenantAllowlist}\n className=\"gap-2\"\n >\n {clearing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n {t('ai_assistant.allowlist.actions.clearStored', 'Clear stored allowlist')}\n </Button>\n </div>\n </div>\n )\n}\n\nexport default AiTenantAllowlistPageClient\n"],
5
+ "mappings": "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",
6
6
  "names": ["list"]
7
7
  }
@@ -203,7 +203,7 @@ function ChatLane({ agent, debug }) {
203
203
  if (seeds.length > 0) setUiParts(seeds);
204
204
  }, []);
205
205
  if (agent.executionMode !== "chat") {
206
- return /* @__PURE__ */ jsxs(Alert, { variant: "info", "data-ai-playground-unsupported": "chat", children: [
206
+ return /* @__PURE__ */ jsxs(Alert, { status: "information", "data-ai-playground-unsupported": "chat", children: [
207
207
  /* @__PURE__ */ jsx(AlertTitle, { children: t(
208
208
  "ai_assistant.playground.chat.notSupportedTitle",
209
209
  "Chat mode is not available for this agent."
@@ -285,7 +285,7 @@ function ObjectLane({ agent }) {
285
285
  }
286
286
  });
287
287
  if (!isSupported) {
288
- return /* @__PURE__ */ jsxs(Alert, { variant: "info", "data-ai-playground-unsupported": "object", children: [
288
+ return /* @__PURE__ */ jsxs(Alert, { status: "information", "data-ai-playground-unsupported": "object", children: [
289
289
  /* @__PURE__ */ jsx(AlertTitle, { children: t(
290
290
  "ai_assistant.playground.object.notSupportedTitle",
291
291
  "Object mode is not available for this agent."
@@ -336,7 +336,7 @@ function ObjectLane({ agent }) {
336
336
  )
337
337
  ] })
338
338
  ] }),
339
- error ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-playground-object-error": error.code ?? "unknown", children: [
339
+ error ? /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-playground-object-error": error.code ?? "unknown", children: [
340
340
  /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.playground.object.errorTitle", "Object run failed") }),
341
341
  /* @__PURE__ */ jsxs(AlertDescription, { children: [
342
342
  error.code ? /* @__PURE__ */ jsx("span", { className: "mr-2 font-mono text-xs", children: error.code }) : null,
@@ -413,7 +413,7 @@ function AiPlaygroundPageClient() {
413
413
  );
414
414
  }
415
415
  if (isError) {
416
- return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-playground-error": true, children: [
416
+ return /* @__PURE__ */ jsxs(Alert, { status: "error", "data-ai-playground-error": true, children: [
417
417
  /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.playground.loadErrorTitle", "Failed to load AI agents") }),
418
418
  /* @__PURE__ */ jsxs(AlertDescription, { children: [
419
419
  /* @__PURE__ */ jsx("span", { children: error instanceof Error ? error.message : String(error) }),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { Bot, BookOpen, Loader2, Play, RefreshCcw } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@open-mercato/ui/primitives/tabs'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { AiChat, createAiUiPartRegistry, LoopDisabledBanner, useAiShortcuts } from '@open-mercato/ui/ai'\nimport type { AiChatDebugPromptSection, AiChatDebugTool } from '@open-mercato/ui/ai'\nimport { ConversationShareButton } from '../../../../components/ConversationShareButton'\n\ntype PlaygroundAgentTool = {\n name: string\n displayName?: string\n isMutation?: boolean\n registered?: boolean\n requiredFeatures?: string[]\n}\n\ntype PlaygroundAgent = {\n id: string\n moduleId: string\n label: string\n description: string\n executionMode: 'chat' | 'object'\n mutationPolicy: string\n allowedTools: string[]\n requiredFeatures: string[]\n acceptedMediaTypes: string[]\n hasOutputSchema: boolean\n systemPrompt?: string\n readOnly?: boolean\n maxSteps?: number | null\n tools?: PlaygroundAgentTool[]\n}\n\ntype AgentsResponse = {\n agents: PlaygroundAgent[]\n total: number\n}\n\ntype RunObjectResponse = {\n object: unknown\n finishReason?: string\n usage?: { inputTokens?: number; outputTokens?: number }\n}\n\ntype RunObjectError = {\n error: string\n code?: string\n issues?: unknown\n}\n\nasync function fetchAgents(): Promise<AgentsResponse> {\n const { result, status } = await apiCallOrThrow<AgentsResponse>(\n '/api/ai_assistant/ai/agents',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load agents' },\n )\n if (!result) throw new Error(`Failed to load agents (${status})`)\n return result\n}\n\nfunction PlaygroundLoading({ message }: { message: string }) {\n return (\n <div\n className=\"flex items-center gap-2 rounded-lg border border-border bg-background p-4 text-sm text-muted-foreground\"\n role=\"status\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{message}</span>\n </div>\n )\n}\n\nfunction PlaygroundNoAgents() {\n const t = useT()\n return (\n <EmptyState\n icon={<Bot className=\"size-6\" aria-hidden />}\n title={t(\n 'ai_assistant.playground.empty.title',\n 'No AI agents are registered for your role yet.',\n )}\n description={t(\n 'ai_assistant.playground.empty.description',\n 'Declare agents inside `packages/<module>/src/modules/<module>/ai-agents.ts`, run `yarn generate`, and ensure the caller holds the agent\\'s required features.',\n )}\n >\n <div className=\"mt-2 inline-flex items-center gap-2 text-xs text-muted-foreground\">\n <BookOpen className=\"size-3\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.playground.empty.docLabel',\n 'See packages/ai-assistant/AGENTS.md for the agent definition reference.',\n )}\n </span>\n </div>\n </EmptyState>\n )\n}\n\nfunction AgentDetails({ agent }: { agent: PlaygroundAgent }) {\n const t = useT()\n return (\n <div className=\"rounded-md border border-border bg-muted/30 p-3 text-sm\">\n <div className=\"font-semibold\">{agent.label}</div>\n <p className=\"mt-1 text-xs text-muted-foreground\">{agent.description}</p>\n <dl className=\"mt-3 grid grid-cols-2 gap-2 text-xs\">\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.module', 'Module')}\n </dt>\n <dd className=\"font-mono\">{agent.moduleId}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.executionMode', 'Execution mode')}\n </dt>\n <dd className=\"font-mono\">{agent.executionMode}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.mutationPolicy', 'Mutation policy')}\n </dt>\n <dd className=\"font-mono\">{agent.mutationPolicy}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.tools', 'Allowed tools')}\n </dt>\n <dd className=\"font-mono\">{agent.allowedTools.length}</dd>\n </div>\n </dl>\n </div>\n )\n}\n\nfunction buildDebugTools(agent: PlaygroundAgent): AiChatDebugTool[] {\n if (agent.tools && agent.tools.length > 0) {\n return agent.tools.map((tool) => ({\n name: tool.name,\n displayName: tool.displayName ?? tool.name,\n isMutation: Boolean(tool.isMutation),\n requiredFeatures: tool.requiredFeatures ?? [],\n }))\n }\n return agent.allowedTools.map((toolName) => ({ name: toolName }))\n}\n\nfunction buildDebugPromptSections(agent: PlaygroundAgent): AiChatDebugPromptSection[] {\n const sections: AiChatDebugPromptSection[] = []\n if (agent.systemPrompt) {\n sections.push({ id: 'role', source: 'default', text: agent.systemPrompt })\n }\n const placeholderIds = [\n 'scope',\n 'data',\n 'tools',\n 'attachments',\n 'mutationPolicy',\n 'responseStyle',\n 'overrides',\n ] as const\n for (const id of placeholderIds) {\n sections.push({ id, source: 'placeholder' })\n }\n return sections\n}\n\ntype AgentModelResolution = {\n agentId: string\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n}\n\ntype SettingsAgentResolutionResponse = {\n agents: AgentModelResolution[]\n}\n\nasync function fetchAgentResolutions(): Promise<SettingsAgentResolutionResponse> {\n const result = await apiCall<SettingsAgentResolutionResponse>('/api/ai_assistant/settings')\n if (!result.ok || !result.result) return { agents: [] }\n return { agents: result.result.agents ?? [] }\n}\n\nasync function fetchLoopOverrideForAgent(\n agentId: string,\n): Promise<{ agentId: string; override: { loopDisabled?: boolean | null } | null }> {\n const result = await apiCall<{ agentId: string; override: { loopDisabled?: boolean | null } | null }>(\n `/api/ai_assistant/ai/agents/${encodeURIComponent(agentId)}/loop-override`,\n { method: 'GET', credentials: 'include' },\n )\n if (!result.ok || !result.result) return { agentId, override: null }\n return result.result\n}\n\nfunction LoopDisabledPlaygroundBanner({ agentId }: { agentId: string }) {\n const { data } = useQuery({\n queryKey: ['ai_assistant', 'loop_override', agentId],\n queryFn: () => fetchLoopOverrideForAgent(agentId),\n staleTime: 30000,\n })\n if (!data?.override?.loopDisabled) return null\n return <LoopDisabledBanner agentId={agentId} />\n}\n\nfunction ModelResolutionPanel({ agentId }: { agentId: string }) {\n const t = useT()\n const { data } = useQuery({\n queryKey: ['ai_assistant', 'settings', 'agents'],\n queryFn: fetchAgentResolutions,\n staleTime: 30000,\n })\n\n const resolution = data?.agents.find((agent) => agent.agentId === agentId)\n if (!resolution) return null\n\n return (\n <dl\n className=\"grid grid-cols-2 gap-x-4 gap-y-1 rounded-md border border-border bg-muted/30 p-3 text-xs sm:grid-cols-4\"\n data-ai-playground-model-resolution={agentId}\n >\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.provider', 'Provider')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-provider>\n {resolution.providerId}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.model', 'Model')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-model>\n {resolution.modelId}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.baseUrl', 'Base URL')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-base-url>\n {resolution.baseURL ?? t('ai_assistant.playground.resolution.none', '\u2014')}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.source', 'Source')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-source>\n {resolution.source}\n </dd>\n </div>\n </dl>\n )\n}\n\ntype PlaygroundUiPartSeed = {\n componentId: string\n pendingActionId?: string\n payload?: unknown\n}\n\nfunction readPlaygroundUiPartSeeds(): PlaygroundUiPartSeed[] {\n if (typeof window === 'undefined') return []\n try {\n const params = new URLSearchParams(window.location.search)\n const componentId = params.get('uiPart')\n if (!componentId) return []\n const pendingActionId = params.get('pendingActionId') ?? undefined\n return [{ componentId, pendingActionId }]\n } catch {\n return []\n }\n}\n\nfunction ChatLane({ agent, debug }: { agent: PlaygroundAgent; debug: boolean }) {\n const t = useT()\n // Scoped registry so repeated mounts do not share state with other pages.\n // Step 5.10: opt in to the LIVE mutation-approval cards so the playground\n // exercises the real cards when the chat response surfaces a pending\n // action (via the `?uiPart=...` debug seed for Playwright).\n const registry = React.useMemo(\n () => createAiUiPartRegistry({ seedLiveApprovalCards: true }),\n [],\n )\n const debugTools = React.useMemo(() => buildDebugTools(agent), [agent])\n const debugPromptSections = React.useMemo(\n () => buildDebugPromptSections(agent),\n [agent],\n )\n const [uiParts, setUiParts] = React.useState<PlaygroundUiPartSeed[]>([])\n const [conversationId, setConversationId] = React.useState<string | null>(null)\n\n // Step 5.10: the dispatcher does not yet surface `AiUiPart` entries through\n // the plain-text stream consumed by `useAiChat`. For now the playground\n // reads a `?uiPart=<componentId>&pendingActionId=...` seed from the URL\n // so Playwright + operator debug flows can render the approval cards\n // against a stubbed `/api/ai_assistant/ai/actions/:id` endpoint. When the\n // dispatcher switches to the UIMessageChunk format this effect swaps over\n // to the streamed `uiParts` payload.\n React.useEffect(() => {\n const seeds = readPlaygroundUiPartSeeds()\n if (seeds.length > 0) setUiParts(seeds)\n }, [])\n\n if (agent.executionMode !== 'chat') {\n return (\n <Alert variant=\"info\" data-ai-playground-unsupported=\"chat\">\n <AlertTitle>\n {t(\n 'ai_assistant.playground.chat.notSupportedTitle',\n 'Chat mode is not available for this agent.',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.playground.chat.notSupportedBody',\n 'Pick an agent whose execution mode is \"chat\", or switch to the object-mode tab.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-2\" data-ai-playground-chat={agent.id}>\n <AiChat\n key={agent.id}\n agent={agent.id}\n pageContext={{ source: 'playground', pageId: 'ai_assistant.playground' }}\n debug={debug}\n registry={registry}\n className=\"min-h-96\"\n debugTools={debugTools}\n debugPromptSections={debugPromptSections}\n uiParts={uiParts}\n onConversationIdChange={setConversationId}\n headerActions={conversationId ? <ConversationShareButton conversationId={conversationId} /> : null}\n />\n </div>\n )\n}\n\nfunction ObjectLane({ agent }: { agent: PlaygroundAgent }) {\n const t = useT()\n const [prompt, setPrompt] = React.useState('')\n const [isRunning, setIsRunning] = React.useState(false)\n const [result, setResult] = React.useState<RunObjectResponse | null>(null)\n const [error, setError] = React.useState<RunObjectError | null>(null)\n const [lastRequest, setLastRequest] = React.useState<unknown>(null)\n\n const isSupported = agent.executionMode === 'object'\n const canRun = isSupported && prompt.trim().length > 0 && !isRunning\n\n const runObject = React.useCallback(async () => {\n if (!canRun) return\n const body = {\n agent: agent.id,\n messages: [{ role: 'user' as const, content: prompt }],\n pageContext: { source: 'playground', pageId: 'ai_assistant.playground' },\n }\n setLastRequest(body)\n setIsRunning(true)\n setResult(null)\n setError(null)\n try {\n const { ok, status, result } = await apiCall<RunObjectResponse | RunObjectError>(\n '/api/ai_assistant/ai/run-object',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n },\n )\n if (!ok) {\n const payload = (result as RunObjectError | null) ?? { error: `HTTP ${status}` }\n setError(payload)\n return\n }\n setResult((result as RunObjectResponse | null) ?? { object: null })\n } catch (err) {\n setError({\n error: err instanceof Error ? err.message : String(err),\n code: 'network_error',\n })\n } finally {\n setIsRunning(false)\n }\n }, [agent.id, canRun, prompt])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: () => {\n void runObject()\n },\n onCancel: () => {\n setError(null)\n },\n })\n\n if (!isSupported) {\n return (\n <Alert variant=\"info\" data-ai-playground-unsupported=\"object\">\n <AlertTitle>\n {t(\n 'ai_assistant.playground.object.notSupportedTitle',\n 'Object mode is not available for this agent.',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.playground.object.notSupportedBody',\n 'This agent declares executionMode = \"chat\". Pick an object-mode agent to preview structured output, or switch to the chat tab.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-3\" data-ai-playground-object>\n <div className=\"flex flex-col gap-2\">\n <Label htmlFor=\"ai-playground-object-input\">\n {t('ai_assistant.playground.object.inputLabel', 'Prompt')}\n </Label>\n <Textarea\n id=\"ai-playground-object-input\"\n rows={4}\n value={prompt}\n placeholder={t(\n 'ai_assistant.playground.object.inputPlaceholder',\n 'Describe what the agent should produce...',\n )}\n onChange={(event) => setPrompt(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"resize-none\"\n aria-label={t('ai_assistant.playground.object.inputLabel', 'Prompt')}\n />\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'ai_assistant.playground.object.shortcutHint',\n 'Press Cmd/Ctrl+Enter to run.',\n )}\n </p>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void runObject()}\n disabled={!canRun}\n data-ai-playground-object-run\n >\n {isRunning ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : (\n <Play className=\"size-4\" aria-hidden />\n )}\n <span>{t('ai_assistant.playground.object.run', 'Run object')}</span>\n </Button>\n </div>\n </div>\n\n {error ? (\n <Alert variant=\"destructive\" data-ai-playground-object-error={error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.playground.object.errorTitle', 'Object run failed')}\n </AlertTitle>\n <AlertDescription>\n {error.code ? <span className=\"mr-2 font-mono text-xs\">{error.code}</span> : null}\n {error.error}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {result ? (\n <section\n className=\"rounded-md border border-border bg-muted/30 p-3\"\n data-ai-playground-object-result\n >\n <h3 className=\"text-sm font-semibold\">\n {t('ai_assistant.playground.object.resultTitle', 'Generated object')}\n </h3>\n <pre className=\"mt-2 max-h-96 overflow-auto rounded bg-background p-2 text-xs font-mono\">\n {JSON.stringify(result.object, null, 2)}\n </pre>\n {result.usage || result.finishReason ? (\n <dl className=\"mt-3 grid grid-cols-3 gap-2 text-xs\">\n {result.finishReason ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.finishReason', 'Finish reason')}\n </dt>\n <dd className=\"font-mono\">{result.finishReason}</dd>\n </div>\n ) : null}\n {result.usage?.inputTokens !== undefined ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.inputTokens', 'Input tokens')}\n </dt>\n <dd className=\"font-mono\">{result.usage.inputTokens}</dd>\n </div>\n ) : null}\n {result.usage?.outputTokens !== undefined ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.outputTokens', 'Output tokens')}\n </dt>\n <dd className=\"font-mono\">{result.usage.outputTokens}</dd>\n </div>\n ) : null}\n </dl>\n ) : null}\n </section>\n ) : null}\n\n {lastRequest && (error || result) ? (\n <details\n className=\"rounded-md border border-border bg-muted/20 p-2 text-xs\"\n data-ai-playground-object-debug\n >\n <summary className=\"cursor-pointer font-semibold\">\n {t('ai_assistant.playground.object.debugTitle', 'Last request payload')}\n </summary>\n <pre className=\"mt-2 max-h-64 overflow-auto whitespace-pre-wrap font-mono\">\n {JSON.stringify(lastRequest, null, 2)}\n </pre>\n </details>\n ) : null}\n </div>\n )\n}\n\nexport function AiPlaygroundPageClient() {\n const t = useT()\n const [selectedAgentId, setSelectedAgentId] = React.useState<string | null>(null)\n const [debugEnabled, setDebugEnabled] = React.useState(false)\n const [tab, setTab] = React.useState<'chat' | 'object'>('chat')\n\n const { data, isLoading, isError, error, refetch, isFetching } = useQuery<AgentsResponse>({\n queryKey: ['ai_assistant', 'playground', 'agents'],\n queryFn: fetchAgents,\n })\n\n const agents = React.useMemo<PlaygroundAgent[]>(() => data?.agents ?? [], [data])\n\n React.useEffect(() => {\n if (!agents.length) {\n if (selectedAgentId !== null) setSelectedAgentId(null)\n return\n }\n if (!selectedAgentId || !agents.some((agent) => agent.id === selectedAgentId)) {\n setSelectedAgentId(agents[0].id)\n }\n }, [agents, selectedAgentId])\n\n const selectedAgent = React.useMemo<PlaygroundAgent | null>(() => {\n if (!selectedAgentId) return null\n return agents.find((agent) => agent.id === selectedAgentId) ?? null\n }, [agents, selectedAgentId])\n\n if (isLoading) {\n return (\n <PlaygroundLoading\n message={t('ai_assistant.playground.loadingAgents', 'Loading AI agents...')}\n />\n )\n }\n\n if (isError) {\n return (\n <Alert variant=\"destructive\" data-ai-playground-error>\n <AlertTitle>\n {t('ai_assistant.playground.loadErrorTitle', 'Failed to load AI agents')}\n </AlertTitle>\n <AlertDescription>\n <span>{error instanceof Error ? error.message : String(error)}</span>\n <div className=\"mt-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n void refetch()\n }}\n >\n <RefreshCcw className=\"size-4\" aria-hidden />\n <span>{t('ai_assistant.playground.retry', 'Retry')}</span>\n </Button>\n </div>\n </AlertDescription>\n </Alert>\n )\n }\n\n if (!agents.length) {\n return <PlaygroundNoAgents />\n }\n\n return (\n <div className=\"flex flex-col gap-4\" data-ai-playground>\n <header className=\"flex flex-col gap-1\">\n <h1 className=\"text-2xl font-bold tracking-tight\">\n {t('ai_assistant.playground.title', 'AI Playground')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.playground.subtitle',\n 'Exercise every registered AI agent end-to-end. Use the debug panel to inspect request and response payloads, and the object-mode tab to preview structured output.',\n )}\n </p>\n </header>\n\n <section className=\"flex flex-col gap-3 rounded-lg border border-border bg-background p-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between\">\n <div className=\"flex flex-col gap-2 sm:flex-1\">\n <Label htmlFor=\"ai-playground-agent-picker\">\n {t('ai_assistant.playground.agentPickerLabel', 'Agent')}\n </Label>\n <select\n id=\"ai-playground-agent-picker\"\n data-ai-playground-agent-picker\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm\"\n value={selectedAgentId ?? ''}\n onChange={(event) => setSelectedAgentId(event.target.value)}\n >\n {agents.map((agent) => (\n <option key={agent.id} value={agent.id}>\n {agent.label} ({agent.id})\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-3 sm:flex-shrink-0\">\n <Label htmlFor=\"ai-playground-debug\" className=\"text-sm\">\n {t('ai_assistant.playground.debugToggle', 'Debug panel')}\n </Label>\n <Switch\n id=\"ai-playground-debug\"\n checked={debugEnabled}\n onCheckedChange={(next: boolean) => setDebugEnabled(next)}\n aria-label={t('ai_assistant.playground.debugToggle', 'Debug panel')}\n data-ai-playground-debug-toggle\n />\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n void refetch()\n }}\n aria-label={t('ai_assistant.playground.refresh', 'Refresh agents')}\n disabled={isFetching}\n >\n <RefreshCcw className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n </div>\n {selectedAgent ? <AgentDetails agent={selectedAgent} /> : null}\n {selectedAgent ? <ModelResolutionPanel agentId={selectedAgent.id} /> : null}\n {selectedAgent ? <LoopDisabledPlaygroundBanner agentId={selectedAgent.id} /> : null}\n </section>\n\n {selectedAgent ? (\n <Tabs\n value={tab}\n onValueChange={(next: string) => setTab(next === 'object' ? 'object' : 'chat')}\n >\n <TabsList>\n <TabsTrigger value=\"chat\">\n {t('ai_assistant.playground.tabs.chat', 'Chat')}\n </TabsTrigger>\n <TabsTrigger value=\"object\">\n {t('ai_assistant.playground.tabs.object', 'Object mode')}\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"chat\">\n <ChatLane agent={selectedAgent} debug={debugEnabled} />\n </TabsContent>\n <TabsContent value=\"object\">\n <ObjectLane agent={selectedAgent} />\n </TabsContent>\n </Tabs>\n ) : null}\n </div>\n )\n}\n\nexport default AiPlaygroundPageClient\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { Bot, BookOpen, Loader2, Play, RefreshCcw } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@open-mercato/ui/primitives/tabs'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { AiChat, createAiUiPartRegistry, LoopDisabledBanner, useAiShortcuts } from '@open-mercato/ui/ai'\nimport type { AiChatDebugPromptSection, AiChatDebugTool } from '@open-mercato/ui/ai'\nimport { ConversationShareButton } from '../../../../components/ConversationShareButton'\n\ntype PlaygroundAgentTool = {\n name: string\n displayName?: string\n isMutation?: boolean\n registered?: boolean\n requiredFeatures?: string[]\n}\n\ntype PlaygroundAgent = {\n id: string\n moduleId: string\n label: string\n description: string\n executionMode: 'chat' | 'object'\n mutationPolicy: string\n allowedTools: string[]\n requiredFeatures: string[]\n acceptedMediaTypes: string[]\n hasOutputSchema: boolean\n systemPrompt?: string\n readOnly?: boolean\n maxSteps?: number | null\n tools?: PlaygroundAgentTool[]\n}\n\ntype AgentsResponse = {\n agents: PlaygroundAgent[]\n total: number\n}\n\ntype RunObjectResponse = {\n object: unknown\n finishReason?: string\n usage?: { inputTokens?: number; outputTokens?: number }\n}\n\ntype RunObjectError = {\n error: string\n code?: string\n issues?: unknown\n}\n\nasync function fetchAgents(): Promise<AgentsResponse> {\n const { result, status } = await apiCallOrThrow<AgentsResponse>(\n '/api/ai_assistant/ai/agents',\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load agents' },\n )\n if (!result) throw new Error(`Failed to load agents (${status})`)\n return result\n}\n\nfunction PlaygroundLoading({ message }: { message: string }) {\n return (\n <div\n className=\"flex items-center gap-2 rounded-lg border border-border bg-background p-4 text-sm text-muted-foreground\"\n role=\"status\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{message}</span>\n </div>\n )\n}\n\nfunction PlaygroundNoAgents() {\n const t = useT()\n return (\n <EmptyState\n icon={<Bot className=\"size-6\" aria-hidden />}\n title={t(\n 'ai_assistant.playground.empty.title',\n 'No AI agents are registered for your role yet.',\n )}\n description={t(\n 'ai_assistant.playground.empty.description',\n 'Declare agents inside `packages/<module>/src/modules/<module>/ai-agents.ts`, run `yarn generate`, and ensure the caller holds the agent\\'s required features.',\n )}\n >\n <div className=\"mt-2 inline-flex items-center gap-2 text-xs text-muted-foreground\">\n <BookOpen className=\"size-3\" aria-hidden />\n <span>\n {t(\n 'ai_assistant.playground.empty.docLabel',\n 'See packages/ai-assistant/AGENTS.md for the agent definition reference.',\n )}\n </span>\n </div>\n </EmptyState>\n )\n}\n\nfunction AgentDetails({ agent }: { agent: PlaygroundAgent }) {\n const t = useT()\n return (\n <div className=\"rounded-md border border-border bg-muted/30 p-3 text-sm\">\n <div className=\"font-semibold\">{agent.label}</div>\n <p className=\"mt-1 text-xs text-muted-foreground\">{agent.description}</p>\n <dl className=\"mt-3 grid grid-cols-2 gap-2 text-xs\">\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.module', 'Module')}\n </dt>\n <dd className=\"font-mono\">{agent.moduleId}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.executionMode', 'Execution mode')}\n </dt>\n <dd className=\"font-mono\">{agent.executionMode}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.mutationPolicy', 'Mutation policy')}\n </dt>\n <dd className=\"font-mono\">{agent.mutationPolicy}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.meta.tools', 'Allowed tools')}\n </dt>\n <dd className=\"font-mono\">{agent.allowedTools.length}</dd>\n </div>\n </dl>\n </div>\n )\n}\n\nfunction buildDebugTools(agent: PlaygroundAgent): AiChatDebugTool[] {\n if (agent.tools && agent.tools.length > 0) {\n return agent.tools.map((tool) => ({\n name: tool.name,\n displayName: tool.displayName ?? tool.name,\n isMutation: Boolean(tool.isMutation),\n requiredFeatures: tool.requiredFeatures ?? [],\n }))\n }\n return agent.allowedTools.map((toolName) => ({ name: toolName }))\n}\n\nfunction buildDebugPromptSections(agent: PlaygroundAgent): AiChatDebugPromptSection[] {\n const sections: AiChatDebugPromptSection[] = []\n if (agent.systemPrompt) {\n sections.push({ id: 'role', source: 'default', text: agent.systemPrompt })\n }\n const placeholderIds = [\n 'scope',\n 'data',\n 'tools',\n 'attachments',\n 'mutationPolicy',\n 'responseStyle',\n 'overrides',\n ] as const\n for (const id of placeholderIds) {\n sections.push({ id, source: 'placeholder' })\n }\n return sections\n}\n\ntype AgentModelResolution = {\n agentId: string\n providerId: string\n modelId: string\n baseURL: string | null\n source: string\n}\n\ntype SettingsAgentResolutionResponse = {\n agents: AgentModelResolution[]\n}\n\nasync function fetchAgentResolutions(): Promise<SettingsAgentResolutionResponse> {\n const result = await apiCall<SettingsAgentResolutionResponse>('/api/ai_assistant/settings')\n if (!result.ok || !result.result) return { agents: [] }\n return { agents: result.result.agents ?? [] }\n}\n\nasync function fetchLoopOverrideForAgent(\n agentId: string,\n): Promise<{ agentId: string; override: { loopDisabled?: boolean | null } | null }> {\n const result = await apiCall<{ agentId: string; override: { loopDisabled?: boolean | null } | null }>(\n `/api/ai_assistant/ai/agents/${encodeURIComponent(agentId)}/loop-override`,\n { method: 'GET', credentials: 'include' },\n )\n if (!result.ok || !result.result) return { agentId, override: null }\n return result.result\n}\n\nfunction LoopDisabledPlaygroundBanner({ agentId }: { agentId: string }) {\n const { data } = useQuery({\n queryKey: ['ai_assistant', 'loop_override', agentId],\n queryFn: () => fetchLoopOverrideForAgent(agentId),\n staleTime: 30000,\n })\n if (!data?.override?.loopDisabled) return null\n return <LoopDisabledBanner agentId={agentId} />\n}\n\nfunction ModelResolutionPanel({ agentId }: { agentId: string }) {\n const t = useT()\n const { data } = useQuery({\n queryKey: ['ai_assistant', 'settings', 'agents'],\n queryFn: fetchAgentResolutions,\n staleTime: 30000,\n })\n\n const resolution = data?.agents.find((agent) => agent.agentId === agentId)\n if (!resolution) return null\n\n return (\n <dl\n className=\"grid grid-cols-2 gap-x-4 gap-y-1 rounded-md border border-border bg-muted/30 p-3 text-xs sm:grid-cols-4\"\n data-ai-playground-model-resolution={agentId}\n >\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.provider', 'Provider')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-provider>\n {resolution.providerId}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.model', 'Model')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-model>\n {resolution.modelId}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.baseUrl', 'Base URL')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-base-url>\n {resolution.baseURL ?? t('ai_assistant.playground.resolution.none', '\u2014')}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-muted-foreground\">\n {t('ai_assistant.playground.resolution.source', 'Source')}\n </dt>\n <dd className=\"font-mono\" data-ai-playground-resolution-source>\n {resolution.source}\n </dd>\n </div>\n </dl>\n )\n}\n\ntype PlaygroundUiPartSeed = {\n componentId: string\n pendingActionId?: string\n payload?: unknown\n}\n\nfunction readPlaygroundUiPartSeeds(): PlaygroundUiPartSeed[] {\n if (typeof window === 'undefined') return []\n try {\n const params = new URLSearchParams(window.location.search)\n const componentId = params.get('uiPart')\n if (!componentId) return []\n const pendingActionId = params.get('pendingActionId') ?? undefined\n return [{ componentId, pendingActionId }]\n } catch {\n return []\n }\n}\n\nfunction ChatLane({ agent, debug }: { agent: PlaygroundAgent; debug: boolean }) {\n const t = useT()\n // Scoped registry so repeated mounts do not share state with other pages.\n // Step 5.10: opt in to the LIVE mutation-approval cards so the playground\n // exercises the real cards when the chat response surfaces a pending\n // action (via the `?uiPart=...` debug seed for Playwright).\n const registry = React.useMemo(\n () => createAiUiPartRegistry({ seedLiveApprovalCards: true }),\n [],\n )\n const debugTools = React.useMemo(() => buildDebugTools(agent), [agent])\n const debugPromptSections = React.useMemo(\n () => buildDebugPromptSections(agent),\n [agent],\n )\n const [uiParts, setUiParts] = React.useState<PlaygroundUiPartSeed[]>([])\n const [conversationId, setConversationId] = React.useState<string | null>(null)\n\n // Step 5.10: the dispatcher does not yet surface `AiUiPart` entries through\n // the plain-text stream consumed by `useAiChat`. For now the playground\n // reads a `?uiPart=<componentId>&pendingActionId=...` seed from the URL\n // so Playwright + operator debug flows can render the approval cards\n // against a stubbed `/api/ai_assistant/ai/actions/:id` endpoint. When the\n // dispatcher switches to the UIMessageChunk format this effect swaps over\n // to the streamed `uiParts` payload.\n React.useEffect(() => {\n const seeds = readPlaygroundUiPartSeeds()\n if (seeds.length > 0) setUiParts(seeds)\n }, [])\n\n if (agent.executionMode !== 'chat') {\n return (\n <Alert status=\"information\" data-ai-playground-unsupported=\"chat\">\n <AlertTitle>\n {t(\n 'ai_assistant.playground.chat.notSupportedTitle',\n 'Chat mode is not available for this agent.',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.playground.chat.notSupportedBody',\n 'Pick an agent whose execution mode is \"chat\", or switch to the object-mode tab.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-2\" data-ai-playground-chat={agent.id}>\n <AiChat\n key={agent.id}\n agent={agent.id}\n pageContext={{ source: 'playground', pageId: 'ai_assistant.playground' }}\n debug={debug}\n registry={registry}\n className=\"min-h-96\"\n debugTools={debugTools}\n debugPromptSections={debugPromptSections}\n uiParts={uiParts}\n onConversationIdChange={setConversationId}\n headerActions={conversationId ? <ConversationShareButton conversationId={conversationId} /> : null}\n />\n </div>\n )\n}\n\nfunction ObjectLane({ agent }: { agent: PlaygroundAgent }) {\n const t = useT()\n const [prompt, setPrompt] = React.useState('')\n const [isRunning, setIsRunning] = React.useState(false)\n const [result, setResult] = React.useState<RunObjectResponse | null>(null)\n const [error, setError] = React.useState<RunObjectError | null>(null)\n const [lastRequest, setLastRequest] = React.useState<unknown>(null)\n\n const isSupported = agent.executionMode === 'object'\n const canRun = isSupported && prompt.trim().length > 0 && !isRunning\n\n const runObject = React.useCallback(async () => {\n if (!canRun) return\n const body = {\n agent: agent.id,\n messages: [{ role: 'user' as const, content: prompt }],\n pageContext: { source: 'playground', pageId: 'ai_assistant.playground' },\n }\n setLastRequest(body)\n setIsRunning(true)\n setResult(null)\n setError(null)\n try {\n const { ok, status, result } = await apiCall<RunObjectResponse | RunObjectError>(\n '/api/ai_assistant/ai/run-object',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\n },\n )\n if (!ok) {\n const payload = (result as RunObjectError | null) ?? { error: `HTTP ${status}` }\n setError(payload)\n return\n }\n setResult((result as RunObjectResponse | null) ?? { object: null })\n } catch (err) {\n setError({\n error: err instanceof Error ? err.message : String(err),\n code: 'network_error',\n })\n } finally {\n setIsRunning(false)\n }\n }, [agent.id, canRun, prompt])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: () => {\n void runObject()\n },\n onCancel: () => {\n setError(null)\n },\n })\n\n if (!isSupported) {\n return (\n <Alert status=\"information\" data-ai-playground-unsupported=\"object\">\n <AlertTitle>\n {t(\n 'ai_assistant.playground.object.notSupportedTitle',\n 'Object mode is not available for this agent.',\n )}\n </AlertTitle>\n <AlertDescription>\n {t(\n 'ai_assistant.playground.object.notSupportedBody',\n 'This agent declares executionMode = \"chat\". Pick an object-mode agent to preview structured output, or switch to the chat tab.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"flex flex-col gap-3\" data-ai-playground-object>\n <div className=\"flex flex-col gap-2\">\n <Label htmlFor=\"ai-playground-object-input\">\n {t('ai_assistant.playground.object.inputLabel', 'Prompt')}\n </Label>\n <Textarea\n id=\"ai-playground-object-input\"\n rows={4}\n value={prompt}\n placeholder={t(\n 'ai_assistant.playground.object.inputPlaceholder',\n 'Describe what the agent should produce...',\n )}\n onChange={(event) => setPrompt(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"resize-none\"\n aria-label={t('ai_assistant.playground.object.inputLabel', 'Prompt')}\n />\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs text-muted-foreground\">\n {t(\n 'ai_assistant.playground.object.shortcutHint',\n 'Press Cmd/Ctrl+Enter to run.',\n )}\n </p>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void runObject()}\n disabled={!canRun}\n data-ai-playground-object-run\n >\n {isRunning ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n ) : (\n <Play className=\"size-4\" aria-hidden />\n )}\n <span>{t('ai_assistant.playground.object.run', 'Run object')}</span>\n </Button>\n </div>\n </div>\n\n {error ? (\n <Alert status=\"error\" data-ai-playground-object-error={error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.playground.object.errorTitle', 'Object run failed')}\n </AlertTitle>\n <AlertDescription>\n {error.code ? <span className=\"mr-2 font-mono text-xs\">{error.code}</span> : null}\n {error.error}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {result ? (\n <section\n className=\"rounded-md border border-border bg-muted/30 p-3\"\n data-ai-playground-object-result\n >\n <h3 className=\"text-sm font-semibold\">\n {t('ai_assistant.playground.object.resultTitle', 'Generated object')}\n </h3>\n <pre className=\"mt-2 max-h-96 overflow-auto rounded bg-background p-2 text-xs font-mono\">\n {JSON.stringify(result.object, null, 2)}\n </pre>\n {result.usage || result.finishReason ? (\n <dl className=\"mt-3 grid grid-cols-3 gap-2 text-xs\">\n {result.finishReason ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.finishReason', 'Finish reason')}\n </dt>\n <dd className=\"font-mono\">{result.finishReason}</dd>\n </div>\n ) : null}\n {result.usage?.inputTokens !== undefined ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.inputTokens', 'Input tokens')}\n </dt>\n <dd className=\"font-mono\">{result.usage.inputTokens}</dd>\n </div>\n ) : null}\n {result.usage?.outputTokens !== undefined ? (\n <div>\n <dt className=\"text-muted-foreground\">\n {t('ai_assistant.playground.object.outputTokens', 'Output tokens')}\n </dt>\n <dd className=\"font-mono\">{result.usage.outputTokens}</dd>\n </div>\n ) : null}\n </dl>\n ) : null}\n </section>\n ) : null}\n\n {lastRequest && (error || result) ? (\n <details\n className=\"rounded-md border border-border bg-muted/20 p-2 text-xs\"\n data-ai-playground-object-debug\n >\n <summary className=\"cursor-pointer font-semibold\">\n {t('ai_assistant.playground.object.debugTitle', 'Last request payload')}\n </summary>\n <pre className=\"mt-2 max-h-64 overflow-auto whitespace-pre-wrap font-mono\">\n {JSON.stringify(lastRequest, null, 2)}\n </pre>\n </details>\n ) : null}\n </div>\n )\n}\n\nexport function AiPlaygroundPageClient() {\n const t = useT()\n const [selectedAgentId, setSelectedAgentId] = React.useState<string | null>(null)\n const [debugEnabled, setDebugEnabled] = React.useState(false)\n const [tab, setTab] = React.useState<'chat' | 'object'>('chat')\n\n const { data, isLoading, isError, error, refetch, isFetching } = useQuery<AgentsResponse>({\n queryKey: ['ai_assistant', 'playground', 'agents'],\n queryFn: fetchAgents,\n })\n\n const agents = React.useMemo<PlaygroundAgent[]>(() => data?.agents ?? [], [data])\n\n React.useEffect(() => {\n if (!agents.length) {\n if (selectedAgentId !== null) setSelectedAgentId(null)\n return\n }\n if (!selectedAgentId || !agents.some((agent) => agent.id === selectedAgentId)) {\n setSelectedAgentId(agents[0].id)\n }\n }, [agents, selectedAgentId])\n\n const selectedAgent = React.useMemo<PlaygroundAgent | null>(() => {\n if (!selectedAgentId) return null\n return agents.find((agent) => agent.id === selectedAgentId) ?? null\n }, [agents, selectedAgentId])\n\n if (isLoading) {\n return (\n <PlaygroundLoading\n message={t('ai_assistant.playground.loadingAgents', 'Loading AI agents...')}\n />\n )\n }\n\n if (isError) {\n return (\n <Alert status=\"error\" data-ai-playground-error>\n <AlertTitle>\n {t('ai_assistant.playground.loadErrorTitle', 'Failed to load AI agents')}\n </AlertTitle>\n <AlertDescription>\n <span>{error instanceof Error ? error.message : String(error)}</span>\n <div className=\"mt-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n void refetch()\n }}\n >\n <RefreshCcw className=\"size-4\" aria-hidden />\n <span>{t('ai_assistant.playground.retry', 'Retry')}</span>\n </Button>\n </div>\n </AlertDescription>\n </Alert>\n )\n }\n\n if (!agents.length) {\n return <PlaygroundNoAgents />\n }\n\n return (\n <div className=\"flex flex-col gap-4\" data-ai-playground>\n <header className=\"flex flex-col gap-1\">\n <h1 className=\"text-2xl font-bold tracking-tight\">\n {t('ai_assistant.playground.title', 'AI Playground')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'ai_assistant.playground.subtitle',\n 'Exercise every registered AI agent end-to-end. Use the debug panel to inspect request and response payloads, and the object-mode tab to preview structured output.',\n )}\n </p>\n </header>\n\n <section className=\"flex flex-col gap-3 rounded-lg border border-border bg-background p-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between\">\n <div className=\"flex flex-col gap-2 sm:flex-1\">\n <Label htmlFor=\"ai-playground-agent-picker\">\n {t('ai_assistant.playground.agentPickerLabel', 'Agent')}\n </Label>\n <select\n id=\"ai-playground-agent-picker\"\n data-ai-playground-agent-picker\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm\"\n value={selectedAgentId ?? ''}\n onChange={(event) => setSelectedAgentId(event.target.value)}\n >\n {agents.map((agent) => (\n <option key={agent.id} value={agent.id}>\n {agent.label} ({agent.id})\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-3 sm:flex-shrink-0\">\n <Label htmlFor=\"ai-playground-debug\" className=\"text-sm\">\n {t('ai_assistant.playground.debugToggle', 'Debug panel')}\n </Label>\n <Switch\n id=\"ai-playground-debug\"\n checked={debugEnabled}\n onCheckedChange={(next: boolean) => setDebugEnabled(next)}\n aria-label={t('ai_assistant.playground.debugToggle', 'Debug panel')}\n data-ai-playground-debug-toggle\n />\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n void refetch()\n }}\n aria-label={t('ai_assistant.playground.refresh', 'Refresh agents')}\n disabled={isFetching}\n >\n <RefreshCcw className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n </div>\n {selectedAgent ? <AgentDetails agent={selectedAgent} /> : null}\n {selectedAgent ? <ModelResolutionPanel agentId={selectedAgent.id} /> : null}\n {selectedAgent ? <LoopDisabledPlaygroundBanner agentId={selectedAgent.id} /> : null}\n </section>\n\n {selectedAgent ? (\n <Tabs\n value={tab}\n onValueChange={(next: string) => setTab(next === 'object' ? 'object' : 'chat')}\n >\n <TabsList>\n <TabsTrigger value=\"chat\">\n {t('ai_assistant.playground.tabs.chat', 'Chat')}\n </TabsTrigger>\n <TabsTrigger value=\"object\">\n {t('ai_assistant.playground.tabs.object', 'Object mode')}\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"chat\">\n <ChatLane agent={selectedAgent} debug={debugEnabled} />\n </TabsContent>\n <TabsContent value=\"object\">\n <ObjectLane agent={selectedAgent} />\n </TabsContent>\n </Tabs>\n ) : null}\n </div>\n )\n}\n\nexport default AiPlaygroundPageClient\n"],
5
+ "mappings": "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",
6
6
  "names": ["result"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ai-assistant",
3
- "version": "0.6.7-develop.6862.1.c11a64ce0a",
3
+ "version": "0.6.7-develop.6869.1.d1ce9999ea",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -99,16 +99,16 @@
99
99
  "zod-to-json-schema": "^3.25.2"
100
100
  },
101
101
  "peerDependencies": {
102
- "@open-mercato/shared": "0.6.7-develop.6862.1.c11a64ce0a",
103
- "@open-mercato/ui": "0.6.7-develop.6862.1.c11a64ce0a",
102
+ "@open-mercato/shared": "0.6.7-develop.6869.1.d1ce9999ea",
103
+ "@open-mercato/ui": "0.6.7-develop.6869.1.d1ce9999ea",
104
104
  "react": "^19.0.0",
105
105
  "react-dom": "^19.0.0",
106
106
  "zod": ">=3.23.0"
107
107
  },
108
108
  "devDependencies": {
109
- "@open-mercato/cli": "0.6.7-develop.6862.1.c11a64ce0a",
110
- "@open-mercato/shared": "0.6.7-develop.6862.1.c11a64ce0a",
111
- "@open-mercato/ui": "0.6.7-develop.6862.1.c11a64ce0a",
109
+ "@open-mercato/cli": "0.6.7-develop.6869.1.d1ce9999ea",
110
+ "@open-mercato/shared": "0.6.7-develop.6869.1.d1ce9999ea",
111
+ "@open-mercato/ui": "0.6.7-develop.6869.1.d1ce9999ea",
112
112
  "@types/react": "^19.2.17",
113
113
  "@types/react-dom": "^19.2.3",
114
114
  "react": "19.2.8",
@@ -714,7 +714,7 @@ function MutationPolicySection({ agent }: { agent: AgentSettings }) {
714
714
  </div>
715
715
 
716
716
  <Alert
717
- variant="info"
717
+ status="information"
718
718
  icon={<ShieldAlert aria-hidden="true" />}
719
719
  data-ai-agent-mutation-policy-notice
720
720
  >
@@ -740,7 +740,7 @@ function MutationPolicySection({ agent }: { agent: AgentSettings }) {
740
740
  )}
741
741
  />
742
742
  ) : query.isError ? (
743
- <Alert variant="destructive" data-ai-agent-mutation-policy-load-error>
743
+ <Alert status="error" data-ai-agent-mutation-policy-load-error>
744
744
  <AlertTitle>
745
745
  {t(
746
746
  'ai_assistant.agents.mutation_policy.loadErrorTitle',
@@ -828,7 +828,7 @@ function MutationPolicySection({ agent }: { agent: AgentSettings }) {
828
828
  )}
829
829
 
830
830
  {state.kind === 'success' ? (
831
- <Alert variant="success" data-ai-agent-mutation-policy-state="success">
831
+ <Alert status="success" data-ai-agent-mutation-policy-state="success">
832
832
  <AlertTitle>
833
833
  {t('ai_assistant.agents.mutation_policy.savedTitle', 'Mutation policy updated')}
834
834
  </AlertTitle>
@@ -836,7 +836,7 @@ function MutationPolicySection({ agent }: { agent: AgentSettings }) {
836
836
  </Alert>
837
837
  ) : null}
838
838
  {state.kind === 'error' ? (
839
- <Alert variant="destructive" data-ai-agent-mutation-policy-state="error">
839
+ <Alert status="error" data-ai-agent-mutation-policy-state="error">
840
840
  <AlertTitle>
841
841
  {t(
842
842
  'ai_assistant.agents.mutation_policy.errorTitle',
@@ -1334,7 +1334,7 @@ function AgentModelOverrideSection({ agent }: { agent: AgentSettings }) {
1334
1334
  )}
1335
1335
  />
1336
1336
  ) : settingsQuery.isError ? (
1337
- <Alert variant="destructive" data-ai-agent-model-override-load-error>
1337
+ <Alert status="error" data-ai-agent-model-override-load-error>
1338
1338
  <AlertTitle>
1339
1339
  {t(
1340
1340
  'ai_assistant.agents.model_override.loadErrorTitle',
@@ -1544,12 +1544,12 @@ function AgentModelOverrideSection({ agent }: { agent: AgentSettings }) {
1544
1544
  ) : null}
1545
1545
 
1546
1546
  {state.kind === 'success' ? (
1547
- <Alert variant="success" data-ai-agent-model-override-state="success">
1547
+ <Alert status="success" data-ai-agent-model-override-state="success">
1548
1548
  <AlertDescription>{state.message}</AlertDescription>
1549
1549
  </Alert>
1550
1550
  ) : null}
1551
1551
  {state.kind === 'error' ? (
1552
- <Alert variant="destructive" data-ai-agent-model-override-state="error">
1552
+ <Alert status="error" data-ai-agent-model-override-state="error">
1553
1553
  <AlertDescription>{state.message}</AlertDescription>
1554
1554
  </Alert>
1555
1555
  ) : null}
@@ -1793,7 +1793,7 @@ function LoopPolicySection({ agent }: { agent: AgentSettings }) {
1793
1793
  message={t('ai_assistant.agents.loop_policy.loading', 'Loading loop policy...')}
1794
1794
  />
1795
1795
  ) : query.isError ? (
1796
- <Alert variant="destructive" data-ai-agent-loop-policy-load-error>
1796
+ <Alert status="error" data-ai-agent-loop-policy-load-error>
1797
1797
  <AlertTitle>
1798
1798
  {t('ai_assistant.agents.loop_policy.loadErrorTitle', 'Failed to load loop policy')}
1799
1799
  </AlertTitle>
@@ -1891,7 +1891,7 @@ function LoopPolicySection({ agent }: { agent: AgentSettings }) {
1891
1891
  </div>
1892
1892
 
1893
1893
  {state.kind === 'success' ? (
1894
- <Alert variant="success" data-ai-agent-loop-policy-state="success">
1894
+ <Alert status="success" data-ai-agent-loop-policy-state="success">
1895
1895
  <AlertTitle>
1896
1896
  {t('ai_assistant.agents.loop_policy.savedTitle', 'Loop policy updated')}
1897
1897
  </AlertTitle>
@@ -1899,7 +1899,7 @@ function LoopPolicySection({ agent }: { agent: AgentSettings }) {
1899
1899
  </Alert>
1900
1900
  ) : null}
1901
1901
  {state.kind === 'error' ? (
1902
- <Alert variant="destructive" data-ai-agent-loop-policy-state="error">
1902
+ <Alert status="error" data-ai-agent-loop-policy-state="error">
1903
1903
  <AlertTitle>
1904
1904
  {t(
1905
1905
  'ai_assistant.agents.loop_policy.errorTitle',
@@ -2246,7 +2246,7 @@ function AgentDetailPanel({ agent }: { agent: AgentSettings }) {
2246
2246
  </header>
2247
2247
  <div className="mt-3">
2248
2248
  <Alert
2249
- variant="info"
2249
+ status="information"
2250
2250
  icon={<Wand2 aria-hidden="true" />}
2251
2251
  data-ai-agent-prompt-notice
2252
2252
  >
@@ -2262,7 +2262,7 @@ function AgentDetailPanel({ agent }: { agent: AgentSettings }) {
2262
2262
  </Alert>
2263
2263
  </div>
2264
2264
  {saveState.kind === 'success' ? (
2265
- <Alert variant="success" className="mt-3" data-ai-agent-prompt-state="success">
2265
+ <Alert status="success" className="mt-3" data-ai-agent-prompt-state="success">
2266
2266
  <AlertTitle>
2267
2267
  {saveState.version > 0
2268
2268
  ? t('ai_assistant.agents.override.savedTitle', 'Prompt override saved')
@@ -2276,7 +2276,7 @@ function AgentDetailPanel({ agent }: { agent: AgentSettings }) {
2276
2276
  </Alert>
2277
2277
  ) : null}
2278
2278
  {saveState.kind === 'error' ? (
2279
- <Alert variant="destructive" className="mt-3" data-ai-agent-prompt-state="error">
2279
+ <Alert status="error" className="mt-3" data-ai-agent-prompt-state="error">
2280
2280
  <AlertTitle>
2281
2281
  {t('ai_assistant.agents.override.errorTitle', 'Failed to save prompt override')}
2282
2282
  </AlertTitle>
@@ -2396,7 +2396,7 @@ function AgentDetailPanel({ agent }: { agent: AgentSettings }) {
2396
2396
  )}
2397
2397
  />
2398
2398
  ) : overrideQuery.isError ? (
2399
- <Alert variant="destructive" data-ai-agent-override-history-error>
2399
+ <Alert status="error" data-ai-agent-override-history-error>
2400
2400
  <AlertTitle>
2401
2401
  {t(
2402
2402
  'ai_assistant.agents.override.history.errorTitle',
@@ -2505,7 +2505,7 @@ export function AiAgentSettingsPageClient() {
2505
2505
 
2506
2506
  if (isError) {
2507
2507
  return (
2508
- <Alert variant="destructive" data-ai-agent-settings-error>
2508
+ <Alert status="error" data-ai-agent-settings-error>
2509
2509
  <AlertTitle>
2510
2510
  {t('ai_assistant.agents.loadErrorTitle', 'Failed to load AI agents')}
2511
2511
  </AlertTitle>
@@ -129,7 +129,7 @@ export function AiTenantAllowlistPageClient(): React.JSX.Element {
129
129
  return (
130
130
  <div className="flex max-w-3xl flex-col gap-4">
131
131
  {pageHeader}
132
- <Alert variant="destructive">
132
+ <Alert status="error">
133
133
  <AlertTitle>{t('ai_assistant.allowlist.loadError.title', 'Failed to load allowlist')}</AlertTitle>
134
134
  <AlertDescription>
135
135
  {settingsQuery.error instanceof Error
@@ -311,7 +311,7 @@ export function AiTenantAllowlistPageClient(): React.JSX.Element {
311
311
  {envBanner}
312
312
 
313
313
  {feedback ? (
314
- <Alert variant={feedback.kind === 'error' ? 'destructive' : undefined}>
314
+ <Alert status={feedback.kind === 'error' ? 'error' : undefined}>
315
315
  <AlertDescription>{feedback.text}</AlertDescription>
316
316
  </Alert>
317
317
  ) : null}