Запланированные задачи (Cron)

Планируйте автоматический запуск задач с помощью естественного языка или cron-выражений. Hermes предоставляет управление cron через единый инструмент cronjob с операциями в стиле действий, вместо отдельных инструментов для создания, просмотра и удаления.

Что умеет cron

Cron-задачи могут:

Всё это доступно самому Hermes через инструмент cronjob, так что вы можете создавать, приостанавливать, редактировать и удалять задачи, просто попросив об этом на обычном языке — без необходимости использовать CLI.

Сессии, запущенные через cron, не могут рекурсивно создавать новые cron-задачи. Hermes отключает инструменты управления cron внутри выполнения cron-задач, чтобы предотвратить бесконечные циклы планирования.

Создание запланированных задач

В чате через /cron

/cron add 30m "Remind me to check the build"
/cron add "every 2h" "Check server status"
/cron add "every 1h" "Summarize new feed items" --skill blogwatcher
/cron add "every 1h" "Use both skills and combine the result" --skill blogwatcher --skill maps

Из автономного CLI

hermes cron create "every 2h" "Check server status"
hermes cron create "every 1h" "Summarize new feed items" --skill blogwatcher
hermes cron create "every 1h" "Use both skills and combine the result" \
  --skill blogwatcher \
  --skill maps \
  --name "Skill combo"

Через естественный диалог

Просто попросите Hermes обычным языком:

Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.

Hermes использует единый инструмент cronjob внутри.

Cron-задачи с навыками

Cron-задача может загружать один или несколько навыков перед выполнением промпта.

Один навык

cronjob(
    action="create",
    skill="blogwatcher",
    prompt="Check the configured feeds and summarize anything new.",
    schedule="0 9 * * *",
    name="Morning feeds",
)

Несколько навыков

Навыки загружаются по порядку. Промпт становится инструкцией, добавленной поверх этих навыков.

cronjob(
    action="create",
    skills=["blogwatcher", "maps"],
    prompt="Look for new local events and interesting nearby places, then combine them into one short brief.",
    schedule="every 6h",
    name="Local brief",
)

Это полезно, когда вы хотите, чтобы запланированный агент наследовал переиспользуемые рабочие процессы без необходимости вставлять полный текст навыка в сам cron-промпт.

Запуск задачи внутри директории проекта

Cron-задачи по умолчанию запускаются в отрыве от какого-либо репозитория — не загружаются AGENTS.md, CLAUDE.md или .cursorrules, а инструменты terminal / file / code-exec запускаются из рабочей директории, в которой был запущен шлюз. Передайте --workdir (CLI) или workdir= (вызов инструмента), чтобы изменить это:

# Standalone CLI (schedule and prompt are positional)
hermes cron create "every 1d at 09:00" \
  "Audit open PRs, summarize CI health, and post to #eng" \
  --workdir /home/me/projects/acme
# From a chat, via the cronjob tool
cronjob(
    action="create",
    schedule="every 1d at 09:00",
    workdir="/home/me/projects/acme",
    prompt="Audit open PRs, summarize CI health, and post to #eng",
)

Когда workdir установлен:

note Сериализация Задачи с workdir выполняются последовательно на каждом такте планировщика, а не в параллельном пуле. Это сделано намеренно — TERMINAL_CWD является глобальным для процесса, поэтому две задачи с workdir, запущенные одновременно, повредили бы рабочие директории друг друга. Задачи без workdir по-прежнему выполняются параллельно.

Редактирование задач

Вам не нужно удалять и создавать задачи заново, чтобы изменить их.

Чат

/cron edit <job_id> --schedule "every 4h"
/cron edit <job_id> --prompt "Use the revised task"
/cron edit <job_id> --skill blogwatcher --skill maps
/cron edit <job_id> --remove-skill blogwatcher
/cron edit <job_id> --clear-skills

Автономный CLI

hermes cron edit <job_id> --schedule "every 4h"
hermes cron edit <job_id> --prompt "Use the revised task"
hermes cron edit <job_id> --skill blogwatcher --skill maps
hermes cron edit <job_id> --add-skill maps
hermes cron edit <job_id> --remove-skill blogwatcher
hermes cron edit <job_id> --clear-skills

Примечания:

Действия жизненного цикла

Cron-задачи теперь имеют более полный жизненный цикл, чем просто создание/удаление.

Чат

/cron list
/cron pause <job_id>
/cron resume <job_id>
/cron run <job_id>
/cron remove <job_id>

Автономный CLI

hermes cron list
hermes cron pause <job_id>
hermes cron resume <job_id>
hermes cron run <job_id>
hermes cron remove <job_id>
hermes cron status
hermes cron tick

Что они делают:

Как это работает

Выполнение cron обрабатывается демоном шлюза. Шлюз запускает такты планировщика каждые 60 секунд, выполняя все подлежащие запуску задачи в изолированных сессиях агента.

hermes gateway install     # Install as a user service
sudo hermes gateway install --system   # Linux: boot-time system service for servers
hermes gateway             # Or run in foreground

hermes cron list
hermes cron status

Поведение планировщика шлюза

На каждом такте Hermes:

  1. загружает задачи из ~/.hermes/cron/jobs.json

  2. проверяет next_run_at относительно текущего времени

  3. запускает новую сессию AIAgent для каждой подлежащей запуску задачи

  4. опционально внедряет один или несколько прикреплённых навыков в эту новую сессию

  5. выполняет промпт до завершения

  6. доставляет финальный ответ

  7. обновляет метаданные запуска и следующее запланированное время

Файловая блокировка в ~/.hermes/cron/.tick.lock предотвращает перекрытие тактов планировщика, чтобы одна и та же группа задач не была запущена дважды.

Варианты доставки

При планировании задач вы указываете, куда будет направлен результат:

Вариант Описание Пример
"origin" Туда, где была создана задача По умолчанию в мессенджерах
"local" Сохранить только в локальные файлы (~/.hermes/cron/output/) По умолчанию в CLI
"telegram" Домашний канал Telegram Использует TELEGRAM_HOME_CHANNEL
"telegram:123456" Конкретный чат Telegram по ID Прямая доставка
"telegram:-100123:17585" Конкретная тема Telegram Формат chat_id:thread_id
"discord" Домашний канал Discord Использует DISCORD_HOME_CHANNEL
"discord:#engineering" Конкретный канал Discord По имени канала
"slack" Домашний канал Slack
"whatsapp" WhatsApp домой
"signal" Signal
"matrix" Домашняя комната Matrix
"mattermost" Домашний канал Mattermost
"email" Email
"sms" SMS через Twilio
"homeassistant" Home Assistant
"dingtalk" DingTalk
"feishu" Feishu/Lark
"wecom" WeCom
"weixin" Weixin (WeChat)
"bluebubbles" BlueBubbles (iMessage)
"qqbot" QQ Bot (Tencent QQ)
"all" Разослать на все подключённые домашние каналы Определяется в момент запуска
"telegram,discord" Разослать на конкретный набор каналов Список через запятую
"origin,all" Доставить в исходный чат плюс все остальные подключённые каналы Комбинируйте любые токены

Финальный ответ агента доставляется автоматически. Вам не нужно вызывать send_message в cron-промпте.

Маршрутизация через (all)

all позволяет отправить одну cron-задачу на все настроенные каналы мессенджеров, без необходимости перечислять их по имени. Он определяется в момент запуска, так что задача, созданная до подключения Telegram, автоматически его учтёт на следующем такте после установки TELEGRAM_HOME_CHANNEL.

Семантика: all расширяется до всех платформ с настроенным домашним каналом. Ноль каналов — это нормально; задача просто не будет иметь целей доставки, что будет зафиксировано как ошибка доставки.

all комбинируется с явными целями. origin,all доставляет в исходный чат плюс все остальные подключённые домашние каналы, дедуплицируя по (platform, chat_id, thread_id).

Обёртка ответа

По умолчанию доставляемый cron-вывод оборачивается в заголовок и подвал, чтобы получатель знал, что это запланированная задача:

Cronjob Response: Morning feeds
-------------

<agent output here>

Note: The agent cannot see this message, and therefore cannot respond to it.

Чтобы доставлять необработанный вывод агента без обёртки, установите cron.wrap_response в false:

# ~/.hermes/config.yaml
cron:
  wrap_response: false

Тихое подавление

Если финальный ответ агента начинается с [SILENT], доставка полностью подавляется. Вывод всё равно сохраняется локально для аудита (в ~/.hermes/cron/output/), но сообщение не отправляется цели доставки.

Это полезно для мониторинговых задач, которые должны сообщать только о проблемах:

Check if nginx is running. If everything is healthy, respond with only [SILENT].
Otherwise, report the issue.

Неудачные задачи всегда доставляются независимо от маркера [SILENT] — только успешные запуски могут быть подавлены.

Тайм-аут скрипта

Предварительные скрипты (прикреплённые через параметр script) имеют стандартный тайм-аут 120 секунд. Если вашим скриптам нужно больше времени — например, для включения случайных задержек, чтобы избежать ботоподобных шаблонов — вы можете увеличить это значение:

# ~/.hermes/config.yaml
cron:
  script_timeout_seconds: 300   # 5 minutes

Или установите переменную окружения HERMES_CRON_SCRIPT_TIMEOUT. Порядок разрешения: переменная окружения → config.yaml → значение по умолчанию 120с.

Режим без агента (только скрипт)

Для повторяющихся задач, которые не требуют логики LLM — классические сторожевые процессы, оповещения о диске/памяти, пульсации, CI-пинги — передайте no_agent=True при создании. Планировщик запускает ваш скрипт по расписанию и доставляет его stdout напрямую, полностью пропуская агента:

hermes cron create "every 5m" \
  --no-agent \
  --script memory-watchdog.sh \
  --deliver telegram \
  --name "memory-watchdog"

Семантика:

Файлы .sh / .bash запускаются через /bin/bash; всё остальное — через текущий интерпретатор Python (sys.executable). Скрипты должны находиться в ~/.hermes/scripts/ (те же правила изоляции, что и для предварительных скриптов).

Агент настраивает это за вас

Схема инструмента cronjob предоставляет no_agent напрямую Hermes, так что вы можете описать сторожевой процесс в чате и позволить агенту настроить его:

Ping me on Telegram if RAM is over 85%, every 5 minutes.

Hermes запишет скрипт проверки в ~/.hermes/scripts/ через write_file, а затем вызовет:

cronjob(action="create", schedule="every 5m",
        script="memory-watchdog.sh", no_agent=True,
        deliver="telegram", name="memory-watchdog")

Он автоматически выбирает no_agent=True, когда содержимое сообщения полностью определяется скриптом (сторожевые процессы, пороговые оповещения, пульсации). Тот же инструмент также позволяет агенту приостанавливать, возобновлять, редактировать и удалять задачи — так что весь жизненный цикл управляется через чат без необходимости прикасаться к CLI.

Смотрите Руководство по cron-задачам только со скриптом с примерами.

Цепочки задач с context_from

Cron-задачи запускаются в изолированных сессиях без памяти о предыдущих запусках. Но иногда вывод одной задачи — это именно то, что нужно следующей. Параметр context_from автоматически устанавливает эту связь — промпт задачи B получает последний вывод задачи A в качестве контекста во время выполнения.

# Job 1: Collect raw data
cronjob(
    action="create",
    prompt="Fetch the top 10 AI/ML stories from Hacker News. Save them to ~/.hermes/data/briefs/raw.md in markdown format with title, URL, and score.",
    schedule="0 7 * * *",
    name="AI News Collector",
)

# Job 2: Triage — receives Job 1's output as context
# Get Job 1's ID from: cronjob(action="list")
cronjob(
    action="create",
    prompt="Read ~/.hermes/data/briefs/raw.md. Score each story 1–10 for engagement potential and novelty. Output the top 5 to ~/.hermes/data/briefs/ranked.md.",
    schedule="30 7 * * *",
    context_from="<job1_id>",
    name="AI News Triage",
)

# Job 3: Ship — receives Job 2's output as context
cronjob(
    action="create",
    prompt="Read ~/.hermes/data/briefs/ranked.md. Write 3 tweet drafts (hook + body + hashtags). Deliver to telegram:7976161601.",
    schedule="0 8 * * *",
    context_from="<job2_id>",
    name="AI News Brief",
)

Как это работает:

Что принимает context_from:

Формат Пример
Один ID задачи (строка) context_from="a1b2c3d4"
Несколько ID задач (список) context_from=["job_a", "job_b"]

Результаты объединяются в порядке перечисления.

Когда использовать:

Восстановление провайдера

Cron-задачи наследуют ваши настроенные резервные провайдеры и ротацию пула учётных данных. Если ключ API первичного провайдера ограничен по скорости или провайдер возвращает ошибку, cron-агент может:

Это делает cron-задачи, запускающиеся с высокой частотой или в часы пик, более устойчивыми — один ключ с ограничением скорости не приведёт к сбою всего запуска.

Форматы расписания

Финальный ответ агента доставляется автоматически — вам не нужно включать send_message в cron-промпт для того же назначения. Если cron-запуск вызывает send_message для той же цели, куда планировщик уже доставит ответ, Hermes пропускает этот дублирующий вызов и сообщает модели, что пользовательский контент должен быть в финальном ответе. Используйте send_message только для дополнительных или других целей.

Относительные задержки (однократно)

30m     → Run once in 30 minutes
2h      → Run once in 2 hours
1d      → Run once in 1 day

Интервалы (повторяющиеся)

every 30m    → Every 30 minutes
every 2h     → Every 2 hours
every 1d     → Every day

Cron-выражения

0 9 * * *       → Daily at 9:00 AM
0 9 * * 1-5     → Weekdays at 9:00 AM
0 */6 * * *     → Every 6 hours
30 8 1 * *      → First of every month at 8:30 AM
0 0 * * 0       → Every Sunday at midnight

ISO-метки времени

2026-03-15T09:00:00    → One-time at March 15, 2026 9:00 AM

Поведение повторения

Тип расписания Повторение по умолчанию Поведение
Однократное (30m, метка времени) 1 Запускается один раз
Интервал (every 2h) бесконечно Запускается до удаления
Cron-выражение бесконечно Запускается до удаления

Вы можете переопределить это:

cronjob(
    action="create",
    prompt="...",
    schedule="every 2h",
    repeat=5,
)

Управление задачами программно

API для агента — это один инструмент:

cronjob(action="create", ...)
cronjob(action="list")
cronjob(action="update", job_id="...")
cronjob(action="pause", job_id="...")
cronjob(action="resume", job_id="...")
cronjob(action="run", job_id="...")
cronjob(action="remove", job_id="...")

При update передайте skills=[], чтобы удалить все прикреплённые навыки.

Наборы инструментов для cron-задач

Cron запускает каждую задачу в новой сессии агента без привязанного чат-канала. По умолчанию cron-агент получает тот набор инструментов, который вы настроили для платформы cron в hermes tools — не CLI по умолчанию, не всё подряд.

hermes tools
# → pick the "cron" platform in the curses UI
# → toggle toolsets on/off just like you would for Telegram/Discord/etc.

Более точное управление для каждой задачи доступно через поле enabled_toolsets в cronjob.create (или для существующей задачи через cronjob.update):

cronjob(action="create", name="weekly-news-summary",
        schedule="every sunday 9am",
        enabled_toolsets=["web", "file"],      # just web + file, no terminal/browser/etc.
        prompt="Summarize this week's AI news: ...")

Когда enabled_toolsets установлен для задачи, он имеет приоритет; иначе применяется конфигурация платформы cron из hermes tools; иначе Hermes использует встроенные значения по умолчанию. Это важно для контроля затрат: включение moa, browser, delegation в каждую крошечную задачу «получить новости» раздувает схему инструментов в каждом LLM-вызове.

Пропуск агента полностью: wakeAgent

Если ваша cron-задача использует предварительный скрипт (через script=), скрипт может решить во время выполнения, должен ли Hermes вообще вызывать агента. Выведите последнюю строку stdout в формате:

{"wakeAgent": false}

…и cron пропустит запуск агента для этого такта. Полезно для частых опросов (каждые 1–5 минут), которые должны пробуждать LLM только когда состояние действительно изменилось — иначе вы будете платить за пустые шаги агента снова и снова.

# pre-check script
import json, sys
latest = fetch_latest_issue_count()
prev = read_state("issue_count")
if latest == prev:
    print(json.dumps({"wakeAgent": False}))   # skip this tick
    sys.exit(0)
write_state("issue_count", latest)
print(json.dumps({"wakeAgent": True, "context": {"new_issues": latest - prev}}))

Когда wakeAgent не указан, значение по умолчанию — true (пробудить агента как обычно).

Цепочки задач: context_from

Cron-задача может получать последний успешный вывод одной или нескольких других задач, перечисляя их имена (или ID) в context_from:

cronjob(action="create", name="daily-digest",
        schedule="every day 7am",
        context_from=["ai-news-fetch", "github-prs-fetch"],
        prompt="Write the daily digest using the outputs above.")

Последние завершённые выводы указанных задач вставляются перед промптом как контекст для этого запуска. Каждая задача должна быть действительным ID или именем (см. cronjob action="list"). Примечание: цепочка читает последний завершённый вывод — она не ждёт выполнения вышестоящих задач в том же такте.

Хранилище задач

Задачи хранятся в ~/.hermes/cron/jobs.json. Вывод запусков задач сохраняется в ~/.hermes/cron/output/{job_id}/{timestamp}.md.

Задачи могут хранить model и provider как null. Когда эти поля опущены, Hermes определяет их во время выполнения из глобальной конфигурации. Они появляются в записи задачи только тогда, когда установлено переопределение для конкретной задачи.

Хранилище использует атомарные файловые записи, поэтому прерванные записи не оставляют частично записанных файлов задач.

Самодостаточные промпты всё ещё важны

warning Важно Cron-задачи запускаются в полностью новой сессии агента. Промпт должен содержать всё, что нужно агенту, что уже не предоставлено прикреплёнными навыками.

ПЛОХО: "Check on that server issue"

ХОРОШО: "SSH into server 192.168.1.100 as user 'deploy', check if nginx is running with 'systemctl status nginx', and verify https://example.com returns HTTP 200."

Безопасность

Промпты запланированных задач проверяются на инъекции и попытки кражи учётных данных при создании и обновлении. Промпты, содержащие невидимые Unicode-трюки, попытки SSH-бэкдоров или очевидные попытки кражи секретов, блокируются.