feat: reorganize agent config form — Identity, Adapter, Run Policy panes

Move Prompt Template into Identity section, merge adapter type dropdown
into a unified Adapter pane, and rename Heartbeat Policy to Run Policy
with a collapsible Advanced Run Policy section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-20 14:11:30 -06:00
parent 4c8a1f4513
commit 38162dc317

View File

@@ -271,6 +271,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
// Section toggle state — advanced always starts collapsed // Section toggle state — advanced always starts collapsed
const [adapterAdvancedOpen, setAdapterAdvancedOpen] = useState(false); const [adapterAdvancedOpen, setAdapterAdvancedOpen] = useState(false);
const [runPolicyAdvancedOpen, setRunPolicyAdvancedOpen] = useState(false);
const [heartbeatOpen, setHeartbeatOpen] = useState(!isCreate); const [heartbeatOpen, setHeartbeatOpen] = useState(!isCreate);
const [cwdPickerNotice, setCwdPickerNotice] = useState<string | null>(null); const [cwdPickerNotice, setCwdPickerNotice] = useState<string | null>(null);
@@ -380,35 +381,34 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
}} }}
/> />
</Field> </Field>
{isLocal && (
<Field label="Prompt Template" hint={help.promptTemplate}>
<MarkdownEditor
value={eff(
"adapterConfig",
"promptTemplate",
String(config.promptTemplate ?? ""),
)}
onChange={(v) => mark("adapterConfig", "promptTemplate", v || undefined)}
placeholder="You are agent {{ agent.name }}. Your role is {{ agent.role }}..."
contentClassName="min-h-[88px] text-sm font-mono"
imageUploadHandler={async (file) => {
const namespace = `agents/${props.agent.id}/prompt-template`;
const asset = await uploadMarkdownImage.mutateAsync({ file, namespace });
return asset.contentPath;
}}
/>
</Field>
)}
</div> </div>
</div> </div>
)} )}
{/* ---- Adapter type ---- */} {/* ---- Adapter ---- */}
<div className={cn("px-4 py-2.5", isCreate ? "border-t border-border" : "border-b border-border")}>
<Field label="Adapter" hint={help.adapterType}>
<AdapterTypeDropdown
value={adapterType}
onChange={(t) => {
if (isCreate) {
set!({ adapterType: t, model: "", thinkingEffort: "" });
} else {
setOverlay((prev) => ({
...prev,
adapterType: t,
adapterConfig: {}, // clear adapter config when type changes
}));
}
}}
/>
</Field>
</div>
{/* ---- Adapter Configuration ---- */}
<div className={cn(isCreate ? "border-t border-border" : "border-b border-border")}> <div className={cn(isCreate ? "border-t border-border" : "border-b border-border")}>
<div className="px-4 py-2 flex items-center justify-between gap-2"> <div className="px-4 py-2 flex items-center justify-between gap-2">
<span className="text-xs font-medium text-muted-foreground"> <span className="text-xs font-medium text-muted-foreground">
Adapter Configuration Adapter
</span> </span>
<Button <Button
type="button" type="button"
@@ -422,6 +422,23 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
</Button> </Button>
</div> </div>
<div className="px-4 pb-3 space-y-3"> <div className="px-4 pb-3 space-y-3">
<Field label="Adapter type" hint={help.adapterType}>
<AdapterTypeDropdown
value={adapterType}
onChange={(t) => {
if (isCreate) {
set!({ adapterType: t, model: "", thinkingEffort: "" });
} else {
setOverlay((prev) => ({
...prev,
adapterType: t,
adapterConfig: {}, // clear adapter config when type changes
}));
}
}}
/>
</Field>
{testEnvironment.error && ( {testEnvironment.error && (
<div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive"> <div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
{testEnvironment.error instanceof Error {testEnvironment.error instanceof Error
@@ -491,30 +508,16 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
</Field> </Field>
)} )}
{/* Prompt template */} {/* Prompt template (create mode only — edit mode shows this in Identity) */}
{isLocal && ( {isLocal && isCreate && (
<Field label="Prompt template" hint={help.promptTemplate}> <Field label="Prompt Template" hint={help.promptTemplate}>
<MarkdownEditor <MarkdownEditor
value={ value={val!.promptTemplate}
isCreate onChange={(v) => set!({ promptTemplate: v })}
? val!.promptTemplate
: eff(
"adapterConfig",
"promptTemplate",
String(config.promptTemplate ?? ""),
)
}
onChange={(v) =>
isCreate
? set!({ promptTemplate: v })
: mark("adapterConfig", "promptTemplate", v || undefined)
}
placeholder="You are agent {{ agent.name }}. Your role is {{ agent.role }}..." placeholder="You are agent {{ agent.name }}. Your role is {{ agent.role }}..."
contentClassName="min-h-[88px] text-sm font-mono" contentClassName="min-h-[88px] text-sm font-mono"
imageUploadHandler={async (file) => { imageUploadHandler={async (file) => {
const namespace = isCreate const namespace = "agents/drafts/prompt-template";
? "agents/drafts/prompt-template"
: `agents/${props.agent.id}/prompt-template`;
const asset = await uploadMarkdownImage.mutateAsync({ file, namespace }); const asset = await uploadMarkdownImage.mutateAsync({ file, namespace });
return asset.contentPath; return asset.contentPath;
}} }}
@@ -686,10 +689,10 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
)} )}
</div> </div>
{/* ---- Heartbeat Policy ---- */} {/* ---- Run Policy ---- */}
{isCreate ? ( {isCreate ? (
<CollapsibleSection <CollapsibleSection
title="Heartbeat Policy" title="Run Policy"
icon={<Heart className="h-3 w-3" />} icon={<Heart className="h-3 w-3" />}
open={heartbeatOpen} open={heartbeatOpen}
onToggle={() => setHeartbeatOpen(!heartbeatOpen)} onToggle={() => setHeartbeatOpen(!heartbeatOpen)}
@@ -714,7 +717,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
<div className="border-b border-border"> <div className="border-b border-border">
<div className="px-4 py-2 text-xs font-medium text-muted-foreground flex items-center gap-2"> <div className="px-4 py-2 text-xs font-medium text-muted-foreground flex items-center gap-2">
<Heart className="h-3 w-3" /> <Heart className="h-3 w-3" />
Heartbeat Policy Run Policy
</div> </div>
<div className="px-4 pb-3 space-y-3"> <div className="px-4 pb-3 space-y-3">
<ToggleWithNumber <ToggleWithNumber
@@ -729,12 +732,13 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
numberHint={help.intervalSec} numberHint={help.intervalSec}
showNumber={eff("heartbeat", "enabled", heartbeat.enabled !== false)} showNumber={eff("heartbeat", "enabled", heartbeat.enabled !== false)}
/> />
</div>
{/* Edit-only: wake-on-* and cooldown */} <CollapsibleSection
<div className="space-y-3 pt-2 border-t border-border/50"> title="Advanced Run Policy"
<div className="text-[10px] font-medium text-muted-foreground/60 uppercase tracking-wider"> open={runPolicyAdvancedOpen}
Advanced onToggle={() => setRunPolicyAdvancedOpen(!runPolicyAdvancedOpen)}
</div> >
<div className="space-y-3">
<ToggleField <ToggleField
label="Wake on demand" label="Wake on demand"
hint={help.wakeOnDemand} hint={help.wakeOnDemand}
@@ -770,7 +774,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
/> />
</Field> </Field>
</div> </div>
</div> </CollapsibleSection>
</div> </div>
)} )}