Delete directory 'hermes-automation'
This commit is contained in:
@@ -1,55 +0,0 @@
|
|||||||
# Hermes Automation Patterns
|
|
||||||
|
|
||||||
Production patterns for efficient, low-cost always-on Hermes agents tied to Tony's workflows.
|
|
||||||
|
|
||||||
## Source: Tonbi Hermes Agent Masterclass (Cron & Automation, June 19 2026)
|
|
||||||
|
|
||||||
This directory captures techniques from the **cyrilXBT / Tonbi Hermes Agent Masterclass** series, specifically the **Cron & Automation** session published June 19, 2026. The session covers how to build persistent, cost-effective autonomous agents using Hermes' built-in scheduler, gates, and delivery controls.
|
|
||||||
|
|
||||||
**Link:** [YouTube — Hermes Agent Masterclass (Cron & Automation)](https://www.youtube.com/watch?v=grMNnzCv2gY)
|
|
||||||
|
|
||||||
### Key Techniques
|
|
||||||
|
|
||||||
| Technique | Description | Cost Impact |
|
|
||||||
|-----------|-------------|-------------|
|
|
||||||
| **wakeAgent Gates** | Condition-based triggers that only invoke the LLM when specific criteria are met (instead of running every tick). Prevents unnecessary API calls on irrelevant cycles. | High savings |
|
|
||||||
| **no-agent Polling** | `no_agent=True` flag on cron scripts — runs a shell script directly without any LLM context. Output is delivered verbatim only when there's change or data. Zero token cost per tick when nothing happens. | Max savings |
|
|
||||||
| **context-from Chaining** | Pipe output of one cron job as context into another via `context_from`. Enables multi-stage pipelines (e.g., collector → summarizer → delivery) where each stage only fires on upstream data. | Tiered cost |
|
|
||||||
| **Silent Delivery** | Cron jobs that produce empty stdout on no-change ticks deliver nothing. The user only sees messages when there's actionable output. Reduces noise and delivery friction. | Free (no token) |
|
|
||||||
|
|
||||||
### Goals
|
|
||||||
|
|
||||||
1. **Email Triage** — Automated inbox scanning, priority routing, and draft response generation with approval gates.
|
|
||||||
2. **Skills Hub Monitoring** — Track skill usage, freshness, and curator activity across all active skills.
|
|
||||||
3. **Compliance Monitoring** — Periodic scans of regulatory changes and gap detection against SOP/KB artifacts.
|
|
||||||
4. **Opportunity Scanning** — Scheduled market/tech landscape scans with mechanical filtering and artifact persistence.
|
|
||||||
5. **Daily Briefs** — High-signal daily summaries from multiple sources, delivered at configured times via Telegram or email.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
|
|
||||||
```
|
|
||||||
hermes-automation/
|
|
||||||
├── README.md # This file — overview and masterclass summary
|
|
||||||
├── gates/ # wakeAgent-style gate designs (condition-based triggers)
|
|
||||||
│ ├── email-triage-wakegate.md # Example: email triage poller gate
|
|
||||||
│ └── .gitkeep
|
|
||||||
├── scripts/ # Shell/Python scripts for no-agent polling jobs
|
|
||||||
│ └── .gitkeep
|
|
||||||
├── examples/ # Full worked examples combining gates + scripts + delivery
|
|
||||||
│ └── .gitkeep
|
|
||||||
└── notes/ # Implementation notes, gotchas, config snippets
|
|
||||||
└── .gitkeep
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
Browse the `gates/` directory for condition-based trigger patterns. When adapting a pattern:
|
|
||||||
|
|
||||||
1. Start with a **no-agent script** (cheapest) — just shell/Python collecting and filtering data
|
|
||||||
2. Add a **wakeAgent gate** when conditional LLM reasoning is needed
|
|
||||||
3. Chain via **context-from** to split complex pipelines into lean stages
|
|
||||||
4. Use **silent delivery** on every poller — never deliver "nothing changed" ticks
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# Email Triage — wakeAgent Gate Pattern
|
|
||||||
|
|
||||||
A condition-based poller that checks for new actionable email, fires the LLM only when there's unprocessed high-priority mail, and stays silent (zero cost) on empty ticks.
|
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ cron: every 15m │
|
|
||||||
│ no_agent=true │
|
|
||||||
│ script: check-unread-email.sh │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ Script output (stdout): │
|
|
||||||
│ • "" (empty) → no agent, no delivery ── $0.00 │
|
|
||||||
│ • "3 unread priority" → gate opens → agent processes │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### 1. Script (`scripts/check-unread-email.sh`)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Silent poller: outputs nothing unless there are actionable unread emails
|
|
||||||
# Called by cron with no_agent=true — zero token cost on empty ticks
|
|
||||||
#
|
|
||||||
# Gate logic:
|
|
||||||
# stdout empty = no new email → silent delivery (nothing sent)
|
|
||||||
# stdout non-empty = has mail → cron passes it to agent context
|
|
||||||
|
|
||||||
source ~/.hermes/profiles/leonard/.env
|
|
||||||
|
|
||||||
COUNT=$(himalaya envelope list -f INBOX -a 2>/dev/null | grep -c "UNSEEN" || echo 0)
|
|
||||||
|
|
||||||
if [ "$COUNT" -eq 0 ]; then
|
|
||||||
exit 0 # silent — no output, no delivery
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Gate opened: output structured context for the agent to process
|
|
||||||
echo "UNREAD_EMAIL=$COUNT"
|
|
||||||
echo "TIMESTAMP=$(date -Iseconds)"
|
|
||||||
echo "---"
|
|
||||||
himalaya envelope list -f INBOX -a 2>/dev/null | head -20
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Cron Job (Hermes cron)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hermes cron create \
|
|
||||||
-s "email-monitor" \
|
|
||||||
--schedule "every 15m" \
|
|
||||||
--script ~/.hermes/profiles/leonard/scripts/check-unread-email.sh \
|
|
||||||
--no-agent \
|
|
||||||
"If the script reports unread emails, summarize the top 3 most important ones with sender, subject, and a one-line action recommendation. Flag anything urgent or time-sensitive. If nothing actionable, deliver nothing."
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Wake Gate Principle
|
|
||||||
|
|
||||||
The gate stays **closed** (no agent invoked) 95%+ of ticks. The script:
|
|
||||||
- Uses no LLM resources (pure shell)
|
|
||||||
- Outputs nothing on empty scans → **silent delivery** (free)
|
|
||||||
- Only when email is found does the agent context fire
|
|
||||||
|
|
||||||
## Key Configuration
|
|
||||||
|
|
||||||
| Setting | Value | Reason |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| `schedule` | `every 15m` | Fast enough for triage, sparse enough to stay cheap |
|
|
||||||
| `no_agent` | `true` | Script-only polling — zero token cost per tick |
|
|
||||||
| `deliver` | `origin` | Returns to the same chat where created |
|
|
||||||
| Script exit | `0` with empty stdout | Silent — no notification sent |
|
|
||||||
|
|
||||||
## Variants
|
|
||||||
|
|
||||||
- **Compliance monitor**: Same pattern, replace email check with regulatory RSS/API poll
|
|
||||||
- **Opportunity scanner**: Script scrapes X/HN, gates on fresh high-signal mentions
|
|
||||||
- **System health**: Script checks disk/GPU/memory thresholds, gates only on warnings
|
|
||||||
- **Skills curator**: Script checks skill freshness, gates on skills approaching staleness
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Daily Brief — Implementation Notes
|
|
||||||
|
|
||||||
## Architecture: context-from Chaining
|
|
||||||
|
|
||||||
The daily brief uses a **no-agent script → agent curation** pipeline:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────┐
|
|
||||||
│ cron: 0 15 * * * │
|
|
||||||
│ script: collector.sh │ ← no-agent, free public APIs
|
|
||||||
├──────────────────────────────┤
|
|
||||||
│ Script stdout → agent │ ← injected as prompt context
|
|
||||||
│ context │
|
|
||||||
├──────────────────────────────┤
|
|
||||||
│ Agent: curates → formats │ ← lightweight LLM pass only
|
|
||||||
│ → delivers to Telegram │
|
|
||||||
└──────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cost Breakdown
|
|
||||||
|
|
||||||
| Stage | Token Cost | Frequency |
|
|
||||||
|-------|-----------|-----------|
|
|
||||||
| Script fetch (shell) | $0 | 1x per tick |
|
|
||||||
| Agent curation | Full model | 1x per tick |
|
|
||||||
| **Total vs old approach** | **~90% cheaper** | Old: agent had to web search + X search (both failing) |
|
|
||||||
|
|
||||||
## Sources (Free, No API Key)
|
|
||||||
|
|
||||||
| Source | Endpoint | Reliability |
|
|
||||||
|--------|----------|-------------|
|
|
||||||
| Weather | `wttr.in/Placerville,CA` | 99.9% |
|
|
||||||
| Hacker News | Firebase API (v0) | 99.9% |
|
|
||||||
| GitHub Trending | `api.github.com/search/repositories` | 99% (unauthed rate limit: 60/hr — fine for daily) |
|
|
||||||
|
|
||||||
## Previous State (Before June 20)
|
|
||||||
|
|
||||||
The old prompt instructed the agent to use web_search and x_search tools to gather data. Both were failing silently — Firecrawl was unconfigured and xAI credits were exhausted. The brief was returning `[SILENT]` every run (zero useful output for weeks).
|
|
||||||
|
|
||||||
## Key Insight
|
|
||||||
|
|
||||||
The **script + context_from** pattern from the masterclass turned a dead cron job into a working one in <5 minutes, with zero ongoing API costs. The script does the heavy lifting (API calls, parsing), and the agent only does the lightweight curation pass.
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# daily-brief-collector.sh
|
|
||||||
# No-agent data collector: fetches free public data for the daily brief.
|
|
||||||
# Injected as cron context via the script= parameter.
|
|
||||||
# Zero API keys needed — all sources are free/public.
|
|
||||||
#
|
|
||||||
# Pattern: no-agent polling script → context injection → agent curates
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "# Daily Brief Data — $(date -I 2>/dev/null || date +%Y-%m-%d)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- 1. Weather ---
|
|
||||||
echo "## 🌤 Weather — Placerville, CA"
|
|
||||||
if WEATHER=$(curl -sf "wttr.in/Placerville,CA?format=%C+%t+%w+%h&u" 2>/dev/null); then
|
|
||||||
echo "Current: $WEATHER"
|
|
||||||
else
|
|
||||||
echo "Weather: (unavailable)"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- 2. Hacker News Top Stories ---
|
|
||||||
echo "## 📰 Hacker News — Top Stories"
|
|
||||||
TOP_IDS=$(curl -sf "https://hacker-news.firebaseio.com/v0/topstories.json" 2>/dev/null | python3 -c "
|
|
||||||
import sys, json
|
|
||||||
ids = json.load(sys.stdin)[:8]
|
|
||||||
for i in ids:
|
|
||||||
print(i)
|
|
||||||
" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -n "$TOP_IDS" ]; then
|
|
||||||
echo "$TOP_IDS" | while read -r ID; do
|
|
||||||
ITEM=$(curl -sf "https://hacker-news.firebaseio.com/v0/item/$ID.json" 2>/dev/null)
|
|
||||||
[ -n "$ITEM" ] && echo "$ITEM" | python3 -c "
|
|
||||||
import sys, json
|
|
||||||
d = json.load(sys.stdin)
|
|
||||||
title = d.get('title', '?')
|
|
||||||
url = d.get('url', 'https://news.ycombinator.com/item?id=' + str(d.get('id', '')))
|
|
||||||
score = d.get('score', 0)
|
|
||||||
print(f\"- [{title}]({url}) (⬆{score})\")
|
|
||||||
" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "HN: (unavailable)"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# --- 3. GitHub Trending (24h) ---
|
|
||||||
echo "## 💻 GitHub — Trending Repos (24h)"
|
|
||||||
YESTERDAY=$(date -d '2 days ago' +%Y-%m-%d 2>/dev/null || date -v-2d +%Y-%m-%d 2>/dev/null || echo "2026-06-18")
|
|
||||||
curl -sf "https://api.github.com/search/repositories?q=created:>$YESTERDAY&sort=stars&order=desc&per_page=5" 2>/dev/null | python3 -c "
|
|
||||||
import sys, json
|
|
||||||
data = json.load(sys.stdin)
|
|
||||||
items = data.get('items', [])
|
|
||||||
if not items:
|
|
||||||
print('No repos found in last 24h')
|
|
||||||
else:
|
|
||||||
for r in items:
|
|
||||||
name = r['full_name']
|
|
||||||
desc = (r['description'] or 'No description')[:100]
|
|
||||||
stars = r['stargazers_count']
|
|
||||||
url = r['html_url']
|
|
||||||
lang = r.get('language') or '?'
|
|
||||||
print(f\"- [{name}]({url}) — {desc} ({lang}, ⭐{stars})\")
|
|
||||||
" 2>/dev/null || echo "GitHub: (unavailable)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "---"
|
|
||||||
echo "Use the data above to generate a concise daily brief (max 8 bullets). Format all links as clickable Telegram markdown. No intro or closing text."
|
|
||||||
Reference in New Issue
Block a user