The weekly-digest agent is a Cloudflare Pages Function wired to cron (0 9 * * 6, Saturday mornings). It fans out across four sources looking for mentions of "proactive agents," deduplicates what it finds against previous results, clusters the survivors by topic using an LLM, and upserts a single GitHub issue labeled weekly-digest. A run takes about twelve seconds.
I wanted to write about it because every other post on this site is kind of theoretical. We talk about the three primitives, about the webhook tax, about what a magical agent would do if it existed. The weekly-digest agent is the one we actually built and tested. It has a git history with embarrassing commits and a log line that reads: "Found 30 new mention(s) across 4 sources, deduped, clustered into 4 topic(s)."
So here are the receipts.
The full pipeline — sources to GitHub issue.
How it works
The trigger is cron: 0 9 * * 6. A relaycron HTTP webhook fires the Cloudflare function. The function does four things in sequence, though the first step fans out in parallel.
Step 1: Gather. The agent queries Brave Search with two terms ("proactive agents" and "proactive AI agent") and hits Reddit's JSON API for three subreddits: r/LocalLLaMA, r/AI_Agents, and r/MachineLearning. All four requests go out simultaneously via Promise.allSettled(), so a single flaky source doesn't block the rest.
Step 2: Dedup. The agent loads /_internal/weekly-digest/seen.json from the repo's virtual filesystem (VFS backed by GitHub, using ctx.files.read). Any URL that appeared in a prior run gets dropped. This is the simplest possible dedup: a flat JSON file of URLs, updated after each successful run.
Step 3: Cluster. The surviving mentions go to OpenRouter calling google/gemini-2.5-flash at temperature 0.2 with JSON mode enabled. The system prompt says one thing clearly: "False positives are worse than misses." We'd rather surface three real clusters than five where two are noise.
Step 4: Publish. The agent upserts a single rolling GitHub issue per ISO week, tagged with the weekly-digest label. One issue, updated in place if the agent runs again. No Slack pings, no email, no notification firehose.
If no fresh mentions survive dedup, the agent logs "skipped — quiet week" and returns early. No issue is created for empty weeks.
Here's what the output actually looks like in the GitHub issue:
Weekly digest — week of 2026-05-03
### Proactive agent architectures (3)
- OpenAI's push toward proactive AI — `brave`
> Pulse represents OpenAI's first real attempt at proactive delivery
- Building always-on agents with webhooks — `reddit`
> Thread discussing webhook vs polling for agent triggers
- Proactive Agent Skill v3.1 released — `brave`
### Agent reliability patterns (2)
- Why your AI agent needs a WAL — `reddit`
> Discussion of write-ahead logging for agent state persistence
- Checkpoint-based agent recovery — `brave`
Each cluster gets a heading with a count, each mention gets a linked title with the source and an excerpt. The whole thing is scannable in under a minute.
What worked immediately
Some parts of this agent were surprisingly painless. Worth calling out, because it's not all struggle.
The cron trigger just worked. We wired it to relaycron, pointed it at the function URL, and the first test run completed without incident. Cron is solved infrastructure, and when you lean on something solved, it shows.
The fan-out pattern was right from the start. Promise.allSettled was an obvious choice, but sometimes the obvious choice is obvious because it's correct. We never had to revisit the gather step's concurrency model. Each source returns its results independently, failures are isolated, and the merge step downstream doesn't care which sources contributed.
Dedup via a flat JSON file is laughably simple and works fine. At our scale (roughly 30 mentions per week, maybe 1,500 per year), a flat file of URLs is perfectly adequate. There's no bloom filter, no Redis set, no expiry policy. The file grows by a few kilobytes per week and we can read the whole thing in a glance. Sometimes the right data structure is a list.
A timeline of things that broke — in order of discovery.
What broke
The first week the agent ran, it produced nothing. The second week, it produced garbage. The third week, it worked. Here's what happened in between.
env() vs process.env. The first production bug was the dumbest one. The agent code used process.env.BRAVE_API_KEY, which works fine in Node.js. It does not work on Cloudflare Workers, where secrets are accessed via the env() binding passed to the handler function. The fix was a one-line change. The debugging took an hour because the error wasn't "key not found," it was a silent empty string that made Brave return zero results with a 200 status.
Reddit's JSON API is unreliable. Our primary source for Reddit data is appending .json to subreddit URLs. Simple, no auth required, and it works most of the time. The problem is "most of the time." Reddit returns 429s during traffic spikes and occasionally serves HTML error pages when you ask for JSON. We added a fallback: if the Reddit endpoint returns a 4xx or 5xx, the agent re-queries via Brave using site:reddit.com/r/LocalLLaMA as the search term. The fallback finds fewer results but never fails silently.
Search recall was too narrow. The original queries were too specific. "Proactive agent runtime" returned almost nothing because nobody outside our site uses that phrase. We widened to "proactive agents" and "proactive AI agent," which brought in more noise but also more signal. The clustering step handles the noise; the gather step's job is not to be clever about filtering.
Gemini Flash hallucinated a cluster once. In week three, the clustering step returned a topic called "Enterprise adoption trends" containing two links. One was a Reddit comment about LLaMA quantization that mentioned "proactive" in an unrelated sentence. The other was a Brave result about proactive customer service bots, which has nothing to do with proactive agents in our sense. The temperature was already 0.2. We tightened the system prompt to emphasize that fewer clusters with high confidence beats more clusters with mixed relevance. The hallucinated cluster hasn't reappeared, but I wouldn't call the problem solved.
The delivery choice
When we designed the output channel, the obvious option was Slack. Post the digest to a #mentions channel, let people discuss it. We went with GitHub Issues instead, and the reasoning was about human behavior more than technology.
A Slack message demands attention the moment it arrives. It generates a reply thread. People debate whether mention #14 is actually relevant. The digest becomes a synchronous discussion instead of an asynchronous reference. A GitHub issue sits there. You read it when you have time. You close it when the week is over.
“
The delivery channel shapes behavior more than the content does. A digest in Slack becomes a conversation. A digest in a GitHub issue becomes a reference.
”The restraint is deliberate. The agent files a curated summary somewhere durable and searchable, and goes quiet. I've found that the best production agents are honestly pretty boring to watch. That's the whole point.
Costs
I want to be specific about this because "AI agents are expensive" is a common assumption that doesn't hold at this scale.
| Resource | Usage | Cost |
|---|---|---|
| Brave Search API | ~8 queries/month (2 per run × 4 Saturdays) | Free tier (2,000/month) |
| OpenRouter (Gemini Flash) | 1 call/week, small payload | Fractions of a cent |
| GitHub API | 1–2 calls/week (read issue + upsert) | Free |
| Cloudflare Pages Function | 4 invocations/month, ~12s each | Free tier |
| VFS (GitHub repo storage) | One small JSON file | Free |
Total monthly cost: effectively zero. The Brave free tier gives us 250x more queries than we use. The OpenRouter call costs less than a penny. The GitHub API has no meaningful rate limit at this volume. The Cloudflare function runs for under a minute per month total.
At this price point, the "is this worth running?" question never comes up. It basically costs nothing.
What we'd change
Three things, in order of likelihood we'll actually do them.
Better clustering validation. The Gemini Flash hallucination wasn't catastrophic, but it eroded trust for a week. We're considering a second pass where the agent checks each cluster's links against the cluster label and drops any that don't cohere. This doubles the LLM cost from "nearly nothing" to "nearly nothing times two," so the cost argument is irrelevant. The engineering time to wire it up is the real constraint.
Source expansion. Hacker News is an obvious addition. So is Twitter/X, though the API pricing makes it impractical on a free-tier budget. We could add a Brave site:news.ycombinator.com query for close to zero cost. The gather step's fan-out design makes adding sources trivial to implement, which was the whole point of that architecture.
Data-triggered runs. Right now the agent is purely cron-driven. If a mention spikes on a Wednesday, we don't know until Saturday. For some sources, a data trigger would make more sense: watch an RSS feed or a webhook and fire the pipeline when something appears, not when the clock ticks. This is the M2 roadmap for us, replacing some cron triggers with real-time listener events. The weekly cadence would remain as the default for sources that don't support push.
The weekly-digest agent is not sophisticated. A cron job, four API calls, a dedup file, one LLM call, and a GitHub issue. Some weeks it finds a ton, some weeks it finds nothing. Both are fine. It looks so we don't have to, and stays quiet when there's nothing to report.
Posted May 11, 2026· AgentWorkforce
Issues, PRs, and arguments welcome on GitHub. Or email [email protected].