Tareas programadas
Pide a un agente que haga algo por ti a horas fijas, sin nadie al teclado. Un resumen matinal, una comprobación cada hora, entregado donde quieras.
Tareas recurrentes
Una tarea programada es un trabajo que le encargas una vez a un agente y que a partir de ahí ocurre solo: “cada día laborable a las 9, resume los errores de la noche y envíamelos”. Por debajo, una tarea es un prompt autocontenido, un horario, una zona horaria y un lugar donde entregar el resultado. Cuando se dispara, Pepe ejecuta el agente sobre ese prompt en una sesión nueva y sin historial de chat. No se arrastra nada de ninguna conversación anterior, así que el prompt tiene que decir todo lo que la ejecución necesita (qué hacer, qué datos mirar, la ventana de tiempo).
Crear una tarea desde la CLI
pepe cron add \
--name "morning-brief" \
--agent assistant \
--prompt "Resume las líneas de log de error de las últimas 24 horas y lista los 3 problemas principales." \
--schedule "0 9 * * 1-5" \
--timezone "America/Sao_Paulo" \
--deliver "telegram:123456789"
Solo --name, --prompt y --schedule son obligatorios. El resto tiene valores por defecto razonables:
| Opción | Qué hace | Por defecto |
|---|---|---|
--agent |
Qué agente ejecuta el prompt | Tu agente por defecto |
--timezone |
Zona horaria IANA en la que se lee el horario | La configurada por defecto (ver abajo) |
--model |
Ejecuta esta tarea con una conexión de modelo concreta | El modelo propio del agente |
--deliver |
A dónde va el resultado | none (se registra, no se envía a ningún lado) |
El conjunto completo de comandos:
pepe cron list # todas las tareas, con su próxima ejecución
pepe cron add ... # crea una tarea (ver arriba)
pepe cron run morning-brief # fuerza una ejecución ahora e imprime el resultado
pepe cron disable morning-brief
pepe cron enable morning-brief
pepe cron remove morning-brief
pepe cron logs morning-brief # historial reciente de ejecuciones
Cada tarea recibe un id legible derivado de su nombre (morning-brief). Si ese id ya está ocupado, Pepe le añade un número (morning-brief-2).
Hazlo en el panel
Ejecuta pepe serve y abre la página Scheduled. Lista cada tarea con su próxima hora de ejecución, y te da las mismas acciones como botones: crear una tarea nueva con un formulario, forzar una ejecución ahora, activar o desactivar, editar, eliminar y abrir el historial de una tarea en el mismo lugar. El formulario de creación cubre todo lo que hace la CLI: el agente, el prompt, el horario, la zona horaria, el modelo y a dónde entregar el resultado, incluida la opción “No enviar a ningún lado”. Cuando escribes el horario de una tarea, el panel puede convertir una frase sencilla como “cada día laborable a las 9:30” en la expresión cron correspondiente por ti, usando un modelo configurado, y valida el resultado antes de guardarlo.
Expresiones de horario y zonas horarias
El horario es una expresión cron estándar de 5 campos: minuto hora día-del-mes mes día-de-la-semana.
0 9 * * 1-5 # 09:00, lunes a viernes
*/15 * * * * # cada 15 minutos
0 0 1 * * # medianoche del día 1 de cada mes
30 8 * * * # 08:30 todos los días
Una tarea lleva su propia zona horaria con nombre, no un desfase fijo respecto a UTC. Esto importa porque “las 9am locales” se desplazan respecto a UTC dos veces al año con el horario de verano. Pepe guarda la expresión más un nombre de zona como America/Sao_Paulo o Europe/Berlin, y evalúa el horario en esa zona. En torno a un cambio de horario de verano hace lo sensato: salta hacia adelante en el hueco de primavera y elige el lado tardío del solapamiento de otoño, de modo que un trabajo nunca se dispara dos veces ni desaparece en silencio.
Define tu zona por defecto una vez durante pepe setup. Las tareas que no nombran su propia zona la usan. Si no hay nada configurado, el valor de reserva es UTC.
A dónde va el resultado
El destino de deliver decide qué pasa con la salida de una ejecución:
telegram:<chat_id>lo envía a ese chat de Telegram. El mensaje lleva como prefijo el nombre de la tarea, para que un chat que recibe varias tareas pueda distinguirlas.noneno lo envía a ningún lado. La ejecución se realiza igualmente y queda registrada en el historial. Bueno para tareas cuyo único fin es un efecto secundario (escribir un archivo, llamar a una herramienta).- Cualquier otra cosa (incluido
log) escribe la salida en el log de la aplicación.
Sea cual sea el destino, cada ejecución se añade al archivo de historial propio de esa tarea, así que siempre puedes releer lo que pasó.
El temporizador por minuto y la recuperación
El programador hace un tick cada 30 segundos (a propósito por debajo del minuto, para que una pequeña deriva del reloj nunca le haga perder un minuto). En cada tick mira todas las tareas activas y dispara las que coinciden con el minuto actual en la zona horaria de esa tarea. Un guardián por tarea asegura que un trabajo se dispare como mucho una vez por minuto aunque el tick sea más rápido que eso.
El temporizador vive dentro del proceso de la aplicación, así que solo corre mientras pepe serve o pepe gateway está en pie, y nunca durante un comando de una sola ejecución. Cada tarea que toca se ejecuta en su propio proceso, así que varias tareas que caen en el mismo minuto se disparan a la vez, y una tarea lenta nunca bloquea a otra. Las definiciones de las tareas se guardan en ~/.pepe/config.json, bajo "crons".
Si el proceso estaba caído en el momento en que una tarea debía dispararse, Pepe hace una recuperación acotada al reiniciar. Cuando vuelve y nota que pasó una ranura programada sin ejecución, dispara ese trabajo una vez, siempre que aún esté dentro de una ventana de gracia (la mitad del periodo del trabajo, acotada entre 2 minutos y 2 horas). La recuperación está anclada a la ranura perdida, así que un solo reinicio nunca dispara dos veces. Un trabajo que estuvo caído mucho más tiempo que su ventana de gracia simplemente se retoma en su próxima ranura normal, en lugar de repetir una vieja.
Una tarea no se ejecuta encima de sí misma
Una tarea cuya ejecución anterior sigue en marcha cuando llega su siguiente turno se omite, y la omisión se escribe en su historial.
Se omite en lugar de acumularse porque una tarea aquí no es un script idempotente, es un turno de agente. Cuesta una llamada al modelo, tiene efectos secundarios (un mensaje entregado, un archivo escrito), y cada ejecución de la misma tarea comparte un único espacio de trabajo del agente. Un trabajo que tarda siete minutos con una programación de cinco se acumularía: dos ejecuciones, luego tres, luego cuatro, cada una facturada, el informe entregado dos veces, y dos ejecuciones escribiendo una encima de la otra. Te enterarías por la factura.
Y nunca se omite en silencio. La omisión queda como una entrada fallida en el historial, y dice qué va mal:
⏭️ skipped: the previous run was still going. This job takes longer than its own schedule allows.
Esa entrada es el objetivo. Sin ella, el trabajo simplemente dejaría de ocurrir, puntualmente, y la primera señal sería que aquello que hacía había dejado de hacerse.
pepe cron add --name "digest" --prompt "..." --schedule "*/5 * * * *" --overlap
--overlap (o "overlap": true en la configuración) la ejecuta igualmente, para la tarea en la que la concurrencia es realmente lo que quieres.
Historial de ejecuciones
Cada disparo, ya sea del temporizador, de un pepe cron run forzado, de un botón del panel o de un chat, añade una línea al archivo de historial de esa tarea (<PEPE_HOME>/data/cron_logs/<id>.jsonl). Cada línea registra la marca de tiempo, la fuente, si tuvo éxito y la salida (recortada).
pepe cron logs morning-brief
✦ Runs of morning-brief
✅ 2026-07-06 09:00 · scheduler
3 issues overnight. Top: DB connection pool exhausted (x42), ...
⚠️ 2026-07-05 09:00 · scheduler
error: :timeout
El campo source de cada línea es uno de scheduler (lo disparó el temporizador), manual (lo forzaste desde la CLI o el panel) o agent (lo forzó un chat).
Hazlo por chat
Un agente puede crear y gestionar sus propias tareas programadas durante una conversación, en el chat de la CLI o en cualquier canal conectado, si tiene la herramienta schedule_task en su conjunto. Pídelo en lenguaje natural:
Cada día laborable a las 8:30 de mi hora, revisa la página de estado y avísame aquí si algo está degradado.
El agente conoce la hora local actual (su system prompt está anclado con ella), así que “mañana a las 8:30” se resuelve a la ranura correcta en vez de derivar a UTC. Escribe por ti el prompt completo y autocontenido, elige la expresión cron y, por defecto, entrega el resultado de vuelta al mismo chat desde el que preguntaste.
La herramienta schedule_task admite las mismas acciones que la CLI: create, list, run (forzar ahora para previsualizar), enable, disable, remove e history.
La doble aprobación
Crear trabajo programado desde el chat está deliberadamente protegido dos veces, porque una tarea se ejecuta sola más tarde:
- La herramienta tiene que estar en la lista de permitidos del agente. Un agente nuevo nace con
schedule_taskhabilitada, como cualquier otra herramienta; quítala de la lista del agente si nunca debería poder programar nada. - Cada creación igual te pregunta.
schedule_taskes una herramienta con control, así que a menos que se haya preaprobado, el runtime te pide autorizar la llamada concreta antes de que surta efecto. Cada superficie muestra ese aviso a su manera nativa (botones en línea en Telegram, un menú con las flechas del teclado en la terminal). Puedes responder solo por esta vez, por el resto de la sesión, siempre (recordado en el agente) o denegar.
Así una tarea nunca aparece a tus espaldas: la capacidad se puede quitar por agente, y cada tarea concreta sigue dependiendo de tu autorización por encima de eso.