apps/docs/skills/recipe-persistent-monitor/SKILL.md
Full recipe for a persistent monitoring agent with heartbeats, daily cron reports, webhook triggers, daily token budgets, and graceful shutdown.
npx skillsauth add tylerjrbuell/reactive-agents-ts recipe-persistent-monitorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A long-running monitoring agent that fires a heartbeat every 30 minutes (adaptive — skips if nothing to do), generates a daily report every weekday at 9am, accepts webhook events from external services, and enforces daily token and cost budgets. Shuts down gracefully on SIGINT/SIGTERM.
gateway-persistent-agents — heartbeat, cron, webhook, policy configurationtool-creation — allowedToolscost-budget-enforcement — daily token budget and cost limitsobservability-instrumentation — normal verbosity for productionimport { ReactiveAgents } from "@reactive-agents/runtime";
const agent = await ReactiveAgents.create()
.withName("ops-monitor")
.withProvider("anthropic")
.withReasoning({
defaultStrategy: "reactive",
maxIterations: 8, // keep iterations short for monitoring tasks
})
.withTools({
allowedTools: ["web-search", "http-get", "checkpoint", "final-answer"],
})
.withGateway({
timezone: "America/New_York",
heartbeat: {
intervalMs: 30 * 60 * 1_000, // check every 30 minutes
policy: "adaptive", // skip if nothing requires attention
instruction: `
Check the following:
1. Fetch https://status.myservice.com/api/status — is everything green?
2. If any service is degraded or down, create an incident summary.
3. If all green, no action needed (adaptive policy will skip next run).
`,
maxConsecutiveSkips: 12, // wake up after 12 skips (~6 hours) regardless
},
crons: [
{
schedule: "0 9 * * MON-FRI", // 9am weekdays
instruction: `
Generate a daily operations report:
1. Summarize the last 24 hours of activity from https://api.myservice.com/logs.
2. Highlight any incidents, performance degradations, or anomalies.
3. Save the report to ./reports/daily-{date}.md.
`,
priority: "normal",
timezone: "America/New_York",
},
],
webhooks: [
{
path: "/github/webhook",
adapter: "github",
secret: process.env.GITHUB_WEBHOOK_SECRET,
events: ["push", "pull_request"],
},
],
policies: {
dailyTokenBudget: 50_000, // hard stop after 50k tokens/day
maxActionsPerHour: 20, // rate limit proactive actions
},
})
.withCostTracking({ daily: 5.0 })
.withObservability({ verbosity: "normal", file: "./logs/monitor.jsonl" })
.withHealthCheck()
.build();
// Start the persistent agent loop
const handle = await agent.start();
console.log("Monitor started. Ctrl+C to stop.");
// Graceful shutdown on SIGINT / SIGTERM
const shutdown = async (signal: string) => {
console.log(`\nReceived ${signal}. Shutting down...`);
const summary = await handle.stop();
console.log(`Ran ${summary.totalRuns} times`);
console.log(`Heartbeats fired: ${summary.heartbeatsFired}`);
console.log(`Cron checks: ${summary.cronChecks}`);
process.exit(0);
};
process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));
import { tool } from "@reactive-agents/tools";
const sendAlert = tool("send-alert", "Send an alert to the on-call channel", async (args) => {
const { message, severity } = args as { message: string; severity: "low" | "high" | "critical" };
await slackClient.postMessage({ channel: "#on-call", text: `[${severity}] ${message}` });
return { sent: true };
});
// Add to the agent's tools:
.withTools({
tools: [sendAlert],
allowedTools: ["send-alert", "http-get", "checkpoint"],
})
heartbeat: {
intervalMs: 60 * 60 * 1_000, // 1 hour
policy: "conservative", // only fires on explicit webhook triggers
instruction: "Process incoming events only.",
}
crons: [
{ schedule: "*/15 * * * *", instruction: "Check support queue...", priority: "high" },
{ schedule: "0 * * * *", instruction: "Hourly metrics snapshot...", priority: "normal" },
{ schedule: "0 8 * * MON", instruction: "Weekly executive summary...", priority: "normal" },
]
const summary = await handle.stop();
// summary.totalRuns — total agent executions
// summary.heartbeatsFired — number of heartbeat triggers
// summary.cronChecks — number of cron-triggered runs
.start() returns a handle — you must call handle.stop() before process.exit() or the Node process will not exit cleanlysecret mismatch causes all webhook events to be silently rejected — verify the secret matches what the external service sendspolicy: "adaptive" skips when the agent decides there's nothing to do — the agent itself determines this, so system prompt guidance mattersdailyTokenBudget resets at midnight in the configured timezone — ensure timezone is set correctly for your use casemaxConcurrentSkips is a safety net — without it, an adaptive agent can skip indefinitely if the monitored service is always healthydevelopment
Orient to the Reactive Agents framework, understand the builder API shape, and select the right capability skills for your task.
testing
Enable output verification (hallucination detection, semantic entropy, self-consistency), add post-run verification steps, and run LLM-scored evals across 5 quality dimensions.
data-ai
Configure per-provider behavior, understand streaming quirks, and use the 7-hook adapter system for optimal performance across LLM providers.
data-ai
Configure the 4-layer memory system with SQLite/FTS5/vec storage for persistent agent knowledge that survives sessions.