From 6481dd9f83dc9208ab60c1e60c80d743607d6524 Mon Sep 17 00:00:00 2001 From: Cascade Bot Date: Sun, 1 Mar 2026 11:24:05 +0000 Subject: [PATCH 1/5] feat(settings): replace dialog with full-screen agent definition editor Replace the narrow AgentDefinitionFormDialog modal with a full-page AgentDefinitionEditor component that follows the existing PromptEditor pattern. The editor has three tabs: Definition (form fields), System Prompt (prompt editing, edit mode only), and Raw JSON. Remove the separate Prompts tab from the definitions page since prompt editing is now integrated into the editor. Update AgentDefinitionsTable to accept an onEdit callback instead of managing internal dialog state. Co-Authored-By: Claude Opus 4.6 --- .../settings/agent-definition-editor.tsx | 932 ++++++++++++++++++ .../settings/agent-definition-table.tsx | 223 ++--- web/src/components/settings/prompt-editor.tsx | 2 +- web/src/routes/settings/definitions.tsx | 113 +-- 4 files changed, 1088 insertions(+), 182 deletions(-) create mode 100644 web/src/components/settings/agent-definition-editor.tsx diff --git a/web/src/components/settings/agent-definition-editor.tsx b/web/src/components/settings/agent-definition-editor.tsx new file mode 100644 index 00000000..97a9dc53 --- /dev/null +++ b/web/src/components/settings/agent-definition-editor.tsx @@ -0,0 +1,932 @@ +import type { AppRouter } from '@/../../src/api/router.js'; +import { Badge } from '@/components/ui/badge.js'; +import { Input } from '@/components/ui/input.js'; +import { Label } from '@/components/ui/label.js'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select.js'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs.js'; +import { Textarea } from '@/components/ui/textarea.js'; +import { trpc, trpcClient } from '@/lib/trpc.js'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import type { inferRouterOutputs } from '@trpc/server'; +import { useEffect, useState } from 'react'; +import { ReferencePanel } from './prompt-editor.js'; + +// ───────────────────────────────────────────────────────────────────────────── +// Types +// ───────────────────────────────────────────────────────────────────────────── + +type RouterOutput = inferRouterOutputs; +type DefinitionRow = RouterOutput['agentDefinitions']['list'][number]; +type AgentDefinition = DefinitionRow['definition']; + +export interface AgentDefinitionEditorProps { + /** When provided, we are editing an existing definition. When undefined, we are creating a new one. */ + existing?: DefinitionRow; + onClose: () => void; +} + +interface SchemaData { + toolSetNames: readonly string[]; + sdkToolsNames: readonly string[]; + contextStepNames: readonly string[]; + taskPromptBuilderNames: readonly string[]; + gadgetBuilderNames: readonly string[]; + compactionNames: readonly string[]; +} + +// ───────────────────────────────────────────────────────────────────────────── +// Helper components (shared with form dialog) +// ───────────────────────────────────────────────────────────────────────────── + +function Toggle({ + checked, + onChange, + label, +}: { + checked: boolean; + onChange: (v: boolean) => void; + label: string; +}) { + return ( +
+ + {label} +
+ ); +} + +function MultiSelectBadges({ + available, + selected, + onChange, +}: { + available: readonly string[]; + selected: string[]; + onChange: (v: string[]) => void; +}) { + const toggle = (item: string) => { + if (selected.includes(item)) { + onChange(selected.filter((s) => s !== item)); + } else { + onChange([...selected, item]); + } + }; + return ( +
+ {available.map((item) => ( + + ))} +
+ ); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Form section sub-components +// ───────────────────────────────────────────────────────────────────────────── + +function IdentitySection({ + def, + setIdentity, +}: { + def: AgentDefinition; + setIdentity: (k: keyof AgentDefinition['identity'], v: string) => void; +}) { + return ( +
+

+ Identity +

+
+
+ + setIdentity('emoji', e.target.value)} + placeholder="🤖" + /> +
+
+ + setIdentity('label', e.target.value)} + placeholder="My Agent" + /> +
+
+
+ +