Scheduler Command Action

Run a shell command on schedule and deliver its stdout verbatim — no agent, no model turn

Attach a command to a scheduled job to run a shell command on its schedule and deliver the raw stdout to your channel — no agent is resolved and no model turn is taken.

Diagram

⏰ Schedule fires

💻 Run shell command

📤 stdout

✅ Deliver verbatim to channel

A command job is a model-free action: cheap, deterministic, and token-free — ideal for watchdogs like df -h, uptime, or a health-check curl.

Quick Start

  1. CLI

    Add a job with --command. Its stdout is delivered as-is — no --message, no agent, no tokens.

    bash
    praisonai schedule add "disk-watch" -s hourly \
      --command "df -h /" \
      --deliver telegram:-100123
  2. Python

    Set command on a ScheduleJob. When command is present, the job takes the model-free path.

    python
    from praisonaiagents.scheduler import ScheduleJob, Schedule, DeliveryTarget
    
    job = ScheduleJob(
        name="disk-watch",
        schedule=Schedule(kind="every", every_seconds=3600),
        command="df -h /",
        command_timeout=30,
        delivery=DeliveryTarget.parse("telegram:-100123"),
    )

How It Works

The executor checks for a command before resolving any agent — a command job never touches the model.

Diagram
💬 ChatDeliveryTarget💻 Shell commandExecutor⏰ Cron💬 ChatDeliveryTarget💻 Shell commandExecutor⏰ Cronalt[job has command][no command (agent path)]Tick dueasyncio.to_thread(run command)stdout + exit codedeliver stdout verbatimmessage postedresolve agent → model turn
DetailBehaviour
No model turnNo agent is resolved; zero tokens are spent.
Off the event loopThe command runs via asyncio.to_thread, so a slow command never blocks other ticks.
Bounded runtimeThe command is killed after command_timeout seconds; a timeout returns the conventional 124 code and records the tick as failed.
Process-group killOn POSIX the command runs in its own session, so a timeout kills the whole process group (shell + children) rather than orphaning them.
Exit codes surfaceA non-zero exit is not silently dropped — the delivered text is prefixed [exit N] <output>.
Output capstdout is bounded to 8,000 characters before verbatim delivery through the existing DeliveryTarget path.

Choosing the Right Mode

A scheduled job runs in one of three modes depending on which fields are set.

Diagram

no command, no pre_run

pre_run only

command only

Job configuration

🤖 Normal agent job

🚦 Agent job + cost gate

💻 No-LLM command action

ConfigurationModeModel turn?
No command, no pre_runNormal agent job (default)Yes
pre_run onlyAgent job with a cost gateYes, when the gate says go
command onlyNo-LLM command actionNo

pre_run is a gate (a cheap check that still runs the model); command is an action (a shell command that runs instead of the model).


Configuration Options

ScheduleJob fields

OptionTypeDefaultDescription
commandOptional[str]NoneShell command whose stdout is delivered verbatim. When set, the job runs this command on its schedule and delivers the output as-is to delivery — with no agent resolved and no model turn taken. Additive and backward-compatible: jobs without a command keep the existing agent path.
command_timeoutfloat60.0Max seconds the command may run before it is killed (with its process group on POSIX) and the tick is recorded as failed. Bounds the action so a hung command cannot stall the ticker.
providerOptional[str]NoneModel provider snapshot for a pinned job — not applicable to --command jobs (no model turn). See Scheduler Model Pin.
modelOptional[str]NoneModel snapshot for a pinned job — not applicable to --command jobs (no model turn). See Scheduler Model Pin.
pin_modelboolTrueEnforce the model snapshot on drift — not applicable to --command jobs (no model turn). See Scheduler Model Pin.

command is persisted only when set; command_timeout is persisted only when a command is configured and the timeout differs from the default 60.0. Agent-only jobs are unchanged on disk. A --command job takes no model turn, so provider / model / pin_model have no effect on it — pinning is skipped for command jobs.


Common Patterns

Disk watch

Report free space on the root filesystem every hour.

bash
praisonai schedule add "disk-watch" -s hourly \
  --command "df -h /" \
  --deliver telegram:-100123

Health-check curl

Poll a health endpoint and post the raw response. Use --no-continuable so a reply starts a fresh session instead of resuming.

bash
praisonai schedule add "api-health" -s "*/5m" \
  --command "curl -s https://api.example.com/health" \
  --deliver slack:C012345 \
  --no-continuable

Uptime heartbeat

Post a lightweight heartbeat so you know the host is alive.

python
from praisonaiagents.scheduler import ScheduleJob, Schedule, DeliveryTarget

job = ScheduleJob(
    name="uptime",
    schedule=Schedule(kind="every", every_seconds=300),
    command="uptime",
    delivery=DeliveryTarget.parse("slack:C012345"),
)

Best Practices

Bound every command with command_timeout

The default command_timeout=60.0 kills a hung command with its process group on POSIX. Lower it to match a fast watchdog so a stuck command cannot stall the ticker.

python
job = ScheduleJob(
    name="disk-watch",
    schedule=Schedule(kind="every", every_seconds=3600),
    command="df -h /",
    command_timeout=10,
)
Keep output under 8,000 characters

stdout is capped at 8,000 characters before delivery. Trim chatty commands with head, tail, or a filter so the channel gets a clean, complete message.

bash
praisonai schedule add "top-procs" -s hourly \
  --command "ps aux --sort=-%mem | head -10" \
  --deliver telegram:-100123
Use –no-continuable for pure alerts

A command action is a notification, not a conversation. Add --no-continuable so a reply in the channel starts a fresh session instead of trying to resume a job that has no agent context.

Command jobs are a trusted, human-only surface

command runs an arbitrary host shell command. It is not accepted by the agent-callable schedule_add tool — only a human author via CLI, YAML, or Python can persist one. This prevents a prompt-injected agent from persisting arbitrary shell commands on the host.


Related