Skip to content

Schedules and channels

Most runs start from an HTTP request. Triggers are the other two ways a run begins: a schedule fires a prompt on a clock, and a channel fires on an inbound event. Both run only while the agent is served (maiden serve), and both share the same single runtime lock as the HTTP server — so a triggered run and a request never overlap.

A schedule fires a fixed prompt on a fixed interval. Create a file under schedules/:

my-agent/schedules/heartbeat.toml
prompt = "summarize anything new since the last tick"
every_seconds = 300
KeyTypeDescription
promptstringThe message sent on each fire.
every_secondsintegerSeconds between fires.

Each schedule runs on its own thread, named after the file. The prompt runs through the normal agent loop, so it can call tools and its history accumulates on that thread.

A channel starts a run from an inbound event. Today the only kind is file-drop: maiden watches a directory, and each JSON file dropped into it becomes a message.

my-agent/channels/inbox.toml
kind = "file-drop"
dir = "/var/spool/my-agent-inbox"
KeyTypeDescription
kindstringChannel type. Must be file-drop.
dirstringDirectory to watch for *.json files.

Drop a file like {"message": "process order 1234"} into that directory, and maiden runs the agent with that message. Each file is processed exactly once: after the run, the file is moved into a .processed/ subdirectory, so a restart never re-runs an already-handled message.

Terminal window
echo '{"message":"hello from a dropped file"}' > /var/spool/my-agent-inbox/msg.json

Triggers start automatically with the server:

Terminal window
maiden serve my-agent
# [trigger] schedule 'heartbeat' every 300s
# [trigger] channel 'inbox' watching /var/spool/my-agent-inbox

They log each run’s outcome to stderr. Because everything shares one lock, a busy schedule or channel will queue behind in-flight work rather than run in parallel — see Execution model.