Skip to main content
Turn any scheduled job into a change-aware monitor — wake only when a watched source changed, and hand the agent only the diff.
Preview — contract shipped, runtime not yet wired. The core protocol contract (the monitor field, GateResult.no_change / state_updates, JobStateStoreProtocol, and the no_change run status) landed in PraisonAI PR #3845. The change-detection wrapper that actually probes the source, hashes it, and suppresses unchanged ticks ships in a follow-up PR. Today a job that sets monitor={...} round-trips through storage but behaves like a regular job that fires every tick — nothing suppresses a tick yet. Use this page to author against the stable contract; the “wake only when changed” behaviour arrives with the wrapper.

Quick Start

1

Agent-centric — watch a page, tell me only what's new

The agent schedules the monitor for you from a plain request.
2

Direct Python — build the ScheduleJob yourself

Set monitor to name a cheap source to probe each tick.
3

Shell source — probe a cheap counter

A bounded command is a better source than a full HTML page.
4

YAML — configure via agents.yaml


How It Works

The wrapper hashes the probed source and compares it to the last-seen hash held in per-job state. Caller contract (verbatim from the SDK): “Because a legacy stateless gate accepts only job, callers MUST NOT unconditionally pass state=. Detect capability first — e.g. call should_run(job, state=prior) only when the gate opts into state, otherwise fall back to should_run(job) — so stateless gates keep working unchanged and never raise TypeError.” An error probe (a transient 500) leaves prior state untouched, so a later recovery to the prior output still suppresses — a transient failure does not count as a change.

Run Status: no_change vs the others

RunRecord.status gains a fourth value so an operator can tell a silent monitor apart from a deliberate skip.
no_change implies run=False — the core enforces this invariant in GateResult.__post_init__. Use no_change records in session show to prove a monitor is running even when it is silent.

What’s Stored in Per-Job State

The monitor keeps a small durable scratchpad keyed by job id — typically the last-seen hash, plus an optional cursor / watermark.
The state is bounded (size-capped by the implementation) and cleared when the job is removed. The core owns only the shape; the wrapper decides where to persist it.

Distinct From Other Gates

Pick the right sibling — monitor is the stateful one that seeds a diff.

Configuration Options

The monitor field is a small mapping naming one cheap source to probe each tick.
monitor serialises only when set — None (default) keeps the existing stateless behaviour, and an explicitly-empty mapping round-trips faithfully. For the full field types, see the auto-generated SDK reference rather than duplicating them here.

Best Practices

A JSON endpoint returning a single counter beats hashing a full HTML page — it changes only when the thing you care about changes, and it is fast on every tick.
An error probe leaves prior state untouched — a transient 500 does not “count as a change”. When the source recovers to its prior output, the tick still suppresses. Design probes that fail loudly (non-zero exit) rather than emitting an error page as “content”.
A silent monitor looks identical to a broken one. Check run history — a stream of no_change records proves the monitor is probing every tick and simply has nothing to report.
Callers MUST detect capability first. Pass state= only when the gate opts into state; otherwise fall back to should_run(job) so legacy stateless gates never raise TypeError.

Scheduler Pre-Run Gate

The stateless sibling — a cheap go/no-go gate

Scheduler Command Action

The no-LLM sibling — deliver a command’s stdout verbatim

Scheduler Delivery

Where results go — note no_change ticks do not deliver

Async Agent Scheduler

The scheduler this monitor plugs into