@open-mercato/ai-assistant 0.6.1-develop.3246.1.dbef9d7392 → 0.6.1-develop.3256.1.fe3dec2464

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +82 -18
  3. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js +370 -0
  4. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js.map +7 -0
  5. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js +194 -0
  6. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js.map +7 -0
  7. package/dist/modules/ai_assistant/api/ai/agents/route.js +4 -0
  8. package/dist/modules/ai_assistant/api/ai/agents/route.js.map +2 -2
  9. package/dist/modules/ai_assistant/api/ai/chat/route.js +169 -5
  10. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
  11. package/dist/modules/ai_assistant/api/route/route.js +38 -19
  12. package/dist/modules/ai_assistant/api/route/route.js.map +3 -3
  13. package/dist/modules/ai_assistant/api/settings/allowlist/route.js +195 -0
  14. package/dist/modules/ai_assistant/api/settings/allowlist/route.js.map +7 -0
  15. package/dist/modules/ai_assistant/api/settings/route.js +537 -22
  16. package/dist/modules/ai_assistant/api/settings/route.js.map +3 -3
  17. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +701 -147
  18. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
  19. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +338 -0
  20. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +7 -0
  21. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.js +10 -0
  22. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.js.map +7 -0
  23. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.js +25 -0
  24. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.js.map +7 -0
  25. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js +1 -1
  26. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js.map +2 -2
  27. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +75 -26
  28. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
  29. package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.js +10 -0
  30. package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.js.map +7 -0
  31. package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.js +25 -0
  32. package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.js.map +7 -0
  33. package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js +503 -168
  34. package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js.map +2 -2
  35. package/dist/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.js +5 -0
  36. package/dist/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.js.map +7 -0
  37. package/dist/modules/ai_assistant/data/entities/AiTenantModelAllowlist.js +5 -0
  38. package/dist/modules/ai_assistant/data/entities/AiTenantModelAllowlist.js.map +7 -0
  39. package/dist/modules/ai_assistant/data/entities.js +123 -1
  40. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  41. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +157 -0
  42. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +7 -0
  43. package/dist/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.js +77 -0
  44. package/dist/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.js.map +7 -0
  45. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +1 -1
  46. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +2 -2
  47. package/dist/modules/ai_assistant/i18n/de.json +90 -1
  48. package/dist/modules/ai_assistant/i18n/en.json +90 -1
  49. package/dist/modules/ai_assistant/i18n/es.json +90 -1
  50. package/dist/modules/ai_assistant/i18n/pl.json +90 -1
  51. package/dist/modules/ai_assistant/lib/agent-registry.js +17 -1
  52. package/dist/modules/ai_assistant/lib/agent-registry.js.map +2 -2
  53. package/dist/modules/ai_assistant/lib/agent-runtime.js +133 -36
  54. package/dist/modules/ai_assistant/lib/agent-runtime.js.map +2 -2
  55. package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
  56. package/dist/modules/ai_assistant/lib/baseurl-allowlist.js +29 -0
  57. package/dist/modules/ai_assistant/lib/baseurl-allowlist.js.map +7 -0
  58. package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js +4 -1
  59. package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js.map +2 -2
  60. package/dist/modules/ai_assistant/lib/llm-adapters/google.js +4 -1
  61. package/dist/modules/ai_assistant/lib/llm-adapters/google.js.map +2 -2
  62. package/dist/modules/ai_assistant/lib/model-allowlist.js +211 -0
  63. package/dist/modules/ai_assistant/lib/model-allowlist.js.map +7 -0
  64. package/dist/modules/ai_assistant/lib/model-factory.js +203 -31
  65. package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
  66. package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +32 -1
  67. package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
  68. package/dist/modules/ai_assistant/migrations/Migration20260508140000.js +18 -0
  69. package/dist/modules/ai_assistant/migrations/Migration20260508140000.js.map +7 -0
  70. package/dist/modules/ai_assistant/migrations/Migration20260512090000.js +16 -0
  71. package/dist/modules/ai_assistant/migrations/Migration20260512090000.js.map +7 -0
  72. package/dist/modules/ai_assistant/migrations/Migration20260512130000.js +15 -0
  73. package/dist/modules/ai_assistant/migrations/Migration20260512130000.js.map +7 -0
  74. package/generated/entities/ai_agent_runtime_override/index.ts +13 -0
  75. package/generated/entities/ai_tenant_model_allowlist/index.ts +9 -0
  76. package/generated/entities.ids.generated.ts +2 -0
  77. package/generated/entity-fields-registry.ts +26 -0
  78. package/jest.config.cjs +2 -0
  79. package/package.json +4 -4
  80. package/src/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.ts +477 -0
  81. package/src/modules/ai_assistant/__tests__/settings-page-logic.test.ts +116 -0
  82. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/__tests__/route.test.ts +240 -0
  83. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/route.ts +251 -0
  84. package/src/modules/ai_assistant/api/ai/agents/route.ts +4 -0
  85. package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +273 -0
  86. package/src/modules/ai_assistant/api/ai/chat/route.ts +211 -2
  87. package/src/modules/ai_assistant/api/route/route.ts +49 -25
  88. package/src/modules/ai_assistant/api/settings/__tests__/route.test.ts +408 -0
  89. package/src/modules/ai_assistant/api/settings/allowlist/route.ts +221 -0
  90. package/src/modules/ai_assistant/api/settings/route.ts +721 -27
  91. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +858 -177
  92. package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +458 -0
  93. package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.ts +23 -0
  94. package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.tsx +12 -0
  95. package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.tsx +1 -1
  96. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +89 -12
  97. package/src/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.ts +23 -0
  98. package/src/modules/ai_assistant/backend/config/ai-assistant/settings/page.tsx +18 -0
  99. package/src/modules/ai_assistant/components/AiAssistantSettingsPageClient.tsx +617 -209
  100. package/src/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.ts +7 -0
  101. package/src/modules/ai_assistant/data/entities/AiTenantModelAllowlist.ts +2 -0
  102. package/src/modules/ai_assistant/data/entities.ts +164 -0
  103. package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +227 -0
  104. package/src/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.ts +132 -0
  105. package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentRuntimeOverrideRepository.test.ts +337 -0
  106. package/src/modules/ai_assistant/data/repositories/__tests__/AiTenantModelAllowlistRepository.test.ts +181 -0
  107. package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +1 -1
  108. package/src/modules/ai_assistant/i18n/de.json +90 -1
  109. package/src/modules/ai_assistant/i18n/en.json +90 -1
  110. package/src/modules/ai_assistant/i18n/es.json +90 -1
  111. package/src/modules/ai_assistant/i18n/pl.json +90 -1
  112. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-phase4a.test.ts +396 -0
  113. package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +60 -6
  114. package/src/modules/ai_assistant/lib/__tests__/ai-api-operation-runner.test.ts +4 -2
  115. package/src/modules/ai_assistant/lib/__tests__/baseurl-allowlist.test.ts +75 -0
  116. package/src/modules/ai_assistant/lib/__tests__/llm-adapters-anthropic.test.ts +18 -0
  117. package/src/modules/ai_assistant/lib/__tests__/llm-adapters-google.test.ts +18 -0
  118. package/src/modules/ai_assistant/lib/__tests__/llm-adapters-openai.test.ts +150 -4
  119. package/src/modules/ai_assistant/lib/__tests__/model-allowlist.test.ts +290 -0
  120. package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +634 -0
  121. package/src/modules/ai_assistant/lib/agent-registry.ts +20 -1
  122. package/src/modules/ai_assistant/lib/agent-runtime.ts +220 -44
  123. package/src/modules/ai_assistant/lib/ai-agent-definition.ts +48 -0
  124. package/src/modules/ai_assistant/lib/baseurl-allowlist.ts +64 -0
  125. package/src/modules/ai_assistant/lib/llm-adapters/anthropic.ts +11 -1
  126. package/src/modules/ai_assistant/lib/llm-adapters/google.ts +4 -1
  127. package/src/modules/ai_assistant/lib/model-allowlist.ts +407 -0
  128. package/src/modules/ai_assistant/lib/model-factory.ts +486 -58
  129. package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +44 -0
  130. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +704 -235
  131. package/src/modules/ai_assistant/migrations/Migration20260508140000.ts +18 -0
  132. package/src/modules/ai_assistant/migrations/Migration20260512090000.ts +16 -0
  133. package/src/modules/ai_assistant/migrations/Migration20260512130000.ts +13 -0
@@ -24,8 +24,17 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
24
24
  import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives/alert";
25
25
  import { Badge } from "@open-mercato/ui/primitives/badge";
26
26
  import { Button } from "@open-mercato/ui/primitives/button";
27
+ import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
27
28
  import { IconButton } from "@open-mercato/ui/primitives/icon-button";
28
29
  import { Label } from "@open-mercato/ui/primitives/label";
30
+ import { Radio, RadioGroup } from "@open-mercato/ui/primitives/radio";
31
+ import {
32
+ Select,
33
+ SelectContent,
34
+ SelectItem,
35
+ SelectTrigger,
36
+ SelectValue
37
+ } from "@open-mercato/ui/primitives/select";
29
38
  import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
30
39
  import { Switch } from "@open-mercato/ui/primitives/switch";
31
40
  import { Textarea } from "@open-mercato/ui/primitives/textarea";
@@ -37,6 +46,8 @@ import {
37
46
  } from "@open-mercato/ui/primitives/tooltip";
38
47
  import { EmptyState } from "@open-mercato/ui/backend/EmptyState";
39
48
  import { apiCall, apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
49
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
50
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
40
51
  import { useAiShortcuts } from "@open-mercato/ui/ai";
41
52
  const PROMPT_SECTION_IDS = [
42
53
  "role",
@@ -71,6 +82,15 @@ async function fetchAgents() {
71
82
  if (!result) throw new Error(`Failed to load agents (${status})`);
72
83
  return result;
73
84
  }
85
+ async function fetchRuntimeSettings() {
86
+ const { result, status } = await apiCallOrThrow(
87
+ "/api/ai_assistant/settings",
88
+ { method: "GET", credentials: "include" },
89
+ { errorMessage: "Failed to load runtime settings" }
90
+ );
91
+ if (!result) throw new Error(`Failed to load runtime settings (${status})`);
92
+ return result;
93
+ }
74
94
  async function fetchOverride(agentId) {
75
95
  const { result, status } = await apiCallOrThrow(
76
96
  `/api/ai_assistant/ai/agents/${encodeURIComponent(agentId)}/prompt-override`,
@@ -164,8 +184,8 @@ function PromptSectionEditor({
164
184
  className: "flex flex-col gap-2 rounded-md border border-border bg-muted/20 p-3",
165
185
  "data-ai-agent-prompt-section": sectionId,
166
186
  children: [
167
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
168
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
187
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
188
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
169
189
  /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: sectionLabel }),
170
190
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: override ? t(
171
191
  "ai_assistant.agents.prompt.overrideModeLabel",
@@ -223,7 +243,7 @@ function ToolRow({ tool }) {
223
243
  return /* @__PURE__ */ jsxs(
224
244
  "div",
225
245
  {
226
- className: "flex items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
246
+ className: "flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
227
247
  "data-ai-agent-tool-row": tool.name,
228
248
  children: [
229
249
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 min-w-0", children: [
@@ -233,7 +253,7 @@ function ToolRow({ tool }) {
233
253
  /* @__PURE__ */ jsx("span", { className: "truncate text-xs font-mono text-muted-foreground", children: tool.name })
234
254
  ] })
235
255
  ] }),
236
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
256
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 flex-shrink-0", children: [
237
257
  tool.isMutation ? /* @__PURE__ */ jsx(StatusBadge, { variant: "warning", dot: true, children: t("ai_assistant.agents.tools.mutationBadge", "Mutation") }) : /* @__PURE__ */ jsx(StatusBadge, { variant: "neutral", dot: true, children: t("ai_assistant.agents.tools.readBadge", "Read") }),
238
258
  !tool.registered ? /* @__PURE__ */ jsx(StatusBadge, { variant: "error", dot: true, children: t("ai_assistant.agents.tools.missingBadge", "Missing") }) : null,
239
259
  /* @__PURE__ */ jsxs(Tooltip, { children: [
@@ -303,6 +323,12 @@ function MutationPolicySection({ agent }) {
303
323
  const [isSaving, setIsSaving] = React.useState(false);
304
324
  const [isClearing, setIsClearing] = React.useState(false);
305
325
  const [state, setState] = React.useState({ kind: "idle" });
326
+ const { runMutation: runSavePolicyMutation } = useGuardedMutation({
327
+ contextId: `ai-agent-mutation-policy-save-${agent.id}`
328
+ });
329
+ const { runMutation: runClearPolicyMutation } = useGuardedMutation({
330
+ contextId: `ai-agent-mutation-policy-clear-${agent.id}`
331
+ });
306
332
  React.useEffect(() => {
307
333
  setSelected(currentOverride?.mutationPolicy ?? null);
308
334
  setState({ kind: "idle" });
@@ -319,78 +345,79 @@ function MutationPolicySection({ agent }) {
319
345
  setIsSaving(true);
320
346
  setState({ kind: "idle" });
321
347
  try {
322
- const { ok, status, result } = await apiCall(
323
- `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
324
- {
325
- method: "POST",
326
- headers: { "content-type": "application/json" },
327
- credentials: "include",
328
- body: JSON.stringify({ mutationPolicy: selected })
329
- }
330
- );
331
- const payload = result ?? {};
332
- if (!ok) {
333
- const message = payload.code === "escalation_not_allowed" ? payload.error ?? t(
334
- "ai_assistant.agents.mutation_policy.errors.escalationNotAllowed",
335
- "Cannot upgrade beyond the agent's declared policy \u2014 this is a code-level change."
336
- ) : payload.error ?? `Failed to save mutation policy (${status}).`;
337
- setState({ kind: "error", message });
338
- return;
339
- }
340
- setState({
341
- kind: "success",
342
- message: t(
343
- "ai_assistant.agents.mutation_policy.savedMessage",
344
- "Mutation policy override saved."
345
- )
348
+ await runSavePolicyMutation({
349
+ operation: async () => {
350
+ const { ok, status, result } = await apiCall(
351
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
352
+ {
353
+ method: "POST",
354
+ headers: { "content-type": "application/json" },
355
+ credentials: "include",
356
+ body: JSON.stringify({ mutationPolicy: selected })
357
+ }
358
+ );
359
+ const payload = result ?? {};
360
+ if (!ok) {
361
+ const message = payload.code === "escalation_not_allowed" ? payload.error ?? t(
362
+ "ai_assistant.agents.mutation_policy.errors.escalationNotAllowed",
363
+ "Cannot upgrade beyond the agent's declared policy \u2014 this is a code-level change."
364
+ ) : payload.error ?? `Failed to save mutation policy (${status}).`;
365
+ throw new Error(message);
366
+ }
367
+ },
368
+ context: {}
346
369
  });
370
+ const successMessage = t(
371
+ "ai_assistant.agents.mutation_policy.savedMessage",
372
+ "Mutation policy override saved."
373
+ );
374
+ setState({ kind: "success", message: successMessage });
375
+ flash(successMessage, "success");
347
376
  await queryClient.invalidateQueries({
348
377
  queryKey: ["ai_assistant", "agent_settings", "mutation_policy", agent.id]
349
378
  });
350
379
  } catch (err) {
351
- setState({
352
- kind: "error",
353
- message: err instanceof Error ? err.message : String(err)
354
- });
380
+ const message = err instanceof Error ? err.message : String(err);
381
+ setState({ kind: "error", message });
382
+ flash(message, "error");
355
383
  } finally {
356
384
  setIsSaving(false);
357
385
  }
358
- }, [agent.id, isSaving, queryClient, selected, t]);
386
+ }, [agent.id, isSaving, queryClient, runSavePolicyMutation, selected, t]);
359
387
  const clear = React.useCallback(async () => {
360
388
  if (isClearing) return;
361
389
  setIsClearing(true);
362
390
  setState({ kind: "idle" });
363
391
  try {
364
- const { ok, status, result } = await apiCall(
365
- `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
366
- {
367
- method: "DELETE",
368
- credentials: "include"
369
- }
370
- );
371
- const payload = result ?? {};
372
- if (!ok) {
373
- setState({
374
- kind: "error",
375
- message: payload.error ?? `Failed to clear override (${status}).`
376
- });
377
- return;
378
- }
379
- setState({
380
- kind: "success",
381
- message: t(
382
- "ai_assistant.agents.mutation_policy.clearedMessage",
383
- "Mutation policy override cleared; agent is using its code-declared policy."
384
- )
392
+ await runClearPolicyMutation({
393
+ operation: async () => {
394
+ const { ok, status, result } = await apiCall(
395
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
396
+ {
397
+ method: "DELETE",
398
+ credentials: "include"
399
+ }
400
+ );
401
+ const payload = result ?? {};
402
+ if (!ok) {
403
+ throw new Error(payload.error ?? `Failed to clear override (${status}).`);
404
+ }
405
+ },
406
+ context: {}
385
407
  });
408
+ const successMessage = t(
409
+ "ai_assistant.agents.mutation_policy.clearedMessage",
410
+ "Mutation policy override cleared; agent is using its code-declared policy."
411
+ );
412
+ setState({ kind: "success", message: successMessage });
413
+ flash(successMessage, "success");
386
414
  await queryClient.invalidateQueries({
387
415
  queryKey: ["ai_assistant", "agent_settings", "mutation_policy", agent.id]
388
416
  });
389
417
  } catch (err) {
390
- setState({
391
- kind: "error",
392
- message: err instanceof Error ? err.message : String(err)
393
- });
418
+ const message = err instanceof Error ? err.message : String(err);
419
+ setState({ kind: "error", message });
420
+ flash(message, "error");
394
421
  } finally {
395
422
  setIsClearing(false);
396
423
  }
@@ -401,10 +428,10 @@ function MutationPolicySection({ agent }) {
401
428
  className: "rounded-lg border border-border bg-background p-4",
402
429
  "data-ai-agent-mutation-policy": agent.id,
403
430
  children: [
404
- /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
405
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
431
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
432
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
406
433
  /* @__PURE__ */ jsx(ShieldAlert, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
407
- /* @__PURE__ */ jsxs("div", { children: [
434
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
408
435
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.mutation_policy.title", "Mutation policy") }),
409
436
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
410
437
  "ai_assistant.agents.mutation_policy.subtitle",
@@ -417,21 +444,23 @@ function MutationPolicySection({ agent }) {
417
444
  {
418
445
  variant: mutationPolicyStatusMap[effectivePolicy] ?? "neutral",
419
446
  dot: true,
447
+ className: "max-w-full whitespace-normal break-all",
420
448
  "data-ai-agent-mutation-policy-effective": true,
421
449
  children: effectivePolicy
422
450
  }
423
451
  )
424
452
  ] }),
425
453
  /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-3", children: [
426
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
427
- /* @__PURE__ */ jsxs("div", { children: [
454
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3", children: [
455
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
428
456
  /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.mutation_policy.codeDeclared", "Code-declared") }),
429
- /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
457
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2", children: [
430
458
  /* @__PURE__ */ jsx(
431
459
  StatusBadge,
432
460
  {
433
461
  variant: mutationPolicyStatusMap[codeDeclared] ?? "neutral",
434
462
  dot: true,
463
+ className: "max-w-full whitespace-normal break-all",
435
464
  "data-ai-agent-mutation-policy-code-declared": true,
436
465
  children: codeDeclared
437
466
  }
@@ -443,13 +472,14 @@ function MutationPolicySection({ agent }) {
443
472
  ) })
444
473
  ] })
445
474
  ] }),
446
- /* @__PURE__ */ jsxs("div", { children: [
475
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
447
476
  /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.mutation_policy.tenantOverride", "Tenant override") }),
448
477
  /* @__PURE__ */ jsx("div", { className: "mt-1", children: currentOverride ? /* @__PURE__ */ jsx(
449
478
  StatusBadge,
450
479
  {
451
480
  variant: mutationPolicyStatusMap[currentOverride.mutationPolicy] ?? "neutral",
452
481
  dot: true,
482
+ className: "max-w-full whitespace-normal break-all",
453
483
  "data-ai-agent-mutation-policy-override-current": true,
454
484
  children: currentOverride.mutationPolicy
455
485
  }
@@ -493,10 +523,13 @@ function MutationPolicySection({ agent }) {
493
523
  ) }),
494
524
  /* @__PURE__ */ jsx(AlertDescription, { children: query.error instanceof Error ? query.error.message : String(query.error) })
495
525
  ] }) : /* @__PURE__ */ jsx(
496
- "div",
526
+ RadioGroup,
497
527
  {
528
+ value: selected ?? "",
529
+ onValueChange: (value) => {
530
+ setSelected(value);
531
+ },
498
532
  className: "flex flex-col gap-2",
499
- role: "radiogroup",
500
533
  "aria-label": t(
501
534
  "ai_assistant.agents.mutation_policy.pickerLabel",
502
535
  "Mutation policy override"
@@ -508,24 +541,21 @@ function MutationPolicySection({ agent }) {
508
541
  const isSelected = selected === option;
509
542
  return /* @__PURE__ */ jsxs(Tooltip, { children: [
510
543
  /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
511
- "label",
544
+ "div",
512
545
  {
513
- className: `flex items-start gap-3 rounded-md border px-3 py-2 text-sm cursor-pointer transition-colors ${wouldEscalate ? "border-border bg-muted/30 cursor-not-allowed opacity-60" : isSelected ? "border-primary bg-primary/5" : "border-border bg-background hover:bg-muted/40"}`,
546
+ className: `flex items-start gap-3 rounded-md border px-3 py-2 text-sm cursor-pointer transition-colors ${wouldEscalate ? "border-border bg-muted/30 cursor-not-allowed opacity-60" : isSelected ? "border-accent-indigo bg-accent-indigo/5" : "border-border bg-background hover:bg-muted/40"}`,
547
+ onClick: () => {
548
+ if (wouldEscalate) return;
549
+ setSelected(option);
550
+ },
514
551
  "data-ai-agent-mutation-policy-option": option,
515
552
  "data-ai-agent-mutation-policy-option-disabled": wouldEscalate ? "true" : "false",
516
553
  children: [
517
554
  /* @__PURE__ */ jsx(
518
- "input",
555
+ Radio,
519
556
  {
520
- type: "radio",
521
- name: `mutation-policy-${agent.id}`,
522
557
  value: option,
523
- checked: isSelected,
524
558
  disabled: wouldEscalate,
525
- onChange: () => {
526
- if (wouldEscalate) return;
527
- setSelected(option);
528
- },
529
559
  className: "mt-0.5",
530
560
  "aria-disabled": wouldEscalate
531
561
  }
@@ -571,7 +601,7 @@ function MutationPolicySection({ agent }) {
571
601
  ) }),
572
602
  /* @__PURE__ */ jsx(AlertDescription, { children: state.message })
573
603
  ] }) : null,
574
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
604
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
575
605
  /* @__PURE__ */ jsxs(
576
606
  Button,
577
607
  {
@@ -607,6 +637,509 @@ function MutationPolicySection({ agent }) {
607
637
  }
608
638
  );
609
639
  }
640
+ function AgentModelOverrideSection({ agent }) {
641
+ const t = useT();
642
+ const queryClient = useQueryClient();
643
+ const settingsQuery = useQuery({
644
+ queryKey: ["ai_assistant", "agent_settings", "runtime_settings"],
645
+ queryFn: fetchRuntimeSettings,
646
+ retry: false
647
+ });
648
+ const agentResolution = settingsQuery.data?.agents.find((entry) => entry.agentId === agent.id) ?? null;
649
+ const configuredProviders = React.useMemo(
650
+ () => (settingsQuery.data?.availableProviders ?? []).filter((provider) => provider.configured),
651
+ [settingsQuery.data?.availableProviders]
652
+ );
653
+ const [selectedProviderId, setSelectedProviderId] = React.useState("");
654
+ const [selectedModelId, setSelectedModelId] = React.useState("");
655
+ const [allowedProviders, setAllowedProviders] = React.useState(null);
656
+ const [allowedModelsByProvider, setAllowedModelsByProvider] = React.useState({});
657
+ const [allowlistDirty, setAllowlistDirty] = React.useState(false);
658
+ const [isSaving, setIsSaving] = React.useState(false);
659
+ const [isClearing, setIsClearing] = React.useState(false);
660
+ const [isSavingAllowlist, setIsSavingAllowlist] = React.useState(false);
661
+ const [state, setState] = React.useState({ kind: "idle" });
662
+ const { runMutation: runSaveModelOverrideMutation } = useGuardedMutation({
663
+ contextId: `ai-agent-model-override-save-${agent.id}`
664
+ });
665
+ const { runMutation: runClearModelOverrideMutation } = useGuardedMutation({
666
+ contextId: `ai-agent-model-override-clear-${agent.id}`
667
+ });
668
+ const { runMutation: runSaveModelAllowlistMutation } = useGuardedMutation({
669
+ contextId: `ai-agent-model-override-allowlist-${agent.id}`
670
+ });
671
+ React.useEffect(() => {
672
+ const override = agentResolution?.override;
673
+ setSelectedProviderId(override?.providerId ?? "");
674
+ setSelectedModelId(override?.modelId ?? "");
675
+ setAllowedProviders(agentResolution?.runtimeOverrideAllowlist.tenant?.allowedProviders ?? null);
676
+ setAllowedModelsByProvider({
677
+ ...agentResolution?.runtimeOverrideAllowlist.tenant?.allowedModelsByProvider ?? {}
678
+ });
679
+ setAllowlistDirty(false);
680
+ setState({ kind: "idle" });
681
+ }, [
682
+ agent.id,
683
+ agentResolution?.override?.modelId,
684
+ agentResolution?.override?.providerId,
685
+ agentResolution?.runtimeOverrideAllowlist.tenant
686
+ ]);
687
+ const selectedProvider = configuredProviders.find((provider) => provider.id === selectedProviderId);
688
+ const isProviderAllowedForPicker = React.useCallback(
689
+ (providerId) => {
690
+ if (allowedProviders === null) return true;
691
+ return allowedProviders.includes(providerId);
692
+ },
693
+ [allowedProviders]
694
+ );
695
+ const isModelAllowedForPicker = React.useCallback(
696
+ (providerId, modelId) => {
697
+ const list = allowedModelsByProvider[providerId];
698
+ if (list === void 0) return true;
699
+ return list.includes(modelId);
700
+ },
701
+ [allowedModelsByProvider]
702
+ );
703
+ const hasChange = selectedProviderId.length > 0 && selectedModelId.length > 0 && (selectedProviderId !== (agentResolution?.override?.providerId ?? "") || selectedModelId !== (agentResolution?.override?.modelId ?? ""));
704
+ const save = React.useCallback(async () => {
705
+ if (isSaving || !selectedProviderId || !selectedModelId) return;
706
+ setIsSaving(true);
707
+ setState({ kind: "idle" });
708
+ try {
709
+ await runSaveModelOverrideMutation({
710
+ operation: async () => {
711
+ const { ok, status, result } = await apiCall(
712
+ "/api/ai_assistant/settings",
713
+ {
714
+ method: "PUT",
715
+ headers: { "content-type": "application/json" },
716
+ credentials: "include",
717
+ body: JSON.stringify({
718
+ agentId: agent.id,
719
+ providerId: selectedProviderId,
720
+ modelId: selectedModelId
721
+ })
722
+ }
723
+ );
724
+ if (!ok) {
725
+ throw new Error(result?.error ?? `Failed to save model override (${status}).`);
726
+ }
727
+ },
728
+ context: {}
729
+ });
730
+ const successMessage = t("ai_assistant.agents.model_override.saved", "Model override saved.");
731
+ setState({ kind: "success", message: successMessage });
732
+ flash(successMessage, "success");
733
+ await queryClient.invalidateQueries({
734
+ queryKey: ["ai_assistant", "agent_settings", "runtime_settings"]
735
+ });
736
+ } catch (err) {
737
+ const message = err instanceof Error ? err.message : String(err);
738
+ setState({ kind: "error", message });
739
+ flash(message, "error");
740
+ } finally {
741
+ setIsSaving(false);
742
+ }
743
+ }, [agent.id, isSaving, queryClient, runSaveModelOverrideMutation, selectedModelId, selectedProviderId, t]);
744
+ const clear = React.useCallback(async () => {
745
+ if (isClearing) return;
746
+ setIsClearing(true);
747
+ setState({ kind: "idle" });
748
+ try {
749
+ await runClearModelOverrideMutation({
750
+ operation: async () => {
751
+ const { ok, status, result } = await apiCall(
752
+ "/api/ai_assistant/settings",
753
+ {
754
+ method: "DELETE",
755
+ headers: { "content-type": "application/json" },
756
+ credentials: "include",
757
+ body: JSON.stringify({ agentId: agent.id })
758
+ }
759
+ );
760
+ if (!ok) {
761
+ throw new Error(result?.error ?? `Failed to clear model override (${status}).`);
762
+ }
763
+ },
764
+ context: {}
765
+ });
766
+ setSelectedProviderId("");
767
+ setSelectedModelId("");
768
+ const successMessage = t(
769
+ "ai_assistant.agents.model_override.cleared",
770
+ "Model override cleared; the agent is using the normal resolution chain."
771
+ );
772
+ setState({ kind: "success", message: successMessage });
773
+ flash(successMessage, "success");
774
+ await queryClient.invalidateQueries({
775
+ queryKey: ["ai_assistant", "agent_settings", "runtime_settings"]
776
+ });
777
+ } catch (err) {
778
+ const message = err instanceof Error ? err.message : String(err);
779
+ setState({ kind: "error", message });
780
+ flash(message, "error");
781
+ } finally {
782
+ setIsClearing(false);
783
+ }
784
+ }, [agent.id, isClearing, queryClient, runClearModelOverrideMutation, t]);
785
+ const toggleAllowedProvider = React.useCallback(
786
+ (providerId, next) => {
787
+ setAllowlistDirty(true);
788
+ setState({ kind: "idle" });
789
+ setAllowedProviders((current) => {
790
+ if (next) {
791
+ return current === null ? [providerId] : Array.from(/* @__PURE__ */ new Set([...current, providerId]));
792
+ }
793
+ const baseline = current === null ? configuredProviders.map((provider) => provider.id) : current;
794
+ return baseline.filter((id) => id !== providerId);
795
+ });
796
+ },
797
+ [configuredProviders]
798
+ );
799
+ const toggleAllowedModel = React.useCallback(
800
+ (providerId, modelId, next) => {
801
+ setAllowlistDirty(true);
802
+ setState({ kind: "idle" });
803
+ const provider = configuredProviders.find((entry) => entry.id === providerId);
804
+ const allModelIds = provider?.defaultModels.map((model) => model.id) ?? [];
805
+ setAllowedModelsByProvider((current) => {
806
+ const existing = current[providerId];
807
+ const baseline = existing === void 0 ? allModelIds : existing;
808
+ const nextModels = next ? Array.from(/* @__PURE__ */ new Set([...baseline, modelId])) : baseline.filter((id) => id !== modelId);
809
+ return { ...current, [providerId]: nextModels };
810
+ });
811
+ },
812
+ [configuredProviders]
813
+ );
814
+ const resetAllowlistDraft = React.useCallback(() => {
815
+ setAllowedProviders(null);
816
+ setAllowedModelsByProvider({});
817
+ setAllowlistDirty(true);
818
+ setState({ kind: "idle" });
819
+ }, []);
820
+ const saveAllowlist = React.useCallback(async () => {
821
+ if (isSavingAllowlist) return;
822
+ setIsSavingAllowlist(true);
823
+ setState({ kind: "idle" });
824
+ try {
825
+ await runSaveModelAllowlistMutation({
826
+ operation: async () => {
827
+ const { ok, status, result } = await apiCall(
828
+ "/api/ai_assistant/settings",
829
+ {
830
+ method: "PUT",
831
+ headers: { "content-type": "application/json" },
832
+ credentials: "include",
833
+ body: JSON.stringify({
834
+ agentId: agent.id,
835
+ allowedOverrideProviders: allowedProviders,
836
+ allowedOverrideModelsByProvider: allowedModelsByProvider
837
+ })
838
+ }
839
+ );
840
+ if (!ok) {
841
+ throw new Error(result?.error ?? `Failed to save chat override allowlist (${status}).`);
842
+ }
843
+ },
844
+ context: {}
845
+ });
846
+ setAllowlistDirty(false);
847
+ const successMessage = t(
848
+ "ai_assistant.agents.model_override.allowlistSaved",
849
+ "Chat override choices saved."
850
+ );
851
+ setState({ kind: "success", message: successMessage });
852
+ flash(successMessage, "success");
853
+ await queryClient.invalidateQueries({
854
+ queryKey: ["ai_assistant", "agent_settings", "runtime_settings"]
855
+ });
856
+ } catch (err) {
857
+ const message = err instanceof Error ? err.message : String(err);
858
+ setState({ kind: "error", message });
859
+ flash(message, "error");
860
+ } finally {
861
+ setIsSavingAllowlist(false);
862
+ }
863
+ }, [agent.id, allowedModelsByProvider, allowedProviders, isSavingAllowlist, queryClient, runSaveModelAllowlistMutation, t]);
864
+ const busy = isSaving || isClearing || isSavingAllowlist;
865
+ return /* @__PURE__ */ jsxs(
866
+ "section",
867
+ {
868
+ className: "rounded-lg border border-border bg-background p-4",
869
+ "data-ai-agent-model-override": agent.id,
870
+ children: [
871
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
872
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
873
+ /* @__PURE__ */ jsx(Bot, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
874
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
875
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.model_override.title", "Provider and model") }),
876
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
877
+ "ai_assistant.agents.model_override.subtitle",
878
+ "Override the default provider and model for this tenant and agent."
879
+ ) })
880
+ ] })
881
+ ] }),
882
+ agentResolution ? /* @__PURE__ */ jsxs(
883
+ StatusBadge,
884
+ {
885
+ variant: "info",
886
+ dot: true,
887
+ className: "max-w-full whitespace-normal break-all",
888
+ "data-ai-agent-model-override-effective": true,
889
+ children: [
890
+ agentResolution.providerId,
891
+ " / ",
892
+ agentResolution.modelId
893
+ ]
894
+ }
895
+ ) : null
896
+ ] }),
897
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-3", children: [
898
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3", children: [
899
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
900
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.model_override.codeDefault", "Code-declared default") }),
901
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 break-all font-mono text-xs", children: [
902
+ agent.defaultProvider ?? t("ai_assistant.agents.model_override.anyProvider", "first configured"),
903
+ " / ",
904
+ agent.defaultModel ?? t("ai_assistant.agents.model_override.providerDefault", "provider default")
905
+ ] })
906
+ ] }),
907
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
908
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.model_override.tenantOverride", "Tenant override") }),
909
+ /* @__PURE__ */ jsx("p", { className: "mt-1 break-all font-mono text-xs text-muted-foreground", children: agentResolution?.override ? `${agentResolution.override.providerId ?? "\u2014"} / ${agentResolution.override.modelId ?? "\u2014"}` : t("ai_assistant.agents.model_override.noOverride", "No per-agent override") })
910
+ ] })
911
+ ] }),
912
+ settingsQuery.isLoading ? /* @__PURE__ */ jsx(
913
+ SettingsLoading,
914
+ {
915
+ message: t(
916
+ "ai_assistant.agents.model_override.loading",
917
+ "Loading provider catalog..."
918
+ )
919
+ }
920
+ ) : settingsQuery.isError ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-model-override-load-error": true, children: [
921
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
922
+ /* @__PURE__ */ jsx(AlertTitle, { children: t(
923
+ "ai_assistant.agents.model_override.loadErrorTitle",
924
+ "Failed to load provider catalog"
925
+ ) }),
926
+ /* @__PURE__ */ jsx(AlertDescription, { children: settingsQuery.error instanceof Error ? settingsQuery.error.message : String(settingsQuery.error) })
927
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-3", children: [
928
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
929
+ /* @__PURE__ */ jsx(Label, { htmlFor: `ai-agent-model-provider-${agent.id}`, className: "text-xs", children: t("ai_assistant.agents.model_override.provider", "Provider") }),
930
+ /* @__PURE__ */ jsxs(
931
+ Select,
932
+ {
933
+ value: selectedProviderId,
934
+ onValueChange: (value) => {
935
+ setSelectedProviderId(value);
936
+ setSelectedModelId("");
937
+ setState({ kind: "idle" });
938
+ },
939
+ disabled: busy || configuredProviders.length === 0,
940
+ children: [
941
+ /* @__PURE__ */ jsx(
942
+ SelectTrigger,
943
+ {
944
+ id: `ai-agent-model-provider-${agent.id}`,
945
+ className: "w-full min-w-0",
946
+ "data-ai-agent-model-provider-select": true,
947
+ children: /* @__PURE__ */ jsx(
948
+ SelectValue,
949
+ {
950
+ placeholder: t(
951
+ "ai_assistant.agents.model_override.selectProvider",
952
+ "Select provider"
953
+ )
954
+ }
955
+ )
956
+ }
957
+ ),
958
+ /* @__PURE__ */ jsx(SelectContent, { children: configuredProviders.map((provider) => /* @__PURE__ */ jsx(SelectItem, { value: provider.id, children: provider.name }, provider.id)) })
959
+ ]
960
+ }
961
+ )
962
+ ] }),
963
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
964
+ /* @__PURE__ */ jsx(Label, { htmlFor: `ai-agent-model-model-${agent.id}`, className: "text-xs", children: t("ai_assistant.agents.model_override.model", "Model") }),
965
+ /* @__PURE__ */ jsxs(
966
+ Select,
967
+ {
968
+ value: selectedModelId,
969
+ onValueChange: (value) => {
970
+ setSelectedModelId(value);
971
+ setState({ kind: "idle" });
972
+ },
973
+ disabled: busy || !selectedProvider,
974
+ children: [
975
+ /* @__PURE__ */ jsx(
976
+ SelectTrigger,
977
+ {
978
+ id: `ai-agent-model-model-${agent.id}`,
979
+ className: "w-full min-w-0",
980
+ "data-ai-agent-model-select": true,
981
+ children: /* @__PURE__ */ jsx(
982
+ SelectValue,
983
+ {
984
+ placeholder: t(
985
+ "ai_assistant.agents.model_override.selectModel",
986
+ "Select model"
987
+ )
988
+ }
989
+ )
990
+ }
991
+ ),
992
+ /* @__PURE__ */ jsx(SelectContent, { children: (selectedProvider?.defaultModels ?? []).map((model) => /* @__PURE__ */ jsx(SelectItem, { value: model.id, children: model.name }, model.id)) })
993
+ ]
994
+ }
995
+ )
996
+ ] })
997
+ ] }),
998
+ agent.allowRuntimeModelOverride ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/20 p-3", "data-ai-agent-model-picker-allowlist": true, children: [
999
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
1000
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1001
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: t(
1002
+ "ai_assistant.agents.model_override.allowlistTitle",
1003
+ "Chat override choices"
1004
+ ) }),
1005
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
1006
+ "ai_assistant.agents.model_override.allowlistHelp",
1007
+ "Limit which provider/model overrides users can pick in the chat footer for this agent."
1008
+ ) }),
1009
+ agentResolution?.runtimeOverrideAllowlist.env ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
1010
+ /* @__PURE__ */ jsx("code", { className: "font-mono text-xs", children: agentResolution.runtimeOverrideAllowlist.envVarNames.providers }),
1011
+ " ",
1012
+ t(
1013
+ "ai_assistant.agents.model_override.envAlsoNarrows",
1014
+ "also narrows this list from env."
1015
+ )
1016
+ ] }) : null
1017
+ ] }),
1018
+ /* @__PURE__ */ jsx(
1019
+ StatusBadge,
1020
+ {
1021
+ variant: agentResolution?.runtimeOverrideAllowlist.tenant ? "info" : "neutral",
1022
+ dot: true,
1023
+ children: agentResolution?.runtimeOverrideAllowlist.tenant ? t("ai_assistant.agents.model_override.allowlistCustom", "custom") : t("ai_assistant.agents.model_override.allowlistInherited", "inherited")
1024
+ }
1025
+ )
1026
+ ] }),
1027
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-col gap-3", children: configuredProviders.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
1028
+ "ai_assistant.agents.model_override.allowlistEmpty",
1029
+ "No configured providers are available for chat overrides."
1030
+ ) }) : configuredProviders.map((provider) => {
1031
+ const providerEnabled = isProviderAllowedForPicker(provider.id);
1032
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-background p-3", children: [
1033
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1034
+ /* @__PURE__ */ jsx(
1035
+ Checkbox,
1036
+ {
1037
+ id: `ai-agent-picker-provider-${agent.id}-${provider.id}`,
1038
+ checked: providerEnabled,
1039
+ onCheckedChange: (value) => toggleAllowedProvider(provider.id, value === true)
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsx(
1043
+ Label,
1044
+ {
1045
+ htmlFor: `ai-agent-picker-provider-${agent.id}-${provider.id}`,
1046
+ className: "text-sm font-medium",
1047
+ children: provider.name
1048
+ }
1049
+ )
1050
+ ] }),
1051
+ providerEnabled ? /* @__PURE__ */ jsx("div", { className: "mt-2 grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-2", children: provider.defaultModels.map((model) => /* @__PURE__ */ jsxs(
1052
+ "label",
1053
+ {
1054
+ className: "flex min-w-0 items-center gap-2 text-xs",
1055
+ children: [
1056
+ /* @__PURE__ */ jsx(
1057
+ Checkbox,
1058
+ {
1059
+ checked: isModelAllowedForPicker(provider.id, model.id),
1060
+ onCheckedChange: (value) => toggleAllowedModel(provider.id, model.id, value === true)
1061
+ }
1062
+ ),
1063
+ /* @__PURE__ */ jsx("span", { className: "truncate font-mono", children: model.id }),
1064
+ model.id === provider.defaultModel ? /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-overline", children: t("ai_assistant.agents.model_override.defaultBadge", "default") }) : null
1065
+ ]
1066
+ },
1067
+ model.id
1068
+ )) }) : null
1069
+ ] }, provider.id);
1070
+ }) }),
1071
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-end gap-2", children: [
1072
+ /* @__PURE__ */ jsx(
1073
+ Button,
1074
+ {
1075
+ type: "button",
1076
+ size: "sm",
1077
+ variant: "outline",
1078
+ onClick: resetAllowlistDraft,
1079
+ disabled: busy,
1080
+ children: t("ai_assistant.agents.model_override.allowlistReset", "Inherit")
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsxs(
1084
+ Button,
1085
+ {
1086
+ type: "button",
1087
+ size: "sm",
1088
+ onClick: () => void saveAllowlist(),
1089
+ disabled: busy || !allowlistDirty,
1090
+ "data-ai-agent-model-allowlist-save": true,
1091
+ children: [
1092
+ isSavingAllowlist ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Save, { className: "size-4", "aria-hidden": true }),
1093
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.model_override.allowlistSave", "Save choices") })
1094
+ ]
1095
+ }
1096
+ )
1097
+ ] })
1098
+ ] }) : null,
1099
+ state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { variant: "success", "data-ai-agent-model-override-state": "success", children: [
1100
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4", "aria-hidden": true }),
1101
+ /* @__PURE__ */ jsx(AlertDescription, { children: state.message })
1102
+ ] }) : null,
1103
+ state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-model-override-state": "error", children: [
1104
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
1105
+ /* @__PURE__ */ jsx(AlertDescription, { children: state.message })
1106
+ ] }) : null,
1107
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
1108
+ /* @__PURE__ */ jsxs(
1109
+ Button,
1110
+ {
1111
+ type: "button",
1112
+ size: "sm",
1113
+ variant: "outline",
1114
+ onClick: () => void clear(),
1115
+ disabled: busy || !agentResolution?.override,
1116
+ "data-ai-agent-model-clear": true,
1117
+ children: [
1118
+ isClearing ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true }),
1119
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.model_override.clear", "Clear override") })
1120
+ ]
1121
+ }
1122
+ ),
1123
+ /* @__PURE__ */ jsxs(
1124
+ Button,
1125
+ {
1126
+ type: "button",
1127
+ size: "sm",
1128
+ onClick: () => void save(),
1129
+ disabled: busy || !hasChange,
1130
+ "data-ai-agent-model-save": true,
1131
+ children: [
1132
+ isSaving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Save, { className: "size-4", "aria-hidden": true }),
1133
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.model_override.save", "Save override") })
1134
+ ]
1135
+ }
1136
+ )
1137
+ ] })
1138
+ ] })
1139
+ ]
1140
+ }
1141
+ );
1142
+ }
610
1143
  function AgentDetailPanel({ agent }) {
611
1144
  const t = useT();
612
1145
  const queryClient = useQueryClient();
@@ -632,6 +1165,9 @@ function AgentDetailPanel({ agent }) {
632
1165
  }));
633
1166
  const [isSaving, setIsSaving] = React.useState(false);
634
1167
  const [saveState, setSaveState] = React.useState({ kind: "idle" });
1168
+ const { runMutation: runSavePromptOverrideMutation } = useGuardedMutation({
1169
+ contextId: `ai-agent-prompt-override-save-${agent.id}`
1170
+ });
635
1171
  const overrideQuery = useQuery({
636
1172
  queryKey: ["ai_assistant", "agent_settings", "override", agent.id],
637
1173
  queryFn: () => fetchOverride(agent.id),
@@ -709,97 +1245,107 @@ function AgentDetailPanel({ agent }) {
709
1245
  setIsSaving(true);
710
1246
  setSaveState({ kind: "idle" });
711
1247
  try {
712
- const { ok, status, result } = await apiCall(
713
- `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/prompt-override`,
714
- {
715
- method: "POST",
716
- headers: { "content-type": "application/json" },
717
- credentials: "include",
718
- body: JSON.stringify({
719
- // Send both keys so a pre-Step-5.3 server still accepts the payload.
720
- sections: activeOverrides,
721
- overrides: activeOverrides
722
- })
723
- }
724
- );
725
- const payload = result ?? {};
726
- if (!ok) {
727
- const message = payload.code === "reserved_key" ? t(
728
- "ai_assistant.agents.override.errors.reservedKey",
729
- "Prompt overrides cannot modify policy fields (mutationPolicy, readOnly, allowedTools, acceptedMediaTypes). Remove those sections and retry."
730
- ) : payload.error ?? `Failed to save overrides (${status}).`;
731
- setSaveState({ kind: "error", message });
732
- return;
733
- }
1248
+ const payload = await runSavePromptOverrideMutation({
1249
+ operation: async () => {
1250
+ const { ok, status, result } = await apiCall(
1251
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/prompt-override`,
1252
+ {
1253
+ method: "POST",
1254
+ headers: { "content-type": "application/json" },
1255
+ credentials: "include",
1256
+ body: JSON.stringify({
1257
+ // Send both keys so a pre-Step-5.3 server still accepts the payload.
1258
+ sections: activeOverrides,
1259
+ overrides: activeOverrides
1260
+ })
1261
+ }
1262
+ );
1263
+ const body = result ?? {};
1264
+ if (!ok) {
1265
+ const message = body.code === "reserved_key" ? t(
1266
+ "ai_assistant.agents.override.errors.reservedKey",
1267
+ "Prompt overrides cannot modify policy fields (mutationPolicy, readOnly, allowedTools, acceptedMediaTypes). Remove those sections and retry."
1268
+ ) : body.error ?? `Failed to save overrides (${status}).`;
1269
+ throw new Error(message);
1270
+ }
1271
+ return body;
1272
+ },
1273
+ context: {}
1274
+ });
734
1275
  if (payload.ok === true && typeof payload.version === "number") {
1276
+ const successMessage = t(
1277
+ "ai_assistant.agents.override.savedMessage",
1278
+ "Prompt override saved."
1279
+ );
735
1280
  setSaveState({
736
1281
  kind: "success",
737
1282
  version: payload.version,
738
- message: t(
739
- "ai_assistant.agents.override.savedMessage",
740
- "Prompt override saved."
741
- )
1283
+ message: successMessage
742
1284
  });
1285
+ flash(successMessage, "success");
743
1286
  await queryClient.invalidateQueries({
744
1287
  queryKey: ["ai_assistant", "agent_settings", "override", agent.id]
745
1288
  });
746
1289
  return;
747
1290
  }
1291
+ const legacyMessage = payload.message ?? t(
1292
+ "ai_assistant.agents.prompt.pendingMessage",
1293
+ "Prompt overrides accepted."
1294
+ );
748
1295
  setSaveState({
749
1296
  kind: "success",
750
1297
  version: 0,
751
- message: payload.message ?? t(
752
- "ai_assistant.agents.prompt.pendingMessage",
753
- "Prompt overrides accepted."
754
- )
1298
+ message: legacyMessage
755
1299
  });
1300
+ flash(legacyMessage, "success");
756
1301
  } catch (err) {
757
- setSaveState({
758
- kind: "error",
759
- message: err instanceof Error ? err.message : String(err)
760
- });
1302
+ const message = err instanceof Error ? err.message : String(err);
1303
+ setSaveState({ kind: "error", message });
1304
+ flash(message, "error");
761
1305
  } finally {
762
1306
  setIsSaving(false);
763
1307
  }
764
- }, [activeOverrides, agent.id, isSaving, queryClient, t]);
1308
+ }, [activeOverrides, agent.id, isSaving, queryClient, runSavePromptOverrideMutation, t]);
765
1309
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", "data-ai-agent-detail": agent.id, children: [
766
1310
  /* @__PURE__ */ jsxs("section", { className: "rounded-lg border border-border bg-background p-4", children: [
767
1311
  /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold", children: agent.label }),
768
1312
  /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: agent.description }),
769
- /* @__PURE__ */ jsxs("dl", { className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4 text-sm", children: [
770
- /* @__PURE__ */ jsxs("div", { children: [
1313
+ /* @__PURE__ */ jsxs("dl", { className: "mt-4 grid grid-cols-[repeat(auto-fit,minmax(min(100%,9rem),1fr))] gap-3 text-sm", children: [
1314
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
771
1315
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.module", "Module") }),
772
- /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.moduleId })
1316
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 break-all font-mono text-xs", children: agent.moduleId })
773
1317
  ] }),
774
- /* @__PURE__ */ jsxs("div", { children: [
1318
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
775
1319
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.id", "Agent id") }),
776
- /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.id })
1320
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 break-all font-mono text-xs", children: agent.id })
777
1321
  ] }),
778
- /* @__PURE__ */ jsxs("div", { children: [
1322
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
779
1323
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.executionMode", "Execution mode") }),
780
1324
  /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsx(StatusBadge, { variant: executionModeStatusMap[agent.executionMode], children: agent.executionMode }) })
781
1325
  ] }),
782
- /* @__PURE__ */ jsxs("div", { children: [
1326
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
783
1327
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.mutationPolicy", "Mutation policy") }),
784
1328
  /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsx(
785
1329
  StatusBadge,
786
1330
  {
787
1331
  variant: mutationPolicyStatusMap[agent.mutationPolicy] ?? "neutral",
788
1332
  dot: true,
1333
+ className: "max-w-full whitespace-normal break-all",
789
1334
  children: agent.mutationPolicy
790
1335
  }
791
1336
  ) })
792
1337
  ] }),
793
- /* @__PURE__ */ jsxs("div", { children: [
1338
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
794
1339
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.readOnly", "Read-only") }),
795
1340
  /* @__PURE__ */ jsx("dd", { className: "mt-1 text-xs", children: agent.readOnly ? t("ai_assistant.agents.meta.readOnlyYes", "Yes") : t("ai_assistant.agents.meta.readOnlyNo", "No") })
796
1341
  ] }),
797
- /* @__PURE__ */ jsxs("div", { children: [
1342
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
798
1343
  /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.maxSteps", "Max steps") }),
799
1344
  /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.maxSteps ?? t("ai_assistant.agents.meta.unlimited", "Unlimited") })
800
1345
  ] })
801
1346
  ] })
802
1347
  ] }),
1348
+ /* @__PURE__ */ jsx(AgentModelOverrideSection, { agent }),
803
1349
  /* @__PURE__ */ jsx(MutationPolicySection, { agent }),
804
1350
  /* @__PURE__ */ jsxs(
805
1351
  "section",
@@ -807,8 +1353,8 @@ function AgentDetailPanel({ agent }) {
807
1353
  className: "rounded-lg border border-border bg-background p-4",
808
1354
  "data-ai-agent-prompt-editor": agent.id,
809
1355
  children: [
810
- /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
811
- /* @__PURE__ */ jsxs("div", { children: [
1356
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
1357
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
812
1358
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.prompt.title", "Prompt sections") }),
813
1359
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
814
1360
  "ai_assistant.agents.prompt.subtitle",
@@ -888,8 +1434,8 @@ function AgentDetailPanel({ agent }) {
888
1434
  className: "rounded-lg border border-border bg-background p-4",
889
1435
  "data-ai-agent-tools-list": agent.id,
890
1436
  children: [
891
- /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
892
- /* @__PURE__ */ jsxs("div", { children: [
1437
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
1438
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
893
1439
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.tools.title", "Allowed tools") }),
894
1440
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
895
1441
  "ai_assistant.agents.tools.subtitle",
@@ -911,10 +1457,10 @@ function AgentDetailPanel({ agent }) {
911
1457
  className: "rounded-lg border border-border bg-background p-4",
912
1458
  "data-ai-agent-override-history": agent.id,
913
1459
  children: [
914
- /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
915
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1460
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: [
1461
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
916
1462
  /* @__PURE__ */ jsx(History, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
917
- /* @__PURE__ */ jsxs("div", { children: [
1463
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
918
1464
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.override.history.title", "Prompt override history") }),
919
1465
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
920
1466
  "ai_assistant.agents.override.history.subtitle",
@@ -952,7 +1498,7 @@ function AgentDetailPanel({ agent }) {
952
1498
  ) : (overrideQuery.data?.versions ?? []).slice(0, 5).map((entry) => /* @__PURE__ */ jsxs(
953
1499
  "div",
954
1500
  {
955
- className: "flex items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
1501
+ className: "flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
956
1502
  "data-ai-agent-override-history-row": entry.version,
957
1503
  children: [
958
1504
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
@@ -981,7 +1527,7 @@ function AgentDetailPanel({ agent }) {
981
1527
  className: "rounded-lg border border-border bg-background p-4",
982
1528
  "data-ai-agent-attachments": agent.id,
983
1529
  children: [
984
- /* @__PURE__ */ jsx("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: /* @__PURE__ */ jsxs("div", { children: [
1530
+ /* @__PURE__ */ jsx("header", { className: "flex flex-wrap items-start justify-between gap-3 border-b border-border pb-3", children: /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
985
1531
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.attachments.title", "Attachment policy") }),
986
1532
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
987
1533
  "ai_assistant.agents.attachments.subtitle",
@@ -1050,8 +1596,8 @@ function AiAgentSettingsPageClient() {
1050
1596
  if (!agents.length) {
1051
1597
  return /* @__PURE__ */ jsx(EmptyAgents, {});
1052
1598
  }
1053
- return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", "data-ai-agent-settings": true, children: [
1054
- /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
1599
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-4", "data-ai-agent-settings": true, children: [
1600
+ /* @__PURE__ */ jsxs("header", { className: "flex min-w-0 flex-col gap-1", children: [
1055
1601
  /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold tracking-tight", children: t("ai_assistant.agents.title", "AI Agents") }),
1056
1602
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t(
1057
1603
  "ai_assistant.agents.subtitle",
@@ -1063,23 +1609,31 @@ function AiAgentSettingsPageClient() {
1063
1609
  {
1064
1610
  className: "flex flex-col gap-3 rounded-lg border border-border bg-background p-3",
1065
1611
  "data-ai-agent-settings-picker-wrap": true,
1066
- children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
1067
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-1", children: [
1612
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end justify-between gap-3", children: [
1613
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-[min(100%,16rem)] flex-1 flex-col gap-2", children: [
1068
1614
  /* @__PURE__ */ jsx(Label, { htmlFor: "ai-agent-settings-picker", children: t("ai_assistant.agents.agentPickerLabel", "Agent") }),
1069
- /* @__PURE__ */ jsx(
1070
- "select",
1615
+ /* @__PURE__ */ jsxs(
1616
+ Select,
1071
1617
  {
1072
- id: "ai-agent-settings-picker",
1073
- "data-ai-agent-settings-picker": true,
1074
- className: "h-9 rounded-md border border-input bg-background px-3 text-sm",
1075
1618
  value: selectedAgentId ?? "",
1076
- onChange: (event) => setSelectedAgentId(event.target.value),
1077
- children: agents.map((agent) => /* @__PURE__ */ jsxs("option", { value: agent.id, children: [
1078
- agent.label,
1079
- " (",
1080
- agent.id,
1081
- ")"
1082
- ] }, agent.id))
1619
+ onValueChange: (value) => setSelectedAgentId(value),
1620
+ children: [
1621
+ /* @__PURE__ */ jsx(
1622
+ SelectTrigger,
1623
+ {
1624
+ id: "ai-agent-settings-picker",
1625
+ "data-ai-agent-settings-picker": true,
1626
+ className: "w-full min-w-0",
1627
+ children: /* @__PURE__ */ jsx(SelectValue, {})
1628
+ }
1629
+ ),
1630
+ /* @__PURE__ */ jsx(SelectContent, { children: agents.map((agent) => /* @__PURE__ */ jsxs(SelectItem, { value: agent.id, children: [
1631
+ agent.label,
1632
+ " (",
1633
+ agent.id,
1634
+ ")"
1635
+ ] }, agent.id)) })
1636
+ ]
1083
1637
  }
1084
1638
  )
1085
1639
  ] }),