Agents, routines, helpers, and hooks
Four Fathom concepts that sound similar and do different things. Here's what each is, when to reach for it, and how they compose.
These four words get confused constantly. Each has a specific job. If you're adding behavior to Fathom, picking the right one is usually most of the design problem.
Short version:
| Thing | What it is | When to reach for it |
|---|---|---|
| Agent | A long-running daemon on a host machine | You want Fathom to have a presence on a specific machine, running plugins and executing routines |
| Routine | A scheduled prompt that fires into Fathom (the River) | You want a task performed at a cadence (daily, hourly, Mondays). The harness decides what to do — fetch with claude-code, write a feed card from substrate, fire an alert, propose a state change |
| Helper | A named capability Fathom can call | You want chat or a routine to be able to do a specific thing on demand (fetch weather, summarize a URL, draft text) |
| Hook | A shell command fired on a lifecycle event | You want to capture something that's happening in another tool (Claude Code, an IDE) as deltas |
Agent
The agent is addons/agent/. One instance runs on each machine you want Fathom to know about. It's a Node daemon that:
- Heartbeats into the lake at a steady cadence, so the server knows the machine is online.
- Runs a collection of plugins (heartbeat, sysinfo, kitty, local-ui, vault, homeassistant). Each plugin owns a category of deltas it's responsible for writing.
- Executes routines when the server schedules them, by spawning a kitty window and launching Claude Code inside it.
An agent is bound to a host. If you have three machines you want in Fathom, you pair three agents. Pairing is a one-time operation that exchanges a short-lived pair code for a long-lived API key, stored at ~/.fathom/agent.json on the machine.
Agents don't "do work" in a general sense. They run the plugins and routines you've configured. Think of the agent as the body Fathom has on your laptop: always there, doing the little things it was asked to do, reporting back.
Plugins that receive dispatched work (kitty for claude-code, the ACP plugin for any number of stdio adapters) are called helper plugins. Each one advertises one or more (host, role) pairs via the heartbeat — kitty advertises helper-role:claude-code, the ACP plugin advertises whatever roles you configure under its targets array. Fathom's harness picks a (host, role) pair when it calls the dispatch_helper tool; the matching plugin on that host runs the work and replies through the inbox endpoint. See Wire up helpers for the mint-token-and-configure flow.
Routine
A routine is a scheduled prompt. You write the prompt in the dashboard, pick a cadence (cron expression, or "daily at 8am," or "Mondays at 6pm"), and save it. When the time comes, the cron tick writes a routine-due intent into the puddle — and that's where the routine's job ends. From here, the harness (the River) reads the intent like any other and decides what to do.
That's the architectural shift worth seeing clearly: a routine isn't a claude-code trigger. It's a scheduled "Hey Fathom, handle this." What Fathom does next is a routing decision that belongs to the harness, not to the cron tick or to the routine spec. Some routines need claude-code (fresh data, file work, shell commands). Others should land as feed cards composed from substrate already in the lake. Some are alerts. Some are conversational replies. Some propose state changes the user has to approve.
The harness picks. The routine prompt names the intent — "summarize this week's commits," "check the news and synthesize," "alert me if a research thread has been quiet 3 days" — and the route falls out of that.
Practical implications:
- A routine doesn't always need an agent. Substrate-only routines (synthesis from the lake, alerts, chat-replies) run inside the api process and don't spawn anything externally.
- Routines that need fresh data still spawn claude-code. When the harness picks
claude-code:<host>, the existing dispatch path kicks in: kitty plugin spawns the session, runs the prompt, the closure feeds back into the harness for synthesis. The "synthesize into a concise update" instruction in your prompt is honored on that synthesis tick — by the harness, in Fathom's voice — not by claude-code. - There is no "skip the River" override. Fire Now and the chat-tool
routines.fireaction both fire into the loop the same way the cron tick does — the legacy direct-to-claude-code path (routine-firedelta consumed by the kitty plugin) was retired 2026-04-30. Routines are a scheduled "Hey Fathom, handle this," and Fathom always decides.
Routines are independent of chat sessions. Routine activity lives under routine-id:<id>, not chat:<slug>. You see history on the Routines page or by searching that tag.
For routines that route through claude-code, you need an agent paired on a machine with both kitty and Claude Code installed and authenticated. For substrate-only routines, no agent is required.
Helper
The word "helper" is used two ways in Fathom, and both are in active use. This section is about the inference-time sense; the host-side sense is the helper plugins described in Agent above and the Wire up helpers how-to.
- Helper (inference-time, LLM tool): a named capability Fathom can invoke during a harness turn. Registered in
LAKE_TOOLS(HTTP-backed; visible to chat / MCP / CLI / harness per itssurfaceslist). The harness sees these as OpenAI-style functions. - Helper (host-side, dispatchable agent): a
(host, role)pair an agent advertises via heartbeat — kitty forclaude-code, the ACP plugin foropenclaw,codex, etc. The harness'sdispatch_helpertool targets one of these pairs.
The rest of this section is about the first sense.
A helper is a named capability Fathom can invoke during an inference turn. "Fetch the weather." "Summarize this URL." "Draft a response to this email." "Query a database." Each helper has a name, an input schema, and a runtime that executes it.
The lineage is deliberate: helpers are what lets a chat turn or a routine actually do something beyond speaking. A helper invocation during chat might look like:
- User asks "what's the weather tomorrow?"
- Inference turn decides to call the
weatherhelper. - Helper runs, fetches the forecast, returns it.
- Inference continues with the helper's result in context.
- A delta is written capturing the helper call and its result, so the lake remembers what was asked and what was returned.
Helpers purposefully generate deltas as a side effect of running. That's the design. A weather helper writing a delta for each forecast it fetches means the next time someone asks "what did you tell me about tomorrow's weather yesterday?" the lake can answer.
Helpers compose with everything else. A routine can invoke helpers. A chat turn can. A hook can. They're the verb library.
Hook
A hook is a shell command that fires on a lifecycle event in another tool, with the job of writing deltas into the lake.
The concrete example: Claude Code emits lifecycle events (UserPromptSubmit, Stop, SessionStart). If you install the fathom-connect hooks in a project, each of those events runs a small shell script that writes a delta. The result: every prompt you type into Claude Code and every reply Claude finishes gets captured in Fathom, tagged with the session ID, participant, source. The dashboard can replay any past Claude Code session because the lake holds every turn.
Hooks are the mechanism by which Fathom learns what's going on in tools it doesn't own. Claude Code is the current primary case. In principle, any tool that emits hookable events (IDEs, terminal multiplexers, shells) could be wired up the same way.
Hooks are not agents. They don't run continuously. They fire, write a delta, exit. The work they do is narrow by design: observe an event, record it, get out of the way.
How they compose
A realistic flow that uses all four:
- You pair an agent on your laptop.
- You install hooks into a Claude Code project so every prompt and reply gets captured.
- You create a routine that fires every morning at 8am, with a prompt like "check overnight GitHub notifications and summarize what needs my attention."
- When the routine fires, the agent spawns kitty and runs Claude Code. The Claude Code session invokes the
githubhelper to fetch notifications, summarizes them, writes a delta taggedroutine-id:<id>. - The hooks capture every step of the Claude Code session so the lake has the full transcript, not just the summary.
That's four different pieces doing four different things, and because all of them write deltas into the same lake, the morning routine's output is searchable alongside your chat, alongside yesterday's routine, alongside anything else you've talked about with Fathom.
Which one to reach for
- "I want Fathom on this machine." → Agent.
- "I want a task done on a schedule." → Routine (requires an agent).
- "I want chat to be able to do X." → Helper.
- "I want to capture what's happening in this other tool." → Hook.
When in doubt, the question to ask is: does this need to run continuously, at a scheduled time, on demand, or in response to an event? The four answers map to the four things.