Skip to main content
Turn a scheduled agent into a monitor: probe a cheap source each tick, run the model only when it changed, and stay silent when it didn’t.
The monitor field is a declarable spec the core round-trips through storage. The probe, hashing, bounded diff, state persistence, and the no_change record live in the praisonai-bot / wrapper layer — the core owns only the contract.

Quick Start

1

Turn an agent into a monitor

Add monitor to a ScheduleJob. The agent runs only when the watched URL changes.
2

Watch a shell source instead

Use command inside monitor to watch any cheap shell output — a file, a database count, a health probe.

What The User Sees

Silence is the feature — an unchanged source produces no ping and spends no tokens.

How It Works

Each tick probes the source, hashes the output, and compares it to the last-seen hash held in per-job state.
no_change is a distinct status from skipped. skipped means “a gate said don’t run”; no_change means “a watched source was unchanged” — so operators can tell the two apart in run history.

Configuration Options

monitor spec

monitor is a small mapping naming one cheap source to probe each tick.

ScheduleJob field

GateResult — monitor outcome

The wrapper’s monitor gate returns a GateResult carrying the monitor-mode outcome.

RunRecord.status


Monitor vs Pre-Run vs Command

Three scheduler features feel similar but solve different problems.
From the SDK docstring: “monitor is distinct from pre_run (a stateless go/no-go gate) and command (a model-free delivery action).”
On the scheduled delivery path, a no_change outcome is silently suppressed — no ping, no tokens. See Scheduler Delivery and Bot Intentional Silence for how unattended-monitor silence is enforced unconditionally.

Per-Job State

A monitor needs memory across wake-ups — the last-seen hash or a watermark. The core defines the JobStateStoreProtocol contract; a concrete store lives in the wrapper alongside the heavy monitor gate.
All JobStateStoreProtocol methods are optional. Callers detect support with hasattr() and treat absence as “no per-job state” — today’s stateless behaviour. Legacy stateless gates (should_run(self, job)) still satisfy the protocol under runtime_checkable, so callers must detect capability before passing state=.

Best Practices

The probe runs on every tick. Watch a small, cheap source — a status endpoint, a row count, a file’s modification marker — not a multi-megabyte page. A bounded source keeps the ticker responsive.
Reach for monitor when the whole point is “only tell me when something changed” — a page, a feed, a metric. Unchanged ticks cost zero tokens and deliver nothing.
If the decision is a stateless go/no-go (an inbox check, a queue depth), use pre_run instead — it doesn’t need to remember a prior hash.
The first tick has no prior state, so it always runs and stores the baseline hash. Expect one delivery when a monitor job is created, then silence until the source moves.

Pre-Run Gate

Stateless go/no-go gate — skip when there’s nothing to do

Command Action

Model-free action — deliver a command’s stdout verbatim

Scheduler Delivery

Push scheduled results to Telegram/Discord/Slack/WhatsApp

Bot Intentional Silence

How unattended silence is enforced