import { useAgent } from "agents/react"; import type { Schedule } from "agents"; import { useState, useEffect, useCallback } from "react"; import { Button, Input, Surface, Text } from "@cloudflare/kumo"; import { DemoWrapper } from "../../layout"; import { LogPanel, ConnectionStatus, CodeExplanation, type CodeSection } from "../../components"; import { useLogs, useUserId, useToast } from "../../hooks"; import type { ScheduleAgent, ScheduleAgentState } from "./schedule-agent"; const codeSections: CodeSection[] = [ { title: "Schedule tasks from within an agent", description: "Use this.schedule() to fire a callback after a delay. The schedule is durable — if the Worker hibernates and wakes up, pending schedules still execute.", code: `import { Agent, callable } from "agents"; class ScheduleAgent extends Agent { @callable() async scheduleTask(delaySeconds: number, message: string) { const schedule = await this.schedule( delaySeconds, "onScheduledTask", { message } ); return schedule.id; } async onScheduledTask(payload: { message: string }) { console.log("Executed:", payload.message); this.broadcast(JSON.stringify({ type: "schedule_executed", payload, })); } }` }, { title: "Recurring intervals", description: "Pass a callback name and payload — the agent will keep invoking it at the given interval. Use this.cancelSchedule(id) to stop it.", code: ` @callable() async scheduleRecurring(intervalSeconds: number, label: string) { const schedule = await this.schedule( intervalSeconds, "onRecurringTask", { label, recurring: true } ); return schedule.id; } @callable() async cancelTask(id: string) { return await this.cancelSchedule(id); } @callable() listSchedules() { return this.getSchedules(); }` }, { title: "React to schedule events on the client", description: "The agent broadcasts messages when schedules fire. Listen for them with the onMessage callback in useAgent.", code: `const agent = useAgent({ agent: "schedule-agent", name: "my-instance", onMessage: (event) => { const data = JSON.parse(event.data); if (data.type === "schedule_executed") { // a one-time schedule just fired } else if (data.type === "recurring_executed") { // an interval tick } }, }); // schedule a task from the client const id = await agent.call("scheduleTask", [10, "hello"]); // cancel it later await agent.call("cancelTask", [id]);` } ]; export function ScheduleDemo() { const userId = useUserId(); const { logs, addLog, clearLogs } = useLogs(); const { toast } = useToast(); const [schedules, setSchedules] = useState([]); const [delaySeconds, setDelaySeconds] = useState("5"); const [message, setMessage] = useState("Hello from schedule!"); const [intervalSeconds, setIntervalSeconds] = useState("10"); const [intervalLabel, setIntervalLabel] = useState("Recurring ping"); const agent = useAgent({ agent: "schedule-agent", name: `schedule-demo-${userId}`, onOpen: () => { addLog("info", "connected"); refreshSchedules(); }, onClose: () => addLog("info", "disconnected"), onError: () => addLog("error", "error", "Connection error"), onMessage: (message: MessageEvent) => { try { const data = JSON.parse(message.data as string); if (data.type === "schedule_executed") { addLog("in", "schedule_executed", data.payload); toast("Schedule fired!", "success"); refreshSchedules(); } else if (data.type === "recurring_executed") { addLog("in", "recurring_executed", data.payload); } } catch { // Not JSON or not our message type } } }); const refreshSchedules = useCallback(async () => { try { const result = await agent.call("listSchedules"); setSchedules(result); } catch { // Ignore errors during refresh } }, [agent]); useEffect(() => { if (agent.readyState === WebSocket.OPEN) { refreshSchedules(); } }, [agent.readyState, refreshSchedules]); const handleScheduleTask = async () => { addLog("out", "scheduleTask", { delaySeconds: Number(delaySeconds), message }); try { const id = await agent.call("scheduleTask", [ Number(delaySeconds), message ]); addLog("in", "scheduled", { id }); toast("Task scheduled — fires in " + delaySeconds + "s", "info"); refreshSchedules(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleScheduleRecurring = async () => { addLog("out", "scheduleRecurring", { intervalSeconds: Number(intervalSeconds), label: intervalLabel }); try { const id = await agent.call("scheduleRecurring", [ Number(intervalSeconds), intervalLabel ]); addLog("in", "scheduled", { id }); toast("Recurring task started — every " + intervalSeconds + "s", "info"); refreshSchedules(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const handleCancel = async (id: string) => { addLog("out", "cancelTask", { id }); try { const result = await agent.call("cancelTask", [id]); addLog("in", "cancelled", { id, success: result }); refreshSchedules(); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const formatTime = (timestamp: number) => { const date = new Date(timestamp * 1000); return date.toLocaleString(); }; return ( Agents can schedule work for the future using{" "} this.schedule() . Schedule a one-time task with a delay in seconds, or set up a recurring interval that fires repeatedly. Schedules are durable — they persist across Worker restarts and hibernation, so a task scheduled for an hour from now will still fire even if the Durable Object sleeps in between. Try scheduling a 5-second task and watch the event log. } statusIndicator={ } >
{/* Controls */}
{/* One-time Task */}
One-time Task

Schedule a task to run after a delay

) => setDelaySeconds(e.target.value) } className="w-20" min={1} /> seconds
) => setMessage(e.target.value) } className="w-full" placeholder="Message" />
{/* Recurring Task */}
Recurring Task

Schedule a task to repeat at an interval

) => setIntervalSeconds(e.target.value) } className="w-20" min={5} /> second interval
) => setIntervalLabel(e.target.value) } className="w-full" placeholder="Label" />
{/* Active Schedules */}
Active Schedules ({schedules.length})
{schedules.length === 0 ? (

No active schedules

) : (
{schedules.map((schedule) => (
{schedule.callback}
{schedule.type === "interval" ? `Every ${schedule.intervalSeconds}s` : schedule.time ? `At ${formatTime(schedule.time)}` : schedule.type}
))}
)}
{/* Logs */}
); }