fix(cron): resolve default agent from DB and fix update payload (#549) (#562)

- Backend: resolve real default agent UUID from DB when cron job has no
  agent_id, instead of using literal "default" string that fails lookup
- UI: replace free-text agent input with dropdown in create form
- UI: wrap update params in `patch` object matching backend expectation
- UI: send empty string for agentId to explicitly clear agent selection
- UI: add name attribute to Select elements to fix form field warning
This commit is contained in:
Thieu Nguyen authored and GitHub committed 2026-03-30 11:45:54 +07:00
1 parent 216b8abac6
commit 82e86bcce1
4 files changed
+28 -5

No files matched your search

+9 -1
View File
@@ -27,7 +27,15 @@ var cronHeartbeatWakeFn func(agentID string)
func makeCronJobHandler(sched *scheduler.Scheduler, msgBus *bus.MessageBus, cfg *config.Config, channelMgr *channels.Manager, sessionMgr store.SessionStore, agentStore store.AgentStore) func(job *store.CronJob) (*store.CronJobResult, error) {
return func(job *store.CronJob) (*store.CronJobResult, error) {
agentID := job.AgentID
if agentID == "" {
if agentID == "" && agentStore != nil {
// Resolve real default agent from DB instead of using literal "default" string.
tenantCtx := store.WithTenantID(context.Background(), job.TenantID)
if defaultAgent, err := agentStore.GetDefault(tenantCtx); err == nil {
agentID = defaultAgent.AgentKey
} else {
agentID = cfg.ResolveDefaultAgentID()
}
} else if agentID == "" {
agentID = cfg.ResolveDefaultAgentID()
} else if id, err := uuid.Parse(agentID); err == nil && agentStore != nil {
// Resolve agentKey from UUID so session key uses agentKey
@@ -57,7 +57,7 @@ export function CronOverviewTab({ job, onUpdate }: CronOverviewTabProps) {
await onUpdate(job.id, {
schedule,
message: message.trim(),
agentId: agentId.trim() || undefined,
agentId: agentId.trim() || "",
enabled,
});
setEditingMessage(false);
@@ -176,7 +176,7 @@ export function CronOverviewTab({ job, onUpdate }: CronOverviewTabProps) {
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<div className="space-y-2">
<Label>{t("create.agentId")}</Label>
<Select value={agentId || "__default__"} onValueChange={(v) => setAgentId(v === "__default__" ? "" : v)} disabled={readonly}>
<Select name="agentId" value={agentId || "__default__"} onValueChange={(v) => setAgentId(v === "__default__" ? "" : v)} disabled={readonly}>
<SelectTrigger className="text-base md:text-sm">
<SelectValue placeholder={t("create.agentIdPlaceholder")} />
</SelectTrigger>
+16 -1
View File
@@ -11,8 +11,10 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import type { CronSchedule } from "./hooks/use-cron";
import { slugify, isValidSlug } from "@/lib/slug";
import { useAgents } from "@/pages/agents/hooks/use-agents";
interface CronFormDialogProps {
open: boolean;
@@ -29,6 +31,7 @@ type ScheduleKind = "every" | "cron" | "at";
export function CronFormDialog({ open, onOpenChange, onSubmit }: CronFormDialogProps) {
const { t } = useTranslation("cron");
const { agents } = useAgents();
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const [agentId, setAgentId] = useState("");
@@ -81,7 +84,19 @@ export function CronFormDialog({ open, onOpenChange, onSubmit }: CronFormDialogP
<div className="space-y-2">
<Label>{t("create.agentId")}</Label>
<Input value={agentId} onChange={(e) => setAgentId(e.target.value)} placeholder={t("create.agentIdPlaceholder")} />
<Select name="agentId" value={agentId || "__default__"} onValueChange={(v) => setAgentId(v === "__default__" ? "" : v)}>
<SelectTrigger className="text-base md:text-sm">
<SelectValue placeholder={t("create.agentIdPlaceholder")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="__default__">{t("create.agentIdPlaceholder")}</SelectItem>
{agents.map((a) => (
<SelectItem key={a.id} value={a.id}>
{a.display_name || a.agent_key || a.id}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
+1 -1
View File
@@ -152,7 +152,7 @@ export function useCron() {
const updateJob = useCallback(
async (jobId: string, params: Record<string, unknown>) => {
try {
await ws.call(Methods.CRON_UPDATE, { jobId, ...params });
await ws.call(Methods.CRON_UPDATE, { jobId, patch: params });
await invalidate();
toast.success(i18next.t("cron:toast.updated"));
} catch (err) {