Scheduler Module
The Scheduler module provides deployment scheduling capabilities with a provider-agnostic design.Import
Quick Example
Classes
DeploymentScheduler
Minimal deployment scheduler with provider-agnostic design.
Features:
- Simple interval-based scheduling
- Thread-safe operation
- Extensible deployer factory pattern
ScheduleParser
Parses schedule expressions into scheduling parameters. ScheduleParser is also re-exported from praisonai.scheduler.shared and used internally by AgentScheduler / AsyncAgentScheduler.
DeployerInterface
Abstract interface for deployers to ensure provider compatibility.
Methods
DeploymentScheduler.schedule(interval_minutes)
Schedule deployments at a fixed interval.
Parameters:
interval_minutes(int): Minutes between deployments
DeploymentScheduler.start()
Start the scheduler in a background thread.
DeploymentScheduler.stop()
Stop the scheduler.
DeploymentScheduler.is_running()
Check if the scheduler is currently running.
Returns: bool
Example: Custom Deployer
Timezones
A naiveat: timestamp (no Z, no +HH:MM offset) and every cron: expression resolve their timezone in this order:
resolve_schedule_timezone in praisonaiagents.scheduler.due implements the env-var and UTC fallback; is_due(job, now, default_timezone=...) threads the instance default through.
An
at: string that already carries an offset (at:2026-03-01T09:00:00+05:30, at:2026-03-01T09:00:00Z) is unaffected — the offset in the string always wins. See Schedule Tools → Timezones.ScheduleJob — Pre-Run Gate Fields
Since PR #2238,ScheduleJob (in praisonaiagents.scheduler.models) has two optional fields for the pre-run condition gate:
Timezones
parse_schedule (in praisonaiagents.scheduler.parser) resolves the timezone for a naive at: timestamp and the clock forms (at 9am, at 17:00) in order:
- The
tz=argument passed toparse_schedule/schedule_add(per-schedule). - The instance default — the
scheduler.timezone(orscheduler.tz) key inconfig.yaml, whichConfigYamlScheduleStorereads into its_default_timezoneand passes tois_due(). - The
PRAISONAI_SCHEDULE_TIMEZONEenvironment variable. - The machine’s local zone, with the UTC offset in force on the target date (DST-correct).
at: string that already carries an offset (+05:30, Z, +00:00) is used verbatim. cron: follows the same 1–3 precedence but falls back to UTC at step 4.
Parse-time stamping vs the is_due() fallback
As of PraisonAI #4732, the zone is attached at parse time by praisonaiagents.scheduler.due.localize_wall_clock, so parse_schedule returns a Schedule whose at is already an aware ISO string:
is_due() keeps a legacy branch for naive strings that did not come through the parser — jobs written before this PR, hand-edited config.yaml entries, or a Schedule(kind="at", at=<naive iso>) built directly. That branch calls the same localize_wall_clock helper, so both paths agree. An unknown IANA zone raises ValueError from either path instead of firing never.
The instance default
The store’s instance default flows throughis_due(job, now, default_timezone=...) and applies to naive at: values that omit a per-schedule tz. Set it in config.yaml:
is_due() accepts the same value directly when you evaluate a job yourself:
ScheduleStoreProtocol — per-principal scoping
Each store method accepts an optionalprincipal — the resolved end-user identity used to isolate one gateway user’s automations from another’s. None returns everything (global / single-tenant behaviour), so every call is backward-compatible.
ScheduleJob.principal (Optional[str] = None) round-trips through to_dict/from_dict and is omitted when None, so existing on-disk store files stay byte-identical.
The same scoping was extended to SuggestionStore — list_pending, accept, and dismiss take a principal and refuse cross-owner mutations, with the pending cap and dedup window measured per owner. See Automation Suggestions → Multi-tenant isolation for the suggestion-store surface.
ScheduledAgentExecutor
ScheduledAgentExecutor lives in the bot tier (praisonai-bot). The canonical import is:
Constructor reference
Public methods
Atomic claims: When the backing store supports
claim_due, each due job is reserved under a cross-process lock so it fires at most once across all tickers/processes/hosts. Stores without that support fall back to the non-atomic get_due_jobs path.Example
condition_resolver values
JobConditionProtocol and GateResult
JobConditionProtocol and GateResult are exported from praisonaiagents.scheduler:
GateResult— dataclass withrun: bool,context: Optional[str],reason: Optional[str]JobConditionProtocol— protocol for custom gate implementationsSchedulerProviderProtocol— trigger backend protocol deciding when jobs fire;ScheduleLoop(aliasedInProcessScheduleProvider) is the default. See Scheduler Providers.
RunPolicy
Run-scoped guardrail for unattended scheduled agent runs. Scopes the toolset, scans the assembled prompt for injection patterns, persists a durable output audit, and supports fail-closed delivery on failure.
PromptScanResult
Return type from RunPolicy.scan_prompt() and custom scanner callables.
Related
- Scheduler Pre-Run Gate - Full user-facing documentation
- Scheduled Run Policy - Safety gate for unattended runs
- praisonai-bot SDK - Bot-tier package reference
- Deploy Module - Deployment functionality
- CLI Scheduler - CLI scheduling commands

