一键导入
gateway-persistent-agents
Build always-on agents with heartbeats, cron scheduling, webhook triggers, and a persistent policy engine using the Gateway layer.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Build always-on agents with heartbeats, cron scheduling, webhook triggers, and a persistent policy engine using the Gateway layer.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Agentic harness diagnostic + improvement loop. Probes the framework with real model runs, uses the rax-diagnose CLI to root-cause failures from structured trace data, ships ONE coordinated architectural fix, verifies via before/after diff, commits with empirical evidence. Use at the start of any harness improvement session — replaces ad-hoc grep + log-spelunking with a deterministic feedback loop.
Use when the architecture may have drifted from documentation, packages have grown complex, dead code or disabled systems are suspected, or before planning a major refactor — scoped to the reactive-agents-ts 22-package monorepo.
Reactive Agents framework architecture — layer stack, dependency graph, build order, and package structure. Use when planning work, understanding package relationships, or determining build dependencies.
Use when analyzing the Reactive Agents codebase for architectural improvements, abstraction opportunities, composability gaps, or Effect-TS engineering quality — before proposing refactors, during design reviews, or when codebase complexity is growing.
LLMService API contract — the correct signatures for complete(), stream(), embed(), and response types. Use when calling LLMService from any layer, writing reasoning strategies, or building LLM-dependent features.
LLM provider streaming patterns and per-provider quirks. Use when adding a new provider, implementing adapter hooks, or debugging streaming tool call behavior in packages/llm-provider.
| name | gateway-persistent-agents |
| description | Build always-on agents with heartbeats, cron scheduling, webhook triggers, and a persistent policy engine using the Gateway layer. |
| compatibility | Reactive Agents TypeScript projects using @reactive-agents/* |
| metadata | {"author":"reactive-agents","version":"2.0","tier":"capability"} |
Produce a builder with .withGateway() correctly configured for the persistence pattern needed, with the agent started and gracefully stopped.
import { ReactiveAgents } from "@reactive-agents/runtime";
const agent = await ReactiveAgents.create()
.withName("monitor")
.withProvider("anthropic")
.withReasoning({ defaultStrategy: "reactive", maxIterations: 8 })
.withTools({ allowedTools: ["web-search", "http-get"] })
.withGateway({
heartbeat: {
intervalMs: 1_800_000, // 30 minutes
policy: "adaptive", // skip if no new work
instruction: "Check for new alerts and summarize",
},
crons: [
{
schedule: "0 9 * * MON-FRI", // 9am weekdays
instruction: "Generate daily status report",
priority: "normal",
},
],
policies: {
dailyTokenBudget: 50_000,
maxActionsPerHour: 20,
},
})
.withCostTracking({ daily: 5.0 })
.withObservability({ verbosity: "normal" })
.withHealthCheck()
.build();
// Start the persistent loop
const handle = await agent.start();
// Graceful shutdown
process.on("SIGINT", async () => {
const summary = await handle.stop();
console.log(`Ran ${summary.totalRuns} times, ${summary.heartbeatsFired} heartbeats`);
process.exit(0);
});
heartbeat: {
intervalMs: 3_600_000, // 1 hour
policy: "always", // always run regardless of activity
// policy: "adaptive" // skip if agent has nothing useful to do (default)
// policy: "conservative" // only run on explicit triggers
instruction: "Review incoming messages and respond to urgent ones",
maxConsecutiveSkips: 5, // stop skipping after 5 consecutive no-ops
}
crons: [
{
schedule: "0 9 * * 1", // Every Monday at 9am (standard cron syntax)
instruction: "Review PRs and post weekly summary to Slack",
priority: "high", // "low" | "normal" | "high" | "critical"
timezone: "America/New_York",
enabled: true,
},
{
schedule: "*/15 * * * *", // Every 15 minutes
instruction: "Check for new support tickets and categorize them",
priority: "normal",
},
]
webhooks: [
{
path: "/github/webhook",
adapter: "github",
secret: process.env.GITHUB_WEBHOOK_SECRET,
events: ["push", "pull_request"],
},
]
// Gateway starts an HTTP server on gateway.port (default: varies — check builder docs)
// Incoming webhooks are normalized and passed as tasks to the agent
policies: {
dailyTokenBudget: 100_000, // hard stop after N tokens/day
maxActionsPerHour: 50, // rate-limit proactive actions
heartbeatPolicy: "adaptive", // global override for all heartbeats
requireApprovalFor: ["file-write", "send-email"], // tools that need human approval
}
.withGateway({
persistMemoryAcrossRuns: true, // default: false
heartbeat: { intervalMs: 60_000, instruction: "Check for new work" },
crons: [{ schedule: "0 9 * * *", instruction: "Daily summary" }],
})
.withMemory({ tier: "enhanced", dbPath: "./memory.sqlite" })
When persistMemoryAcrossRuns: true, the agent reuses the same stable agentId across all gateway executions (heartbeats, crons, webhooks). This allows the memory layer to maintain episodic context across runs — the agent "remembers" what it saw in the previous heartbeat when processing the next cron, avoiding repeated work and building narrative continuity.
Without persistence (default): Each gateway execution gets a unique agentId like agent-name-heartbeat-1234567890, so memory is isolated per run. Good for stateless checks.
With persistence: All executions share agent-name, so memory spans across runs. Good for agents that need to understand history, avoid duplication, or provide narrative context (e.g., digest monitors, status trackers).
| Field | Type | Notes |
|---|---|---|
timezone | string | Default timezone for crons (e.g., "America/New_York") |
persistMemoryAcrossRuns | boolean | Reuse same agent ID across all gateway executions so memory spans heartbeats/crons. Default: false |
heartbeat.intervalMs | number | Default: 60,000ms (1 min) |
heartbeat.policy | "always"|"adaptive"|"conservative" | |
heartbeat.instruction | string | Task prompt for each heartbeat |
heartbeat.maxConsecutiveSkips | number | Stop skipping after N no-ops |
crons[].schedule | string | Standard cron expression |
crons[].instruction | string | Task prompt for this cron |
crons[].priority | "low"|"normal"|"high"|"critical" | |
policies.dailyTokenBudget | number | Hard token cap per day |
policies.maxActionsPerHour | number | Rate limit for proactive actions |
policies.requireApprovalFor | string[] | Tools requiring human approval |
port | number | HTTP port for webhook server |
handle.stop())| Field | Type |
|---|---|
totalRuns | number |
heartbeatsFired | number |
cronChecks | number |
The gateway maintains zero-LLM-cost state that powers all policy decisions:
type GatewayState = {
lastExecutionAt: Date | null // Enables adaptive skip
consecutiveHeartbeatSkips: number // Forces execute after N skips (safety net)
tokensUsedToday: number // For daily budget enforcement
actionsThisHour: number // For rate limit enforcement
pendingEvents: GatewayEvent[] // Queued work from webhooks
}
Inspect state at runtime: const status = await agent.gatewayStatus()
For a complete guide to state tracking, policy evaluation, and how state drives decisions, see the Gateway State Tracking Reference.
.withGateway() alone does nothing — you must call .start() on the built agent to begin the loopSIGINT, SIGTERM) that calls handle.stop()intervalMs default is 60,000ms (1 min) — set a longer interval for agents that don't need frequent checksmin hour dom month dow) — verify with a cron parser before deployingpolicies.dailyTokenBudget resets at midnight in the timezone specified — ensure timezone is set correctlypersistMemoryAcrossRuns: true requires .withMemory() with a persistent database (e.g., SQLite) to be useful — otherwise memory tiers default to in-memory and still get wiped between runsconsecutiveHeartbeatSkips is adaptive-mode only — "always" and "conservative" modes don't use it (no skipping to reset)