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:
@@ -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,13 +381,48 @@ 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")}>
|
<div className={cn(isCreate ? "border-t border-border" : "border-b border-border")}>
|
||||||
<Field label="Adapter" hint={help.adapterType}>
|
<div className="px-4 py-2 flex items-center justify-between gap-2">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">
|
||||||
|
Adapter
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 px-2.5 text-xs"
|
||||||
|
onClick={() => testEnvironment.mutate()}
|
||||||
|
disabled={testEnvironment.isPending || !selectedCompanyId}
|
||||||
|
>
|
||||||
|
{testEnvironment.isPending ? "Testing..." : "Test environment"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 pb-3 space-y-3">
|
||||||
|
<Field label="Adapter type" hint={help.adapterType}>
|
||||||
<AdapterTypeDropdown
|
<AdapterTypeDropdown
|
||||||
value={adapterType}
|
value={adapterType}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
@@ -402,26 +438,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ---- Adapter Configuration ---- */}
|
|
||||||
<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">
|
|
||||||
<span className="text-xs font-medium text-muted-foreground">
|
|
||||||
Adapter Configuration
|
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="h-7 px-2.5 text-xs"
|
|
||||||
onClick={() => testEnvironment.mutate()}
|
|
||||||
disabled={testEnvironment.isPending || !selectedCompanyId}
|
|
||||||
>
|
|
||||||
{testEnvironment.isPending ? "Testing..." : "Test environment"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="px-4 pb-3 space-y-3">
|
|
||||||
{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)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Edit-only: wake-on-* and cooldown */}
|
|
||||||
<div className="space-y-3 pt-2 border-t border-border/50">
|
|
||||||
<div className="text-[10px] font-medium text-muted-foreground/60 uppercase tracking-wider">
|
|
||||||
Advanced
|
|
||||||
</div>
|
</div>
|
||||||
|
<CollapsibleSection
|
||||||
|
title="Advanced Run Policy"
|
||||||
|
open={runPolicyAdvancedOpen}
|
||||||
|
onToggle={() => setRunPolicyAdvancedOpen(!runPolicyAdvancedOpen)}
|
||||||
|
>
|
||||||
|
<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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user