Summary: OpenRouter published a TypeScript tutorial (October 31, 2026) for building an always-on agent with @openrouter/agent 0.11.0 — a Hacker News watcher that runs on a schedule, remembers what it has seen across restarts, and stops any run that hits five cents. The interesting part isn’t the demo; it’s which responsibilities the SDK takes and which stay in your code.
The four things an always-on agent needs
The tutorial frames the problem cleanly: a scheduled loop, tools to look at the world and act on it, memory that survives a crash, and limits — a step and cost cap per run, plus human approval for anything with consequences. The demo agent watches HN for chosen topics, writes a digest, and posts to a team channel only for major releases.
The division of labor is the part worth studying. The SDK runs the model and tool calls, applies the stop conditions, and persists the conversation so a paused run can resume. Your code owns the cron schedule and the file of seen story IDs. That split keeps the SDK small and your state boring — a JSON file on disk.
If you want to understand what the SDK is actually doing for you, our earlier post on OpenRouter’s tool-calling loop covers where stop conditions belong when you write the same loop by hand.
Cost control as a first-class parameter
Each check cost $0.0003 to $0.0012 in the tutorial’s runs on October 13, 2026, using Claude Haiku 5.5 — roughly $0.86 to $3.46 per month at one check every 15 minutes. The bounding is layered: stopWhen: [stepCountIs(6), maxCost(0.05)] caps every run, and a credit limit on the API key caps the total. That’s the pattern I’d copy regardless of framework — per-run limits in code, a hard total limit at the account level, so a runaway loop can’t surprise you twice.
A subtler cost trick: each check starts a fresh conversation. Long-term memory lives in a file of seen story IDs, not in an ever-growing context, so the cost of a check stays flat over weeks. The tool filters out already-seen stories before the model ever sees them, which means the model doesn’t burn tokens tracking its own history — or risk re-reporting the same story.
Memory that survives a kill -9
The tutorial’s restart test is the most instructive part. They killed the process right after a search returned 20 new stories. The next run resumed the saved conversation, wrote the digest from the stored search results, and marked all 20 IDs as seen; the following check found zero new stories.
The mechanics: new IDs go into a transient fetched.json during a run and only merge into seen.json when the run finishes. The documented tradeoff — a run failing after writing the digest can list a story twice — is the right call. A duplicate is easier to fix than a lost story.
Approval gates that pause, not block
The post_to_team_channel tool is declared with requireApproval: true. When the model calls it, the run pauses before execution, the pending call is logged with its ID, and scheduled runs are skipped while approval is pending. You drop an approve or reject decision into decisions.json, and the next run resumes the saved conversation with your decision applied. A resumed run can pause again, so the gate isn’t a one-time check.
This is a lightweight pattern for anything with external consequences — posting, emailing, spending. The SDK saves the conversation state; the approval queue is just a file you can review.
Where to start
The tutorial needs Node.js 22, an OpenRouter API key, and three packages (@openrouter/agent pinned at 0.11.0, Zod for tool schemas, tsx). Bun works too. The same structure transfers to any scheduled watcher — a repo, a status page, a competitor’s changelog — wherever something should be checked without you asking. One caveat to keep in mind: the SDK is still on 0.x releases, so pin the version and expect API changes.
