Schedule tasks·续1

// Get tasks in the next hour const upcoming = await this.listSchedules({ timeRange: { start: new Date(), end: new Date(Date.now() + 60 * 60 * 1000), }, });

// Get a specific task by ID const specific = await this.listSchedules({ id: "abc123" });

// Combine filters const upcomingCronJobs = await this.listSchedules({ type: "cron", timeRange: { start: new Date(), end: new Date(Date.now() + 24 * 60 * 60 * 1000), }, });


### Cancel a schedule

Remove a scheduled task before it executes:

```js
const cancelled = await this.cancelSchedule(scheduleId);

if (cancelled) {
    console.log("Schedule cancelled successfully");
} else {
    console.log("Schedule not found (may have already executed)");
}
const cancelled = await this.cancelSchedule(scheduleId);

if (cancelled) {
    console.log("Schedule cancelled successfully");
} else {
    console.log("Schedule not found (may have already executed)");
}

Example: Cancellable reminders

class ReminderAgent extends Agent {
    async setReminder(userId, message, delaySeconds) {
        const schedule = await this.schedule(delaySeconds, "sendReminder", {
            userId,
            message,
        });

// Store the schedule ID so user can cancel later
        this.sql`
      INSERT INTO user_reminders (user_id, schedule_id, message)
      VALUES (${userId}, ${schedule.id}, ${message})
    `;

return schedule.id;
    }

async cancelReminder(scheduleId) {
        const cancelled = await this.cancelSchedule(scheduleId);

if (cancelled) {
            this.sql`DELETE FROM user_reminders WHERE schedule_id = ${scheduleId}`;
        }

return cancelled;
    }

async sendReminder(payload) {
        // Send the reminder...

// Clean up the record
        this.sql`DELETE FROM user_reminders WHERE user_id = ${payload.userId}`;
    }
}
class ReminderAgent extends Agent {
    async setReminder(userId: string, message: string, delaySeconds: number) {
        const schedule = await this.schedule(delaySeconds, "sendReminder", {
            userId,
            message,
        });

// Store the schedule ID so user can cancel later
        this.sql`
      INSERT INTO user_reminders (user_id, schedule_id, message)
      VALUES (${userId}, ${schedule.id}, ${message})
    `;

return schedule.id;
    }

async cancelReminder(scheduleId: string) {
        const cancelled = await this.cancelSchedule(scheduleId);

if (cancelled) {
            this.sql`DELETE FROM user_reminders WHERE schedule_id = ${scheduleId}`;
        }

return cancelled;
    }

async sendReminder(payload: { userId: string; message: string }) {
        // Send the reminder...

// Clean up the record
        this.sql`DELETE FROM user_reminders WHERE user_id = ${payload.userId}`;
    }
}

The Schedule object

When you create or retrieve a schedule, you get a Schedule object:

type Schedule<T> = {
    id: string; // Unique identifier
    callback: string; // Method name to call
    payload: T; // Data passed to the callback
    time: number; // Unix timestamp (seconds) of next execution
} & (
    | { type: "scheduled" } // One-time at specific date
    | { type: "delayed"; delayInSeconds: number } // One-time after delay
    | { type: "cron"; cron: string } // Recurring (cron expression)
    | { type: "interval"; intervalSeconds: number } // Recurring (fixed interval)
);

Example:

const schedule = await this.schedule(60, "myTask", { foo: "bar" });

console.log(schedule);
// {
//   id: "abc123xyz",
//   callback: "myTask",
//   payload: { foo: "bar" },
//   time: 1706745600,
//   type: "delayed",
//   delayInSeconds: 60
// }
const schedule = await this.schedule(60, "myTask", { foo: "bar" });

console.log(schedule);
// {
//   id: "abc123xyz",
//   callback: "myTask",
//   payload: { foo: "bar" },
//   time: 1706745600,
//   type: "delayed",
//   delayInSeconds: 60
// }

Patterns

Rescheduling from callbacks

For dynamic recurring schedules, schedule the next run from within the callback:

class PollingAgent extends Agent {
    async startPolling(intervalSeconds) {
        await this.schedule(intervalSeconds, "poll", { interval: intervalSeconds });
    }

async poll(payload) {
        try {
            const data = await fetch("https://api.example.com/updates");
            await this.processUpdates(await data.json());
        } catch (error) {
            console.error("Polling failed:", error);
        }

// Schedule the next poll (regardless of success/failure)
        await this.schedule(payload.interval, "poll", payload);
    }

async stopPolling() {
        // Cancel all polling schedules
        const schedules = await this.listSchedules({ type: "delayed" });
        for (const schedule of schedules) {
            if (schedule.callback === "poll") {
                await this.cancelSchedule(schedule.id);
            }
        }
    }
}
class PollingAgent extends Agent {
    async startPolling(intervalSeconds: number) {
        await this.schedule(intervalSeconds, "poll", { interval: intervalSeconds });
    }

async poll(payload: { interval: number }) {
        try {
            const data = await fetch("https://api.example.com/updates");
            await this.processUpdates(await data.json());
        } catch (error) {
            console.error("Polling failed:", error);
        }

// Schedule the next poll (regardless of success/failure)
        await this.schedule(payload.interval, "poll", payload);
    }

async stopPolling() {
        // Cancel all polling schedules
        const schedules = await this.listSchedules({ type: "delayed" });
        for (const schedule of schedules) {
            if (schedule.callback === "poll") {
                await this.cancelSchedule(schedule.id);
            }
        }
    }
}

Exponential backoff retry

class RetryAgent extends Agent {
    async attemptTask(payload) {
        try {
            await this.doWork(payload.taskId);
            console.log(
                `Task ${payload.taskId} succeeded on attempt ${payload.attempt}`,
            );
        } catch (error) {
            if (payload.attempt >= payload.maxAttempts) {
                console.error(
                    `Task ${payload.taskId} failed after ${payload.maxAttempts} attempts`,
                );
                return;
            }

// Exponential backoff: 2^attempt seconds (2s, 4s, 8s, 16s...)
            const delaySeconds = Math.pow(2, payload.attempt);

await this.schedule(delaySeconds, "attemptTask", {
                ...payload,
                attempt: payload.attempt + 1,
            });

console.log(`Retrying task ${payload.taskId} in ${delaySeconds}s`);
        }
    }

async doWork(taskId) {
        // Your actual work here
    }
}
class RetryAgent extends Agent {
    async attemptTask(payload: {
        taskId: string;
        attempt: number;
        maxAttempts: number;
    }) {
        try {
            await this.doWork(payload.taskId);
            console.log(
                `Task ${payload.taskId} succeeded on attempt ${payload.attempt}`,
            );
        } catch (error) {
            if (payload.attempt >= payload.maxAttempts) {
                console.error(
                    `Task ${payload.taskId} failed after ${payload.maxAttempts} attempts`,
                );
                return;
            }

// Exponential backoff: 2^attempt seconds (2s, 4s, 8s, 16s...)
            const delaySeconds = Math.pow(2, payload.attempt);

await this.schedule(delaySeconds, "attemptTask", {
                ...payload,
                attempt: payload.attempt + 1,
            });

console.log(`Retrying task ${payload.taskId} in ${delaySeconds}s`);
        }
    }

async doWork(taskId: string) {
        // Your actual work here
    }
}

Self-destructing agents

You can safely call this.destroy() from within a scheduled callback:

class TemporaryAgent extends Agent {
    async onStart() {
        // Self-destruct in 24 hours
        await this.schedule(24 * 60 * 60, "cleanup", {});
    }

async cleanup() {
        // Perform final cleanup
        console.log("Agent lifetime expired, cleaning up...");

// This is safe to call from a scheduled callback
        await this.destroy();
    }
}
class TemporaryAgent extends Agent {
    async onStart() {
        // Self-destruct in 24 hours
        await this.schedule(24 * 60 * 60, "cleanup", {});
    }

async cleanup() {
        // Perform final cleanup
        console.log("Agent lifetime expired, cleaning up...");

// This is safe to call from a scheduled callback
        await this.destroy();
    }
}

Note

When destroy() is called from within a scheduled task, the Agent SDK defers the destruction to ensure the scheduled callback completes successfully. The Agent instance will be evicted immediately after the callback finishes executing.

AI-assisted scheduling

The SDK includes utilities for parsing natural language scheduling requests with AI.

getSchedulePrompt()

Returns a system prompt for parsing natural language into scheduling parameters:

import { getSchedulePrompt, scheduleSchema } from "agents/schedules/parser";
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";

class SmartScheduler extends Agent {
    async parseScheduleRequest(userInput) {
        const result = await generateObject({
            model: openai("gpt-4o"),
            system: getSchedulePrompt({ date: new Date() }),
            prompt: userInput,
            schema: scheduleSchema,
        });

return result.object;
    }

async handleUserRequest(input) {
        // Parse: "remind me to call mom tomorrow at 3pm"
        const parsed = await this.parseScheduleRequest(input);

// parsed = {
        //   description: "call mom",
        //   when: {
        //     type: "scheduled",
        //     date: "2025-01-30T15:00:00Z"
        //   }
        // }

if (parsed.when.type === "scheduled" && parsed.when.date) {
            await this.schedule(new Date(parsed.when.date), "sendReminder", {
                message: parsed.description,
            });
        } else if (parsed.when.type === "delayed" && parsed.when.delayInSeconds) {
            await this.schedule(parsed.when.delayInSeconds, "sendReminder", {
                message: parsed.description,
            });
        } else if (parsed.when.type === "cron" && parsed.when.cron) {
            await this.schedule(parsed.when.cron, "sendReminder", {
                message: parsed.description,
            });
        }
    }

async sendReminder(payload) {
        console.log(`Reminder: ${payload.message}`);
    }
}
import { getSchedulePrompt, scheduleSchema } from "agents/schedules/parser";
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";

class SmartScheduler extends Agent {
    async parseScheduleRequest(userInput: string) {
        const result = await generateObject({
            model: openai("gpt-4o"),
            system: getSchedulePrompt({ date: new Date() }),
            prompt: userInput,
            schema: scheduleSchema,
        });

return result.object;
    }

async handleUserRequest(input: string) {
        // Parse: "remind me to call mom tomorrow at 3pm"
        const parsed = await this.parseScheduleRequest(input);

// parsed = {
        //   description: "call mom",
        //   when: {
        //     type: "scheduled",
        //     date: "2025-01-30T15:00:00Z"
        //   }
        // }

if (parsed.when.type === "scheduled" && parsed.when.date) {
            await this.schedule(new Date(parsed.when.date), "sendReminder", {
                message: parsed.description,
            });
        } else if (parsed.when.type === "delayed" && parsed.when.delayInSeconds) {
            await this.schedule(parsed.when.delayInSeconds, "sendReminder", {
                message: parsed.description,
            });
        } else if (parsed.when.type === "cron" && parsed.when.cron) {
            await this.schedule(parsed.when.cron, "sendReminder", {
                message: parsed.description,
            });
        }
    }

async sendReminder(payload: { message: string }) {
        console.log(`Reminder: ${payload.message}`);
    }
}

scheduleSchema

A Zod schema for validating parsed scheduling data. Uses a discriminated union on when.type so each variant only contains the fields it needs:

import { scheduleSchema } from "agents/schedules/parser";

// The schema is a discriminated union:
// {
//   description: string,
//   when:
//     | { type: "scheduled", date: string }       // ISO 8601 date string
//     | { type: "delayed", delayInSeconds: number }
//     | { type: "cron", cron: string }
//     | { type: "no-schedule" }
// }
import { scheduleSchema } from "agents/schedules/parser";

// The schema is a discriminated union:
// {
//   description: string,
//   when:
//     | { type: "scheduled", date: string }       // ISO 8601 date string
//     | { type: "delayed", delayInSeconds: number }
//     | { type: "cron", cron: string }
//     | { type: "no-schedule" }
// }

Note

Dates are returned as ISO 8601 strings (not Date objects) for compatibility with both Zod v3 and v4 JSON schema generation.

Scheduling vs Queue vs Workflows

Feature Queue Scheduling Workflows
When Immediately (FIFO) Future time Future time
Execution Sequential At scheduled time Multi-step
Retries Built-in Built-in Automatic
Persistence SQLite SQLite Workflow engine
Recurring No Yes (cron) No (use scheduling)
Complex logic No No Yes
Human approval No No Yes