Files
agents_and_robots/shell/cron/scheduler.go
T
egutierrez 4dfc6cf0b9 feat: implementar shell/cron — scheduler autónomo para bots
Nuevo paquete shell/cron con dos archivos:

shell/cron/scheduler.go — Scheduler struct con método Start(ctx) que:
  - Registra todas las entradas de config.ScheduleCfg como jobs de robfig/cron
  - Omite schedules sin output_room o sin action.kind (warn en log)
  - Bloquea hasta que ctx sea cancelado, luego detiene el cron limpiamente
  - Recibe MatrixSender, CompleteFunc y *slog.Logger como dependencias (sin importar agents/)

shell/cron/actions.go — ejecutores para fase 1:
  - send_message: resuelve contenido desde Message (inline) o Template (archivo .md),
    luego llama a matrix.SendMarkdown
  - llm_prompt: resuelve prompt desde Prompt o Template, llama al LLM y envía
    la respuesta al room configurado; no-op silencioso si no hay LLM

resolveContent() prioriza texto inline sobre ruta de archivo, lo que permite
tanto mensajes cortos en YAML como prompts largos en archivos .md separados.

Fase 2 (run_tool) y fase 3 (inter-bot) quedan pendientes según el issue.
2026-03-08 19:00:32 +00:00

94 lines
2.4 KiB
Go

// Package cron provides a scheduler for autonomous bot activity.
// It is part of the impure shell: it reads files, calls LLMs, and sends Matrix messages.
package cron
import (
"context"
"log/slog"
"github.com/robfig/cron/v3"
"github.com/enmanuel/agents/internal/config"
coretypes "github.com/enmanuel/agents/pkg/llm"
)
// MatrixSender is the subset of matrix.Client needed by the scheduler.
type MatrixSender interface {
SendMarkdown(ctx context.Context, roomID, markdown string) error
}
// Scheduler fires configured schedules and executes send_message or llm_prompt actions.
type Scheduler struct {
cfg []config.ScheduleCfg
matrix MatrixSender
llm coretypes.CompleteFunc // nil when agent has no LLM
model string
logger *slog.Logger
cron *cron.Cron
}
// New creates a Scheduler. llm and model are optional (nil/empty for agents without LLM).
func New(
cfg []config.ScheduleCfg,
matrix MatrixSender,
llm coretypes.CompleteFunc,
model string,
logger *slog.Logger,
) *Scheduler {
return &Scheduler{
cfg: cfg,
matrix: matrix,
llm: llm,
model: model,
logger: logger.With("component", "cron"),
cron: cron.New(),
}
}
// Start registers all schedules and starts the cron loop.
// It returns when ctx is cancelled, stopping the cron runner.
func (s *Scheduler) Start(ctx context.Context) {
for _, sc := range s.cfg {
sc := sc // capture range var
if sc.Cron == "" || sc.Action.Kind == "" {
s.logger.Warn("skipping invalid schedule", "name", sc.Name, "cron", sc.Cron, "kind", sc.Action.Kind)
continue
}
room := sc.OutputRoom
if room == "" {
s.logger.Warn("schedule has no output_room, skipping", "name", sc.Name)
continue
}
handler := s.buildHandler(sc)
if handler == nil {
s.logger.Warn("unsupported action kind, skipping", "name", sc.Name, "kind", sc.Action.Kind)
continue
}
_, err := s.cron.AddFunc(sc.Cron, func() {
handler(ctx, room)
})
if err != nil {
s.logger.Error("failed to register schedule",
"name", sc.Name,
"cron", sc.Cron,
"err", err,
)
continue
}
s.logger.Info("schedule registered", "name", sc.Name, "cron", sc.Cron, "kind", sc.Action.Kind, "room", room)
}
s.cron.Start()
s.logger.Info("cron scheduler started", "schedules", len(s.cfg))
<-ctx.Done()
s.logger.Info("cron scheduler stopping")
cronCtx := s.cron.Stop()
<-cronCtx.Done()
s.logger.Info("cron scheduler stopped")
}