Scheduler Plugin
Service for scheduling AI agents to run at specified intervals.
Overview
The @tokenring-ai/scheduler package provides a scheduling system that runs within AI agents to automatically spawn and execute other agents at defined intervals or conditions. It supports recurring tasks, one-time executions, time window constraints, and timezone-aware scheduling with comprehensive monitoring and state management.
Key Features
- Task Scheduling: Schedule agents to run at specified intervals or conditions
- Time Windows: Define running time windows with start and end times
- Recurring Tasks: Support for second, minute, hour, day, week, and month intervals
- One-time Tasks: Execute tasks once at a specific time
- Agent Integration: Automatically spawn and run agents for scheduled tasks
- Monitoring: Track task execution history and current status
- Chat Commands:
/schedulecommand to manage and view scheduled tasks - Task Conditions: Run tasks on specific days of the week or days of the month
- Timezone Support: Schedule tasks in specific IANA timezones
- State Persistence: Task state persists across agent restarts
- Utility Functions:
parseInterval,getNextRunTime, andcheckDayConditionsfor time calculations
Core Components
SchedulerService
The main service that manages task scheduling and execution within an agent.
Constructor:
constructor(app: TokenRingApp, options: z.output<typeof SchedulerConfigSchema>)
Key Methods:
attach(agent: Agent): Attach scheduler to an agent with task and execution staterunScheduler(agent: Agent): Start the scheduler loopstopScheduler(agent: Agent): Stop the scheduler loopaddTask(name: string, task: ScheduledTask, agent: Agent): Add a new taskremoveTask(name: string, agent: Agent): Remove a taskwatchTasks(agent: Agent, signal: AbortSignal): Watch and schedule task executionsrunTask(name: string, task: ScheduledTask, agent: Agent): Execute a task
State Management
ScheduleTaskState: Tracks configured tasks and execution history
tasks: Map of task name to ScheduledTask configurationhistory: Map of task name to array of TaskRunHistory entries- Serialization: Only
tasksare persisted (nothistory)
ScheduleExecutionState: Tracks runtime execution state
tasks: Map of task name to ExecutionScheduleEntryautoStart: Whether to auto-start the schedulerabortController: Controls the scheduler loop- Serialization: Only
autoStartis persisted
Services
TokenRingService Implementation
The scheduler implements TokenRingService and provides:
- Automatic attachment to agents with scheduler configuration
- State initialization for task and execution management
- Scheduler loop management with abort signal support
- Task execution through agent spawning
SchedulerService API Reference
Constructor
constructor(app: TokenRingApp, options: z.output<typeof SchedulerConfigSchema>)
Creates a new scheduler service instance.
Parameters:
app(TokenRingApp): The TokenRing application instanceoptions(SchedulerConfigSchema): The scheduler configuration
attach(agent: Agent): void
Attaches the scheduler to an agent, initializing task and execution state. Merges configuration using deepMerge and optionally auto-starts the scheduler.
Parameters:
agent(Agent): The agent to attach to
runScheduler(agent: Agent): void
Starts the scheduler loop for the given agent. Creates a background task that watches for task executions. Starts a scheduler only if:
- No scheduler is already running
- At least one task is configured
Parameters:
agent(Agent): The agent with scheduler configuration
stopScheduler(agent: Agent): void
Stops the scheduler loop for the given agent by aborting the running scheduler.
Parameters:
agent(Agent): The agent to stop
addTask(name: string, task: ScheduledTask, agent: Agent): void
Adds a new scheduled task to the agent. If autoStart is enabled and the scheduler is not running, it will start automatically.
Parameters:
name(string): Unique name for the tasktask(ScheduledTask): Task configurationagent(Agent): The agent to add the task to
removeTask(name: string, agent: Agent): void
Removes a scheduled task from the agent, clearing any timers or running tasks. Throws Error if task not found.
Parameters:
name(string): Name of the task to removeagent(Agent): The agent with the task
watchTasks(agent: Agent, signal: AbortSignal): Promise<void>
Watches task state and schedules executions. Monitors for task changes and updates timers accordingly. Subscribes to ScheduleTaskState changes and manages execution state.
Parameters:
agent(Agent): The agent with scheduled taskssignal(AbortSignal): Abort signal to stop watching
runTask(name: string, task: ScheduledTask, agent: Agent): Promise<void>
Executes a scheduled task by sending the task message to the agent and monitoring execution. Tracks execution state and records history.
Parameters:
name(string): Name of the task to runtask(ScheduledTask): Task configurationagent(Agent): The agent running the scheduler
Provider Documentation
The scheduler does not implement provider architecture.
RPC Endpoints
The scheduler does not define RPC endpoints.
Chat Commands
/schedule: Manage and monitor scheduled tasks
/schedule start
Starts the scheduler for the current agent.
Usage:
/schedule start
/schedule stop
Stops the scheduler for the current agent.
Usage:
/schedule stop
/schedule show
Displays the current schedules and running status of all tasks.
Usage:
/schedule show
Example Output:
=== Scheduled Tasks ===
**Daily Report**
Message: /chat Generate daily report
Status: pending
Next Run: Mon, Jan 15, 2024, 9:00:00 AM
Last Run: Sun, Jan 14, 2024, 9:00:00 AM
**Health Check**
Message: /chat Check system health
Status: running
Next Run: Mon, Jan 14, 2024, 2:30:00 PM
/schedule add
Adds a new task interactively through a form prompt.
Usage:
/schedule add
The command will prompt for:
- Task Name: Unique identifier for the task
- Instructions for the agent: The message to send to the agent when the task runs
- How often to run: One of:
- Once (runs only one time)
- Every 5 minutes
- Every hour
- Every day
- Earliest time of day: Optional start time (hh:mm, 24-hour clock)
- Latest time of day: Optional end time (hh:mm, 24-hour clock)
Note: If "Once" is selected, the repeat field is omitted from the task configuration.
/schedule remove
Removes a task by name.
Usage:
/schedule remove <name>
Throws: CommandFailedError if no name provided or task not found
/schedule history
Displays the execution history of all tasks, including status and duration.
Usage:
/schedule history
Example Output:
=== Task Execution History ===
**Daily Report**
- [Mon, Jan 15, 2024, 9:00:00 AM] Daily Report - completed (120s) Task completed successfully
- [Sun, Jan 14, 2024, 9:00:00 AM] Daily Report - failed (45s) Task failed with error: ...
Configuration
Plugin Configuration
Configure at the application level in .tokenring/config.mjs:
export default {
scheduler: {
agentDefaults: {
autoStart: true,
tasks: {}
}
}
};
The agentDefaults are merged with per-agent configuration using deep merge, allowing global defaults while supporting agent-specific overrides.
Agent Configuration
Configure per-agent with task definitions:
export default {
scheduler: {
agentDefaults: {
autoStart: true,
tasks: {
"Daily Report": {
message: "/chat Generate daily report",
repeat: "1 day",
after: "09:00",
before: "17:00",
weekdays: "mon tue wed thu fri"
},
"Health Check": {
message: "/chat Check system health",
repeat: "30 minutes",
after: "00:00",
before: "23:59"
},
"Weekly Cleanup": {
message: "/chat Clean up old files",
repeat: "1 week",
weekdays: "sun"
}
}
}
}
};
Configuration Schema
SchedulerConfigSchema
z.object({
agentDefaults: SchedulerAgentConfigSchema
})
SchedulerAgentConfigSchema
z.object({
autoStart: z.boolean().default(true),
tasks: z.record(z.string(), ScheduledTaskSchema).default({})
}).prefault({})
ScheduledTaskSchema
z.object({
repeat: z.string().optional(),
after: z.string().optional(),
before: z.string().optional(),
weekdays: z.string().optional(),
dayOfMonth: z.number().min(1).max(31).optional(),
lastRunTime: z.number().default(0),
timezone: z.string().optional(),
message: z.string(),
})
Usage Examples
Basic Setup
import TokenRingApp from "@tokenring-ai/app";
import scheduler from "@tokenring-ai/scheduler";
const app = new TokenRingApp({
// Your app configuration
});
app.install(scheduler);
Run Every Hour During Business Hours
{
message: "/chat Sync data",
repeat: "1 hour",
after: "09:00",
before: "17:00",
weekdays: "mon tue wed thu fri"
}
Run Once Daily at Specific Time
{
message: "/chat Generate morning briefing",
repeat: "1 day",
after: "08:00"
}
Run Every 30 Minutes with Timezone
{
message: "/chat Quick system check",
repeat: "30 minutes",
after: "00:00",
before: "23:59",
timezone: "America/New_York"
}
Run on Specific Day of Month
{
message: "/chat Generate monthly report",
repeat: "1 month",
dayOfMonth: 1
}
Run Every 2 Hours with Timezone
{
message: "/chat Check database status",
repeat: "2 hours",
timezone: "UTC"
}
Tools
The scheduler package provides three tools for programmatic task management.
scheduler_add_task
Add a new scheduled task to run an agent at specified intervals.
Input Schema:
| Property | Type | Required | Description |
|---|---|---|---|
taskName | string | Yes | Unique name for the scheduled task |
task.description | string | Yes | A long, several paragraph description of the exact task to execute. This should provide enough detail for an AI agent to understand the purpose and requirements of the task. |
task.context | string | No | Additional context or information relevant to task execution. This should include background information, dependencies, or any other details that could help the agent perform the task more effectively. |
task.repeat | string | No | Interval string (e.g., "1 hour", "30 minutes"). Omit for one-time tasks. |
task.after | string | No | Start time in HH:mm format (24-hour clock) |
task.before | string | No | End time in HH:mm format (24-hour clock) |
task.timezone | string | No | IANA timezone string (e.g., 'America/New_York', 'UTC') |
Note: The tool combines description and context into the task message with the format:
{description}
ADDITIONAL CONTEXT:{context}
Example:
await agent.executeTool('scheduler_add_task', {
taskName: "Daily Backup",
task: {
description: "Run a full backup of all critical data. This includes user documents, database exports, and configuration files. Ensure the backup completes within 30 minutes.",
context: "Backup should run after regular business hours to minimize system impact. Include checksum verification for data integrity.",
repeat: "1 day",
after: "02:00",
before: "03:00",
timezone: "America/New_York"
}
});
scheduler_remove_task
Remove a scheduled task by name.
Input Schema:
| Property | Type | Required | Description |
|---|---|---|---|
taskName | string | Yes | Name of the scheduled task to remove |
Example:
await agent.executeTool('scheduler_remove_task', {
taskName: "Daily Backup"
});
Throws: Error if task not found
scheduler_get_schedule
Get the current schedule of all scheduled tasks with their status and next run times.
Input Schema: Empty object
Example:
const schedule = await agent.executeTool('scheduler_get_schedule', {});
// Returns:
// Scheduled Tasks:
//
// The current date and time is {current datetime}, and the following tasks are scheduled
//
// Daily Backup :
// Message: Run a full backup...
// Status: pending
// Next Run: Mon, Jan 15, 2024, 2:00:00 AM
// Last Run: Sun, Jan 14, 2024, 2:00:00 AM
Utility Functions
The scheduler package provides several utility functions for time calculations.
parseInterval
Parses interval strings into seconds.
Location: @tokenring-ai/scheduler/utility/parseInterval
import { parseInterval } from "@tokenring-ai/scheduler/utility/parseInterval";
// Returns 60 (1 minute in seconds)
const interval = parseInterval("1 minute");
// Returns 3600 (1 hour in seconds)
const interval = parseInterval("1 hour");
// Returns 1728000 (20 days in seconds)
const interval = parseInterval("20 days");
// Returns null for invalid formats
const invalid = parseInterval("invalid");
Supported Units:
second/seconds: 1 secondminute/minutes: 60 secondshour/hours: 3600 secondsday/days: 86400 secondsweek/weeks: 604800 seconds (86400 × 7)month/months: 2678400 seconds (86400 × 31)
getNextRunTime
Calculates the next run time for a scheduled task.
Location: @tokenring-ai/scheduler/utility/getNextRunTime
import { getNextRunTime } from "@tokenring-ai/scheduler/utility/getNextRunTime";
// For a task configured to run daily at 9:00 AM
const task = {
message: "/chat Generate daily report",
repeat: "1 day",
after: "09:00"
};
const nextRun = getNextRunTime(task);
// Returns timestamp for next scheduled run (or null if not schedulable)
Behavior:
- For tasks with
repeat: Calculates next run time based onlastRunTime + interval - For one-time tasks (no
repeat): Calculates next run time from today/tomorrow - Respects
afterandbeforetime windows - Respects
weekdaysanddayOfMonthconditions - Searches up to 30 days ahead (
MAX_DAYS_AHEAD) - Returns
nullif no valid run time found within search window
checkDayConditions
Checks if a date matches day conditions for scheduling.
Location: @tokenring-ai/scheduler/utility/checkDayConditions
import { checkDayConditions } from "@tokenring-ai/scheduler/utility/checkDayConditions";
import moment from "moment-timezone";
const task = {
weekdays: "mon wed fri",
dayOfMonth: 15
};
const now = moment.tz("America/New_York");
// Check if today matches the day conditions
const matches = checkDayConditions(task, now);
Behavior:
- Checks if
dayOfMonthmatches (if specified) - Checks if current day of week is in
weekdayslist (if specified) - Returns
trueif both conditions match (or if no conditions specified)
State Management
The scheduler maintains task state within the agent using two state slices.
ScheduleTaskState
Tracks configured tasks and their execution history:
tasks: Map of task name to ScheduledTask configurationhistory: Map of task name to array of TaskRunHistory entries
Persistence: Only tasks are serialized and persisted across agent restarts. Execution history is not persisted.
ScheduleExecutionState
Tracks runtime execution state:
tasks: Map of task name to ExecutionScheduleEntryautoStart: Whether the scheduler should auto-startabortController: Controls the scheduler loop
Persistence: Only autoStart is serialized and persisted. Runtime state (running tasks, timers) is not persisted and will be reset on restart.
State Restoration Pattern:
// On agent restart, task configurations are restored from serialization
// The scheduler can be manually started with /schedule start
// Or automatically if autoStart is true and tasks exist
ExecutionScheduleEntry
Interface for execution schedule entries:
interface ExecutionScheduleEntry {
nextRunTime: number | null;
status: 'pending' | 'running';
abortController?: AbortController;
timer?: NodeJS.Timeout;
startTime?: number;
}
TaskRunHistory
Interface for task run history entries:
interface TaskRunHistory {
startTime: number;
endTime: number;
status: 'completed' | 'failed';
message: string;
}
Integration
The scheduler integrates with the agent system by:
- Plugin Registration: Registers tools with
ChatServiceand commands withAgentCommandService - Service Registration: Creates and attaches
SchedulerServiceto agents - State Management: Registers state slices for task and execution tracking
- Agent Execution: Sends messages to agents to trigger their work
- Event Monitoring: Monitors agent execution and handles completion
- Command Registration: Registers
/schedulecommand with subcommand routing - Tool Registration: Registers tools with
ChatServicefor programmatic access - State Persistence: Maintains task state across agent restarts
Integration with TokenRingApp
The scheduler is installed as a plugin in the TokenRingApp:
const app = new TokenRingApp("path/to/packages");
app.install(scheduler);
Integration with ChatService
The scheduler registers tools with the ChatService:
app.waitForService(ChatService, chatService =>
chatService.addTools(tools)
);
Integration with AgentCommandService
The scheduler registers commands with the AgentCommandService:
app.waitForService(AgentCommandService, agentCommandService =>
agentCommandService.addAgentCommands(agentCommands)
);
Integration with Agent System
When a task is scheduled to run, the scheduler:
- Sends the task message to the current agent
- Monitors execution through event streaming
- Tracks completion status and timing
- Updates execution history on completion
- Handles errors and captures error messages
Best Practices
Task Naming
- Use descriptive, unique task names
- Avoid names with special characters or spaces
- Consider using prefixes for related tasks (e.g., "backup_daily", "backup_weekly")
- Ensure task names match between tools and commands
Scheduling
- Set appropriate time windows to avoid overlapping executions
- Use timezones to ensure consistent scheduling across regions
- Consider system load when scheduling frequent tasks
- Test task configurations before deploying to production
- Use
weekdaysfor business-day-only tasks - Use
dayOfMonthfor monthly tasks (e.g., billing, reports)
Monitoring
- Regularly check execution history for failed tasks with
/schedule history - Use
/schedule showto verify task status - Monitor agent logs for scheduler warnings and errors
- Review task duration in history to identify performance issues
Task Design
- Keep task messages concise and focused
- Include context in task description for clarity
- Use appropriate repeat intervals (avoid too frequent execution)
- Consider using time windows to limit execution to off-peak hours
State Management
- Understand that execution history is not persisted
- Plan for scheduler restart behavior (use
/schedule startif needed) - Verify task configurations after agent restarts
Error Handling
- Task Not Found:
removeTaskthrowsErrorwhen task doesn't exist - Configuration Validation: Invalid configurations prevent agent attachment via Zod validation
- Graceful Shutdown: Scheduler stops scheduling new tasks and aborts running tasks via abort controller
- Runtime Errors: Execution errors are captured in run history with error message
- Cancelled Operations: Interactive task creation throws
CommandFailedErrorwhen cancelled (e.g., user cancels form) - Missing Task Name:
/schedule removethrowsCommandFailedErrorif no name provided - Task Exited Without Reason: If task execution completes without proper event handling, marked as failed with "Task exited without any reason given"
Monitoring and Logging
- Agent Output: Real-time logging of task scheduling and execution through agent info/error messages
- Run History: All executions tracked with timing and status information
- Status Monitoring: Real-time task status through
/schedule showcommand - Performance Tracking: Runtime duration and time window monitoring
- Timer Management: Automatic cleanup of timer references on task completion or removal
Testing and Development
Run tests:
bun test
Run tests in watch mode:
bun test:watch
Run tests with coverage:
bun test:coverage
Test Files
utility/getNextRunTime.test.ts- Tests for next run time calculationutility/parseInterval.test.ts- Tests for interval parsingutility/checkDayConditions.test.ts- Tests for day condition checking
Package Structure
pkg/scheduler/
├── index.ts # Main exports
├── plugin.ts # Plugin definition for TokenRing integration
├── schema.ts # Zod schemas and type definitions
├── SchedulerService.ts # Core service implementation
├── commands.ts # Command exports
├── tools.ts # Tool exports
├── tools/
│ ├── addScheduledTask.ts # scheduler_add_task tool
│ ├── removeScheduledTask.ts # scheduler_remove_task tool
│ └── getSchedule.ts # scheduler_get_schedule tool
├── state/
│ ├── scheduleTaskState.ts # ScheduleTaskState implementation
│ └── scheduleExecutionState.ts # ScheduleExecutionState implementation
├── commands/
│ └── schedule/
│ ├── add.ts # /schedule add subcommand
│ ├── remove.ts # /schedule remove subcommand
│ ├── show.ts # /schedule show subcommand
│ ├── start.ts # /schedule start subcommand
│ ├── stop.ts # /schedule stop subcommand
│ └── history.ts # /schedule history subcommand
├── utility/
│ ├── parseInterval.ts # parseInterval utility function
│ ├── getNextRunTime.ts # getNextRunTime utility function
│ └── checkDayConditions.ts # checkDayConditions utility function
├── vitest.config.ts # Vitest configuration
└── README.md # Package documentation
Dependencies
Production Dependencies
@tokenring-ai/app(0.2.0) - Application framework and plugin system@tokenring-ai/agent(0.2.0) - Core agent system and orchestration@tokenring-ai/chat(0.2.0) - Chat service and command management@tokenring-ai/rpc(0.2.0) - JSON-RPC implementation@tokenring-ai/utility(0.2.0) - Shared utilitieszod(^4.3.6) - Schema validationmoment-timezone(^0.6.0) - Timezone support
Development Dependencies
vitest(^4.0.18) - Testing frameworktypescript(^5.9.3) - TypeScript compiler
Related Components
- @tokenring-ai/agent: Core agent system that the scheduler attaches to
- @tokenring-ai/chat: Chat service that provides tool integration
- @tokenring-ai/app: Base application framework for plugin registration
- @tokenring-ai/utility: Shared utilities including
deepMergefor configuration