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 onlyjob, 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.Distinct From Other Gates
Pick the right sibling —monitor is the stateful one that seeds a diff.
Configuration Options
Themonitor 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
Prefer a bounded, cheap probe
Prefer a bounded, cheap probe
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.
A transient error is not a change
A transient error is not a change
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”.
Use no_change records to prove a monitor is alive
Use no_change records to prove a monitor is alive
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.Never pass state= to a stateless gate
Never pass state= to a stateless gate
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.Related
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 deliverAsync Agent Scheduler
The scheduler this monitor plugs into

