Files
hermes-agent/website/docs/developer-guide/cron-internals.md
Teknium c58e16757a docs: fix 40+ discrepancies between documentation and codebase (#5818)
Comprehensive audit of all ~100 doc pages against the actual code, fixing:

Reference docs:
- HERMES_API_TIMEOUT default 900 -> 1800 (env-vars)
- TERMINAL_DOCKER_IMAGE default python:3.11 -> nikolaik/python-nodejs (env-vars)
- compression.summary_model default shown as gemini -> actually empty string (env-vars)
- Add missing GOOGLE_API_KEY, GEMINI_API_KEY, GEMINI_BASE_URL env vars (env-vars)
- Add missing /branch (/fork) slash command (slash-commands)
- Fix hermes-cli tool count 39 -> 38 (toolsets-reference)
- Fix hermes-api-server drop list to include text_to_speech (toolsets-reference)
- Fix total tool count 47 -> 48, standalone 14 -> 15 (tools-reference)

User guide:
- web_extract.timeout default 30 -> 360 (configuration)
- Remove display.theme_mode (not implemented in code) (configuration)
- Remove display.background_process_notifications (not in defaults) (configuration)
- Browser inactivity timeout 300/5min -> 120/2min (browser)
- Screenshot path browser_screenshots -> cache/screenshots (browser)
- batch_runner default model claude-sonnet-4-20250514 -> claude-sonnet-4.6
- Add minimax to TTS provider list (voice-mode)
- Remove credential_pool_strategies from auth.json example (credential-pools)
- Fix Slack token path platforms/slack/ -> root ~/.hermes/ (slack)
- Fix Matrix store path for new installs (matrix)
- Fix WhatsApp session path for new installs (whatsapp)
- Fix HomeAssistant config from gateway.json to config.yaml (homeassistant)
- Fix WeCom gateway start command (wecom)

Developer guide:
- Fix tool/toolset counts in architecture overview
- Update line counts: main.py ~5500, setup.py ~3100, run.py ~7500, mcp_tool ~2200
- Replace nonexistent agent/memory_store.py with memory_manager.py + memory_provider.py
- Update _discover_tools() list: remove honcho_tools, add skill_manager_tool
- Add session_search and delegate_task to intercepted tools list (agent-loop)
- Fix budget warning: two-tier system (70% caution, 90% warning) (agent-loop)
- Fix gateway auth order (per-platform first, global last) (gateway-internals)
- Fix email_adapter.py -> email.py, add webhook.py + api_server.py (gateway-internals)
- Add 7 missing providers to provider-runtime list

Other:
- Add Docker --cap-add entries to security doc
- Fix Python version 3.10+ -> 3.11+ (contributing)
- Fix AGENTS.md discovery claim (not hierarchical walk) (tips)
- Fix cron 'add' -> canonical 'create' (cron-internals)
- Add pre_api_request/post_api_request hooks to plugin guide
- Add Google/Gemini provider to providers page
- Clarify OPENAI_BASE_URL deprecation (providers)
2026-04-07 10:17:44 -07:00

7.6 KiB

sidebar_position, title, description
sidebar_position title description
11 Cron Internals How Hermes stores, schedules, edits, pauses, skill-loads, and delivers cron jobs

Cron Internals

The cron subsystem provides scheduled task execution — from simple one-shot delays to recurring cron-expression jobs with skill injection and cross-platform delivery.

Key Files

File Purpose
cron/jobs.py Job model, storage, atomic read/write to jobs.json
cron/scheduler.py Scheduler loop — due-job detection, execution, repeat tracking
tools/cronjob_tools.py Model-facing cronjob tool registration and handler
gateway/run.py Gateway integration — cron ticking in the long-running loop
hermes_cli/cron.py CLI hermes cron subcommands

Scheduling Model

Four schedule formats are supported:

Format Example Behavior
Relative delay 30m, 2h, 1d One-shot, fires after the specified duration
Interval every 2h, every 30m Recurring, fires at regular intervals
Cron expression 0 9 * * * Standard 5-field cron syntax (minute, hour, day, month, weekday)
ISO timestamp 2025-01-15T09:00:00 One-shot, fires at the exact time

The model-facing surface is a single cronjob tool with action-style operations: create, list, update, pause, resume, run, remove.

Job Storage

Jobs are stored in ~/.hermes/cron/jobs.json with atomic write semantics (write to temp file, then rename). Each job record contains:

{
  "id": "job_abc123",
  "name": "Daily briefing",
  "prompt": "Summarize today's AI news and funding rounds",
  "schedule": "0 9 * * *",
  "skills": ["ai-funding-daily-report"],
  "deliver": "telegram:-1001234567890",
  "repeat": null,
  "state": "scheduled",
  "next_run": "2025-01-16T09:00:00Z",
  "run_count": 42,
  "created_at": "2025-01-01T00:00:00Z",
  "model": null,
  "provider": null,
  "script": null
}

Job Lifecycle States

State Meaning
scheduled Active, will fire at next scheduled time
paused Suspended — won't fire until resumed
completed Repeat count exhausted or one-shot that has fired
running Currently executing (transient state)

Backward Compatibility

Older jobs may have a single skill field instead of the skills array. The scheduler normalizes this at load time — single skill is promoted to skills: [skill].

Scheduler Runtime

Tick Cycle

The scheduler runs on a periodic tick (default: every 60 seconds):

tick()
  1. Acquire scheduler lock (prevents overlapping ticks)
  2. Load all jobs from jobs.json
  3. Filter to due jobs (next_run <= now AND state == "scheduled")
  4. For each due job:
     a. Set state to "running"
     b. Create fresh AIAgent session (no conversation history)
     c. Load attached skills in order (injected as user messages)
     d. Run the job prompt through the agent
     e. Deliver the response to the configured target
     f. Update run_count, compute next_run
     g. If repeat count exhausted → state = "completed"
     h. Otherwise → state = "scheduled"
  5. Write updated jobs back to jobs.json
  6. Release scheduler lock

Gateway Integration

In gateway mode, the scheduler tick is integrated into the gateway's main event loop. The gateway calls scheduler.tick() on its periodic maintenance cycle, which runs alongside message handling.

In CLI mode, cron jobs only fire when hermes cron commands are run or during active CLI sessions.

Fresh Session Isolation

Each cron job runs in a completely fresh agent session:

  • No conversation history from previous runs
  • No memory of previous cron executions (unless persisted to memory/files)
  • The prompt must be self-contained — cron jobs cannot ask clarifying questions
  • The cronjob toolset is disabled (recursion guard)

Skill-Backed Jobs

A cron job can attach one or more skills via the skills field. At execution time:

  1. Skills are loaded in the specified order
  2. Each skill's SKILL.md content is injected as context
  3. The job's prompt is appended as the task instruction
  4. The agent processes the combined skill context + prompt

This enables reusable, tested workflows without pasting full instructions into cron prompts. For example:

Create a daily funding report → attach "ai-funding-daily-report" skill

Script-Backed Jobs

Jobs can also attach a Python script via the script field. The script runs before each agent turn, and its stdout is injected into the prompt as context. This enables data collection and change detection patterns:

# ~/.hermes/scripts/check_competitors.py
import requests, json
# Fetch competitor release notes, diff against last run
# Print summary to stdout — agent analyzes and reports

Delivery Model

Cron job results can be delivered to any supported platform:

Target Syntax Example
Origin chat origin Deliver to the chat where the job was created
Local file local Save to ~/.hermes/cron/output/
Telegram telegram or telegram:<chat_id> telegram:-1001234567890
Discord discord or discord:#channel discord:#engineering
Slack slack Deliver to Slack home channel
WhatsApp whatsapp Deliver to WhatsApp home
Signal signal Deliver to Signal
Matrix matrix Deliver to Matrix home room
Mattermost mattermost Deliver to Mattermost home
Email email Deliver via email
SMS sms Deliver via SMS
Home Assistant homeassistant Deliver to HA conversation
DingTalk dingtalk Deliver to DingTalk
Feishu feishu Deliver to Feishu
WeCom wecom Deliver to WeCom

For Telegram topics, use the format telegram:<chat_id>:<thread_id> (e.g., telegram:-1001234567890:17585).

Response Wrapping

By default (cron.wrap_response: true), cron deliveries are wrapped with:

  • A header identifying the cron job name and task
  • A footer noting the agent cannot see the delivered message in conversation

The [SILENT] prefix in a cron response suppresses delivery entirely — useful for jobs that only need to write to files or perform side effects.

Session Isolation

Cron deliveries are NOT mirrored into gateway session conversation history. They exist only in the cron job's own session. This prevents message alternation violations in the target chat's conversation.

Recursion Guard

Cron-run sessions have the cronjob toolset disabled. This prevents:

  • A scheduled job from creating new cron jobs
  • Recursive scheduling that could explode token usage
  • Accidental mutation of the job schedule from within a job

Locking

The scheduler uses file-based locking to prevent overlapping ticks from executing the same due-job batch twice. This is important in gateway mode where multiple maintenance cycles could overlap if a previous tick takes longer than the tick interval.

CLI Interface

The hermes cron CLI provides direct job management:

hermes cron list                    # Show all jobs
hermes cron create                  # Interactive job creation (alias: add)
hermes cron edit <job_id>           # Edit job configuration
hermes cron pause <job_id>          # Pause a running job
hermes cron resume <job_id>         # Resume a paused job
hermes cron run <job_id>            # Trigger immediate execution
hermes cron remove <job_id>         # Delete a job