@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +82 -18
- package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js +370 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js +194 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/route.js +4 -0
- package/dist/modules/ai_assistant/api/ai/agents/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/chat/route.js +169 -5
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/route/route.js +38 -19
- package/dist/modules/ai_assistant/api/route/route.js.map +3 -3
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js +195 -0
- package/dist/modules/ai_assistant/api/settings/allowlist/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/route.js +537 -22
- package/dist/modules/ai_assistant/api/settings/route.js.map +3 -3
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +701 -147
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +338 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.js +25 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js +1 -1
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +75 -26
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.js +25 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js +503 -168
- package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.js +5 -0
- package/dist/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities/AiTenantModelAllowlist.js +5 -0
- package/dist/modules/ai_assistant/data/entities/AiTenantModelAllowlist.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities.js +123 -1
- package/dist/modules/ai_assistant/data/entities.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +157 -0
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +7 -0
- package/dist/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.js +77 -0
- package/dist/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.js.map +7 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +1 -1
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/i18n/de.json +90 -1
- package/dist/modules/ai_assistant/i18n/en.json +90 -1
- package/dist/modules/ai_assistant/i18n/es.json +90 -1
- package/dist/modules/ai_assistant/i18n/pl.json +90 -1
- package/dist/modules/ai_assistant/lib/agent-registry.js +17 -1
- package/dist/modules/ai_assistant/lib/agent-registry.js.map +2 -2
- package/dist/modules/ai_assistant/lib/agent-runtime.js +133 -36
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +2 -2
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
- package/dist/modules/ai_assistant/lib/baseurl-allowlist.js +29 -0
- package/dist/modules/ai_assistant/lib/baseurl-allowlist.js.map +7 -0
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js +4 -1
- package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js.map +2 -2
- package/dist/modules/ai_assistant/lib/llm-adapters/google.js +4 -1
- package/dist/modules/ai_assistant/lib/llm-adapters/google.js.map +2 -2
- package/dist/modules/ai_assistant/lib/model-allowlist.js +211 -0
- package/dist/modules/ai_assistant/lib/model-allowlist.js.map +7 -0
- package/dist/modules/ai_assistant/lib/model-factory.js +203 -31
- package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js +32 -1
- package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +2 -2
- package/dist/modules/ai_assistant/migrations/Migration20260508140000.js +18 -0
- package/dist/modules/ai_assistant/migrations/Migration20260508140000.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260512090000.js +16 -0
- package/dist/modules/ai_assistant/migrations/Migration20260512090000.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260512130000.js +15 -0
- package/dist/modules/ai_assistant/migrations/Migration20260512130000.js.map +7 -0
- package/generated/entities/ai_agent_runtime_override/index.ts +13 -0
- package/generated/entities/ai_tenant_model_allowlist/index.ts +9 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +26 -0
- package/jest.config.cjs +2 -0
- package/package.json +4 -4
- package/src/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.ts +477 -0
- package/src/modules/ai_assistant/__tests__/settings-page-logic.test.ts +116 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/__tests__/route.test.ts +240 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/route.ts +251 -0
- package/src/modules/ai_assistant/api/ai/agents/route.ts +4 -0
- package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +273 -0
- package/src/modules/ai_assistant/api/ai/chat/route.ts +211 -2
- package/src/modules/ai_assistant/api/route/route.ts +49 -25
- package/src/modules/ai_assistant/api/settings/__tests__/route.test.ts +408 -0
- package/src/modules/ai_assistant/api/settings/allowlist/route.ts +221 -0
- package/src/modules/ai_assistant/api/settings/route.ts +721 -27
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +858 -177
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +458 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.meta.ts +23 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/page.tsx +12 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.tsx +1 -1
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +89 -12
- package/src/modules/ai_assistant/backend/config/ai-assistant/settings/page.meta.ts +23 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/settings/page.tsx +18 -0
- package/src/modules/ai_assistant/components/AiAssistantSettingsPageClient.tsx +617 -209
- package/src/modules/ai_assistant/data/entities/AiAgentRuntimeOverride.ts +7 -0
- package/src/modules/ai_assistant/data/entities/AiTenantModelAllowlist.ts +2 -0
- package/src/modules/ai_assistant/data/entities.ts +164 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +227 -0
- package/src/modules/ai_assistant/data/repositories/AiTenantModelAllowlistRepository.ts +132 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentRuntimeOverrideRepository.test.ts +337 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiTenantModelAllowlistRepository.test.ts +181 -0
- package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +1 -1
- package/src/modules/ai_assistant/i18n/de.json +90 -1
- package/src/modules/ai_assistant/i18n/en.json +90 -1
- package/src/modules/ai_assistant/i18n/es.json +90 -1
- package/src/modules/ai_assistant/i18n/pl.json +90 -1
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-phase4a.test.ts +396 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +60 -6
- package/src/modules/ai_assistant/lib/__tests__/ai-api-operation-runner.test.ts +4 -2
- package/src/modules/ai_assistant/lib/__tests__/baseurl-allowlist.test.ts +75 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-anthropic.test.ts +18 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-google.test.ts +18 -0
- package/src/modules/ai_assistant/lib/__tests__/llm-adapters-openai.test.ts +150 -4
- package/src/modules/ai_assistant/lib/__tests__/model-allowlist.test.ts +290 -0
- package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +634 -0
- package/src/modules/ai_assistant/lib/agent-registry.ts +20 -1
- package/src/modules/ai_assistant/lib/agent-runtime.ts +220 -44
- package/src/modules/ai_assistant/lib/ai-agent-definition.ts +48 -0
- package/src/modules/ai_assistant/lib/baseurl-allowlist.ts +64 -0
- package/src/modules/ai_assistant/lib/llm-adapters/anthropic.ts +11 -1
- package/src/modules/ai_assistant/lib/llm-adapters/google.ts +4 -1
- package/src/modules/ai_assistant/lib/model-allowlist.ts +407 -0
- package/src/modules/ai_assistant/lib/model-factory.ts +486 -58
- package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +44 -0
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +704 -235
- package/src/modules/ai_assistant/migrations/Migration20260508140000.ts +18 -0
- package/src/modules/ai_assistant/migrations/Migration20260512090000.ts +16 -0
- package/src/modules/ai_assistant/migrations/Migration20260512130000.ts +13 -0
package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js
CHANGED
|
@@ -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-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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-
|
|
405
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
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-
|
|
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
|
-
|
|
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
|
-
"
|
|
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-
|
|
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
|
-
|
|
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
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
915
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
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-
|
|
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-
|
|
1067
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2
|
|
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__ */
|
|
1070
|
-
|
|
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
|
-
|
|
1077
|
-
children:
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
] }),
|