fix(cron): corn task channel warn (#237)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Haze <hazeone@users.noreply.github.com>
This commit is contained in:
Haze
2026-02-28 20:37:55 +08:00
committed by GitHub
Unverified
parent bdafbca8d4
commit a3462e2de6

View File

@@ -165,6 +165,7 @@ interface GatewayCronJob {
schedule: { kind: string; expr?: string; everyMs?: number; at?: string; tz?: string }; schedule: { kind: string; expr?: string; everyMs?: number; at?: string; tz?: string };
payload: { kind: string; message?: string; text?: string }; payload: { kind: string; message?: string; text?: string };
delivery?: { mode: string; channel?: string; to?: string }; delivery?: { mode: string; channel?: string; to?: string };
sessionTarget?: string;
state: { state: {
nextRunAtMs?: number; nextRunAtMs?: number;
lastRunAtMs?: number; lastRunAtMs?: number;
@@ -230,6 +231,38 @@ function registerCronHandlers(gatewayManager: GatewayManager): void {
const result = await gatewayManager.rpc('cron.list', { includeDisabled: true }); const result = await gatewayManager.rpc('cron.list', { includeDisabled: true });
const data = result as { jobs?: GatewayCronJob[] }; const data = result as { jobs?: GatewayCronJob[] };
const jobs = data?.jobs ?? []; const jobs = data?.jobs ?? [];
// Auto-repair legacy UI-created jobs that were saved without
// delivery: { mode: 'none' }. The Gateway auto-normalizes them
// to delivery: { mode: 'announce' } which then fails with
// "Channel is required" when no external channels are configured.
for (const job of jobs) {
const isIsolatedAgent =
(job.sessionTarget === 'isolated' || !job.sessionTarget) &&
job.payload?.kind === 'agentTurn';
const needsRepair =
isIsolatedAgent &&
job.delivery?.mode === 'announce' &&
!job.delivery?.channel;
if (needsRepair) {
try {
await gatewayManager.rpc('cron.update', {
id: job.id,
patch: { delivery: { mode: 'none' } },
});
job.delivery = { mode: 'none' };
// Clear stale channel-resolution error from the last run
if (job.state?.lastError?.includes('Channel is required')) {
job.state.lastError = undefined;
job.state.lastStatus = 'ok';
}
} catch (e) {
console.warn(`Failed to auto-repair cron job ${job.id}:`, e);
}
}
}
// Transform Gateway format to frontend format // Transform Gateway format to frontend format
return jobs.map(transformCronJob); return jobs.map(transformCronJob);
} catch (error) { } catch (error) {
@@ -256,6 +289,11 @@ function registerCronHandlers(gatewayManager: GatewayManager): void {
enabled: input.enabled ?? true, enabled: input.enabled ?? true,
wakeMode: 'next-heartbeat', wakeMode: 'next-heartbeat',
sessionTarget: 'isolated', sessionTarget: 'isolated',
// UI-created jobs deliver results via ClawX WebSocket chat events,
// not external messaging channels. Setting mode='none' prevents
// the Gateway from attempting channel delivery (which would fail
// with "Channel is required" when no channels are configured).
delivery: { mode: 'none' },
}; };
const result = await gatewayManager.rpc('cron.add', gatewayInput); const result = await gatewayManager.rpc('cron.add', gatewayInput);
// Transform the returned job to frontend format // Transform the returned job to frontend format