fix: make name/emoji/persona optional, auto-skip empty steps, back button

- Make name, emoji, persona params optional in dedicated-channel-agent
- Auto-skip steps whose template args resolve to empty strings
- Move "Back to Recipes" to a back arrow next to the title

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhixian
2026-02-17 23:18:20 +09:00
parent dbf1e69937
commit a373a7c068
3 changed files with 30 additions and 13 deletions

View File

@@ -11,9 +11,9 @@
{ "id": "agent_id", "label": "Agent ID", "type": "string", "required": true, "placeholder": "e.g. my-bot" }, { "id": "agent_id", "label": "Agent ID", "type": "string", "required": true, "placeholder": "e.g. my-bot" },
{ "id": "guild_id", "label": "Guild", "type": "discord_guild", "required": true }, { "id": "guild_id", "label": "Guild", "type": "discord_guild", "required": true },
{ "id": "channel_id", "label": "Channel", "type": "discord_channel", "required": true }, { "id": "channel_id", "label": "Channel", "type": "discord_channel", "required": true },
{ "id": "name", "label": "Display Name", "type": "string", "required": true, "placeholder": "e.g. MyBot" }, { "id": "name", "label": "Display Name", "type": "string", "required": false, "placeholder": "e.g. MyBot" },
{ "id": "emoji", "label": "Emoji", "type": "string", "required": false, "placeholder": "e.g. \ud83e\udd16" }, { "id": "emoji", "label": "Emoji", "type": "string", "required": false, "placeholder": "e.g. \ud83e\udd16" },
{ "id": "persona", "label": "Persona", "type": "textarea", "required": true, "placeholder": "You are..." } { "id": "persona", "label": "Persona", "type": "textarea", "required": false, "placeholder": "You are..." }
], ],
"steps": [ "steps": [
{ "action": "create_agent", "label": "Create independent agent", "args": { "agentId": "{{agent_id}}", "independent": true } }, { "action": "create_agent", "label": "Create independent agent", "args": { "agentId": "{{agent_id}}", "independent": true } },

View File

@@ -81,6 +81,7 @@ export interface ResolvedStep {
label: string; label: string;
args: Record<string, unknown>; args: Record<string, unknown>;
description: string; description: string;
skippable: boolean;
} }
export function resolveSteps( export function resolveSteps(
@@ -92,6 +93,14 @@ export function resolveSteps(
if (step.action === "config_patch") { if (step.action === "config_patch") {
resolved.params = params; resolved.params = params;
} }
// A step is skippable if any of its template args resolved to empty string
const skippable = Object.entries(step.args).some(([key, origValue]) => {
if (typeof origValue === "string" && origValue.includes("{{")) {
const rv = resolved[key];
return typeof rv === "string" && rv.trim() === "";
}
return false;
});
const actionDef = getAction(step.action); const actionDef = getAction(step.action);
const description = actionDef?.describe(resolved) || step.label; const description = actionDef?.describe(resolved) || step.label;
return { return {
@@ -100,6 +109,7 @@ export function resolveSteps(
label: step.label, label: step.label,
args: resolved, args: resolved,
description: description || step.label, description: description || step.label,
skippable,
}; };
}); });
} }

View File

@@ -48,9 +48,10 @@ export function Cook({
const handleNext = () => { const handleNext = () => {
const steps = resolveSteps(recipe.steps, params); const steps = resolveSteps(recipe.steps, params);
setResolvedStepList(steps); setResolvedStepList(steps);
setStepStatuses(steps.map(() => "pending")); // Auto-skip steps whose template args resolved to empty
setStepStatuses(steps.map((s) => (s.skippable ? "skipped" : "pending")));
setStepErrors({}); setStepErrors({});
setHasConfigPatch(steps.some((s) => s.action === "config_patch")); setHasConfigPatch(steps.some((s) => !s.skippable && s.action === "config_patch"));
setPhase("confirm"); setPhase("confirm");
}; };
@@ -75,8 +76,7 @@ export function Cook({
const handleExecute = () => { const handleExecute = () => {
setPhase("execute"); setPhase("execute");
const statuses: StepStatus[] = resolvedStepList.map(() => "pending"); const statuses = [...stepStatuses];
setStepStatuses([...statuses]);
runFrom(0, statuses); runFrom(0, statuses);
}; };
@@ -131,7 +131,12 @@ export function Cook({
return ( return (
<section> <section>
<h2 className="text-2xl font-bold mb-4">{recipe.name}</h2> <div className="flex items-center gap-2 mb-4">
<Button variant="ghost" size="sm" className="px-2" onClick={onDone}>
&larr;
</Button>
<h2 className="text-2xl font-bold">{recipe.name}</h2>
</div>
{phase === "params" && ( {phase === "params" && (
<ParamForm <ParamForm
@@ -149,13 +154,18 @@ export function Cook({
<CardContent> <CardContent>
<div className="space-y-3"> <div className="space-y-3">
{resolvedStepList.map((step, i) => ( {resolvedStepList.map((step, i) => (
<div key={i} className="flex items-start gap-3"> <div key={i} className={cn("flex items-start gap-3", stepStatuses[i] === "skipped" && "opacity-50")}>
<span className={cn("text-lg font-mono w-5 text-center", statusColor(stepStatuses[i]))}> <span className={cn("text-lg font-mono w-5 text-center", statusColor(stepStatuses[i]))}>
{statusIcon(stepStatuses[i])} {statusIcon(stepStatuses[i])}
</span> </span>
<div className="flex-1"> <div className="flex-1">
<div className="text-sm font-medium">{step.label}</div> <div className="text-sm font-medium">
{step.description !== step.label && ( {step.label}
{stepStatuses[i] === "skipped" && phase === "confirm" && (
<span className="text-xs text-muted-foreground ml-2">(skipped empty params)</span>
)}
</div>
{step.description !== step.label && stepStatuses[i] !== "skipped" && (
<div className="text-xs text-muted-foreground">{step.description}</div> <div className="text-xs text-muted-foreground">{step.description}</div>
)} )}
{stepErrors[i] && ( {stepErrors[i] && (
@@ -198,9 +208,6 @@ export function Cook({
Use "Apply Changes" in the sidebar to restart the gateway and activate config changes. Use "Apply Changes" in the sidebar to restart the gateway and activate config changes.
</p> </p>
)} )}
<Button className="mt-4" onClick={onDone}>
Back to Recipes
</Button>
</CardContent> </CardContent>
</Card> </Card>
)} )}