agents automation the-hub homelab

Agents That Wake Up: From Chatbots to Proactive Teammates

Building a heartbeat system that lets AI agents act on their own schedule — birthday reminders, GitHub scans, TV recommendations, and more.

March 7, 2026 · 7 min read · Michael Schilling
A row of glowing orbs on a dark timeline, each pulsing with light at different intervals like heartbeats on a monitor

Part 10 of the series: Building The Hub

The most useful thing my agents do is something I never ask them to do.

Every morning at 08:30, Nightcrawler checks my contacts database for upcoming birthdays. If someone’s birthday is today or in the next few days, a message appears in my DM. On Mondays, I get a weekly digest of the entire upcoming week. On days with no birthdays, nothing — no noise, no “nothing to report” messages. Just silence, which is exactly right.

This is the shift that transformed my agents from tools into teammates: they started waking up on their own.

The heartbeat system

When I first built Nightcrawler, it had two tracks: a chat track for conversations and a heartbeat track for background processing. The heartbeat was a systemd timer firing every 15 minutes, originally just checking the state of The Hub for changes.

That heartbeat infrastructure turned out to be the foundation for something much more powerful. Instead of a single background check, the heartbeat runner now dispatches multiple scheduled skills. Each skill has a definition file with a cron expression, a handler function, and a target — which agent should run it and where the output goes (a DM, a channel, or nowhere).

The systemd timer still fires every 15 minutes. But now the skill runner checks what’s due, and only executes the skills whose cron expressions match the current window. A skill scheduled for 08:30 daily will only fire once, during the 08:30 heartbeat cycle. A skill scheduled for every 6 hours will fire four times a day. The timer is the clock; the skill definitions are the schedule.

A system diagram showing a central heartbeat timer connecting to multiple skill nodes, each with a clock icon showing different schedules, with output arrows pointing to chat DMs and channels

Nightcrawler’s birthday reminders

The birthday skill was the first proactive feature, and it’s still one of my favorites. Every morning, Nightcrawler queries the MIRS MCP server — the contacts database that holds phone numbers, emails, birthdays, and notes for everyone in my network. It runs a SQL query looking for contacts whose birthdays fall within the next few days.

One gotcha that took a minute to debug: the MIRS server returns query results wrapped in a {result: [...]} object, not as a bare array. My initial code tried to iterate over the response directly and got nothing. A small thing, but the kind of detail that wastes fifteen minutes if you’re not expecting it.

The logic branches based on the day of the week. Monday through Saturday, it checks today and the next two days. On Monday specifically, it also generates a weekly digest covering the full upcoming week. The messages are conversational — Nightcrawler doesn’t dump a data table, it writes something like “Heads up, Mein Freund — your friend Alex turns 35 tomorrow. Might be worth a message.”

If there are no upcoming birthdays, the skill exits silently. No notification, no empty message. This was a deliberate design choice. Proactive agents should only speak when they have something worth saying.

JARVIS’s GitHub scanner

JARVIS runs a daily scan at 08:30 across all my GitHub repositories. It checks for open pull requests, failing CI workflows, and Dependabot security alerts. The findings get posted to an alerts channel where I can review them over coffee.

The scanner uses the GitHub MCP tools to list repositories and then checks each one for actionable items. Pull requests older than 7 days get flagged as stale — a reminder that something is sitting unreviewed. Failing CI runs surface immediately. Dependabot alerts get categorized by severity.

The first time I ran it, JARVIS found 15 findings across 62 repositories. Most were stale Dependabot alerts on repos I hadn’t touched in months, but a few were legitimate — a PR I’d forgotten about, a CI pipeline that had been red for a week without me noticing. The value was immediate and obvious.

JARVIS posts to a shared channel rather than a DM, which means other agents can see the findings too. If I ask Gideon for a project status overview later in the day, Gideon can reference what JARVIS already surfaced. The agents build on each other’s work.

SAGE’s TV recommendations

This one is pure quality of life. Every evening at 17:00, SAGE checks what’s on TV tonight and gives me personalized recommendations.

The pipeline starts with the NPO MCP server, which queries the Dutch public broadcasting schedule. Building this MCP server was its own adventure — the NPO website is a Next.js application, and I needed to extract data from their internal API. The key challenge was that Next.js apps embed a buildId in their data URLs, and this ID changes every time the site deploys. The server extracts the current buildId dynamically, caches it for an hour, and automatically retries with a fresh ID if it gets a 404. Without this, the server would break silently every time NPO deployed their website.

With the schedule data in hand, SAGE loads my personal favorites — a list of shows, genres, and preferences stored in The Hub. Then it uses an LLM to curate the recommendations, matching tonight’s schedule against my preferences and writing a natural-language summary of what’s worth watching.

The Gemini integration for SAGE’s recommendation engine hit an unexpected wall. My initial system prompt used structured XML-style tags to define the output format. Gemini’s content parser interpreted these tags as part of a multi-turn conversation format and threw an INVALID_ARGUMENT error. The fix was two-fold: strip the system prompt down to a minimal, dedicated prompt file without complex markup, and disable the chat format wrapper that was prepending [user]: prefixes to the prompt. The prefixes were triggering Gemini’s multi-turn conversation parser, which then choked on the XML-ish content.

SAGE posts the recommendations to my DM around 17:00 — early enough to plan the evening, late enough that the schedule data is final. If nothing matches my preferences, SAGE says so briefly and moves on. No filler.

The pattern

All of these scheduled skills follow the same pattern:

  1. Skill definition — a file that declares the cron expression, the handler module, the target agent, and the output destination.
  2. Handler function — the actual logic. Query data, process it, format a message.
  3. Heartbeat dispatcher — the runner that checks which skills are due and invokes their handlers.
  4. Output routing — the handler returns a message, and the dispatcher sends it to the configured destination (DM, channel, or silent).

Adding a new proactive skill means writing a handler and a skill definition. The infrastructure handles scheduling, dispatch, and delivery. It’s the same “share the plumbing, isolate the logic” principle that makes the multi-agent architecture work.

Beyond the agent skills, the heartbeat system also runs maintenance tasks. A capture processor fires at 23:00 daily, processing any captures that accumulated during the day. A Hub sync task runs three times a day, keeping the agents’ local context up to date with the latest changes to The Hub. These aren’t flashy, but they keep the system healthy without any manual intervention.

Why this matters

The difference between a chatbot and an agent is initiative. A chatbot waits for you to type something. An agent notices things and acts on them. When JARVIS flags a stale PR at 08:30 and I merge it before standup, that’s value I didn’t have to ask for. When Nightcrawler reminds me about a friend’s birthday, that’s a relationship I might have let slip.

Proactive features also change how you think about your agents. They stop being tools you reach for and become teammates who are paying attention. I check my morning messages now not just for Slack notifications, but for what my agents surfaced overnight. It’s a subtle shift, but it fundamentally changes the relationship with the system.

The heartbeat pattern is simple — a timer, a skill registry, some cron expressions. But simple infrastructure that enables autonomous behavior is exactly the kind of leverage that makes a homelab AI setup worth building. My agents don’t just answer questions anymore. They wake up, look around, and tell me what matters.

#agents #automation #the-hub #homelab