Skip to content

Task Scheduler

The task scheduler lets you run agent queries automatically on a schedule -- once at a future time, on a recurring interval, or via a cron expression. Scheduled tasks are persisted in SQLite so they survive process restarts, and execution is handled by a background daemon thread that polls for due tasks every 60 seconds.

Optional component

The scheduler is a standalone module (openjarvis.scheduler). It is not wired into the default Jarvis / JarvisSystem startup. You enable it explicitly via SystemBuilder or by starting the CLI daemon with jarvis scheduler start.


Schedule Types

schedule_type schedule_value format Example Meaning
once ISO 8601 UTC datetime "2026-03-01T09:00:00Z" Run once at that timestamp
interval Seconds as a string "3600" Run every hour, starting immediately
cron Standard 5-field cron expression "0 9 * * 1-5" 09:00 UTC, Monday–Friday

Cron support

Full cron expression support requires croniter (uv pip install croniter). Without it, the scheduler uses a minimal built-in parser that handles simple minute hour * * * patterns only.


CLI Commands

The jarvis scheduler subcommand group manages tasks and the daemon from the terminal.

Start the daemon

jarvis scheduler start

Starts the background polling daemon. The daemon runs in the foreground until interrupted (Ctrl+C). In production, run it under systemd or launchd (see Deployment).

Create a task

# Run once at a specific time
jarvis scheduler create \
  --prompt "Generate the weekly summary report" \
  --type once \
  --value "2026-03-01T09:00:00Z"

# Run every hour
jarvis scheduler create \
  --prompt "Check for new emails and summarize" \
  --type interval \
  --value "3600" \
  --agent orchestrator \
  --tools retrieval,think

# Run on a cron schedule
jarvis scheduler create \
  --prompt "Summarize overnight logs" \
  --type cron \
  --value "0 8 * * 1-5"

List tasks

# All tasks
jarvis scheduler list

# Active tasks only
jarvis scheduler list --status active

# Paused tasks
jarvis scheduler list --status paused

Example output:

ID               AGENT     TYPE       VALUE          STATUS   NEXT RUN
a3f9b12c4d8e    simple    cron       0 8 * * 1-5    active   2026-02-26T08:00:00+00:00
b7c2e56f1a3d    orchestr  interval   3600           active   2026-02-25T14:05:00+00:00

Pause and resume tasks

# Pause a running task
jarvis scheduler pause a3f9b12c4d8e

# Resume -- next_run is recomputed from the current time
jarvis scheduler resume a3f9b12c4d8e

Cancel a task

# Permanently cancel (status -> "cancelled", next_run cleared)
jarvis scheduler cancel a3f9b12c4d8e

View run logs

# Last 10 executions for a task
jarvis scheduler logs a3f9b12c4d8e

Example output:

Run 1: started=2026-02-25T08:00:01Z finished=2026-02-25T08:00:04Z success=True
  Result: Overnight logs contain 3 warnings and no errors.
Run 2: started=2026-02-24T08:00:00Z finished=2026-02-24T08:00:05Z success=True
  Result: Logs are clean.

Python API

scheduler_example.py
from openjarvis.scheduler.store import SchedulerStore
from openjarvis.scheduler.scheduler import TaskScheduler

# Set up storage
store = SchedulerStore(db_path="~/.openjarvis/scheduler.db")  # (1)!

# Wire in a JarvisSystem for task execution
from openjarvis import Jarvis
jarvis = Jarvis()

scheduler = TaskScheduler(
    store=store,
    system=jarvis,         # (2)!
    poll_interval=60,      # (3)!
)

# Create tasks
daily_summary = scheduler.create_task(
    prompt="Summarize the latest news headlines",
    schedule_type="cron",
    schedule_value="0 8 * * *",
    agent="simple",
)
print(f"Created task {daily_summary.id}, next run: {daily_summary.next_run}")

# List active tasks
for task in scheduler.list_tasks(status="active"):
    print(f"  {task.id}: {task.prompt} @ {task.next_run}")

# Manage task state
scheduler.pause_task(daily_summary.id)
scheduler.resume_task(daily_summary.id)   # next_run recomputed from now
scheduler.cancel_task(daily_summary.id)  # permanent

# Start the background thread
scheduler.start()   # (4)!

# ... application runs ...

scheduler.stop()
jarvis.close()
  1. SQLite database storing all task state and run logs.
  2. The scheduler calls system.ask(task.prompt, agent=task.agent, tools=...) when a task is due. Pass system=None for a dry-run mode that logs what it would execute without calling the agent.
  3. Seconds between polling cycles. Lower values increase responsiveness at the cost of more SQLite reads.
  4. Starts a daemon thread named "jarvis-scheduler". Daemon threads exit automatically when the main process exits.

ScheduledTask Fields

Every task is represented as a ScheduledTask dataclass.

Field Type Default Description
id str auto (16 hex) Unique task identifier
prompt str -- Query sent to the agent on execution
schedule_type str -- "cron", "interval", or "once"
schedule_value str -- Cron expression, interval seconds, or ISO datetime
context_mode str "isolated" Execution context mode
status str "active" "active", "paused", "completed", "cancelled"
next_run str or None computed ISO 8601 UTC datetime of the next execution
last_run str or None None ISO 8601 UTC datetime of the last execution
agent str "simple" Agent registry key to use for execution
tools str "" Comma-separated tool names for the agent
metadata dict {} Arbitrary metadata for the task

Using Scheduler Tools with Agents

The five scheduler MCP tools (schedule_task, list_scheduled_tasks, pause_scheduled_task, resume_scheduled_task, cancel_scheduled_task) can be passed to any ToolUsingAgent, allowing an agent to schedule follow-up tasks autonomously.

# Let the orchestrator schedule its own follow-up
jarvis ask --agent orchestrator \
  --tools schedule_task,list_scheduled_tasks \
  "Research transformer architectures and schedule a daily summary at 8am"
from openjarvis import Jarvis

j = Jarvis()
response = j.ask(
    "Schedule a weekly digest of research papers every Monday at 9am",
    agent="orchestrator",
    tools=["schedule_task"],
)
print(response)

See Scheduler Tools for full parameter reference.


Configuration

Scheduler settings live in the [scheduler] section of ~/.openjarvis/config.toml.

~/.openjarvis/config.toml
[scheduler]
enabled = false
db_path = "~/.openjarvis/scheduler.db"
poll_interval = 60
default_agent = "simple"
Key Type Default Description
enabled bool false Start the scheduler daemon automatically
db_path str ~/.openjarvis/scheduler.db SQLite database path
poll_interval int 60 Seconds between polling cycles
default_agent str "simple" Default agent for tasks that omit agent

See Also