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)
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
cronjobtoolset is disabled (recursion guard)
Skill-Backed Jobs
A cron job can attach one or more skills via the skills field. At execution time:
- Skills are loaded in the specified order
- Each skill's SKILL.md content is injected as context
- The job's prompt is appended as the task instruction
- 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 |
Deliver to WhatsApp home | |
| Signal | signal |
Deliver to Signal |
| Matrix | matrix |
Deliver to Matrix home room |
| Mattermost | mattermost |
Deliver to Mattermost home |
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