Skip to content
business loginincident 4f2a · opened 03:14
Annotated 03:16 — auth-service #482 merged at 03:09 changed the session cookie domain. Sentry shows InvalidSessionError first seen 03:11, inside the window.

thinkbot

An ops agent that triages monitoring alerts. It receives an alert from clawdwatch, investigates it against GitHub, Datadog and Sentry, and reports what it found to Slack or Telegram.

A check tells you an endpoint returned 500. thinkbot looks for what changed around that window — a pull request merged, an exception that first appeared inside it, a metric that stepped rather than wobbled — and says so in a paragraph. If the evidence does not support a cause, it says the cause is unclear and lists what it ruled out.

Silence is a valid outcome. If triage found nothing, it posts nothing: an empty channel beats filler under an incident someone is trying to read.

Why it exists

A health endpoint returned a 500 whose body named the failing dependency for five hours, while the alert carried only expected 200, got 500. The cause was sitting in a response nobody kept, and nobody was awake to read it.

clawdwatch now captures that body. thinkbot is the thing that reads it at 3am.

Receiving alerts

Two transports, one triage path.

Service binding. If clawdwatch runs on the same Cloudflare account, it calls thinkbot's AlertInbox entrypoint directly. The platform authenticates the call, so there is no shared secret and no public endpoint:

jsonc
// in the monitoring Worker's config
"services": [
  { "binding": "AGENT", "service": "thinkbot", "entrypoint": "AlertInbox" }
]
ts
import { rpc } from "clawdwatch";
notifiers: [rpc({ binding: (env) => env.AGENT })];

Signed webhook. For a sender that cannot use a binding, POST /hooks/clawdwatch verifies an HMAC over timestamp.body using clawdwatch's own verifySignature rather than a local reimplementation.

An RPC call carries no signature — authenticity comes from the binding — so the shared triage path never assumes one was checked.

What it can look at

Tools are composed per turn from whatever the deployment is credentialed for, so running this against a different estate is configuration rather than a patch.

TOOLS nameNeedsUsed for
githubGITHUB_TOKEN, GITHUB_OWNERpull requests merged recently, workflow runs
datadogDD_API_KEY, DD_APP_KEYmetrics that stepped around the failure window
sentrySENTRY_TOKEN, SENTRY_ORGexceptions first seen inside the window
workersCF_ACCOUNT_ID, CF_API_TOKENWorker invocation telemetry — outcomes, wall vs CPU
clawdwatchMONITORING_URLcheck history, incidents, and writing findings back

A provider without its credentials is not offered at all, which is not the same as failing gracefully: a tool that cannot answer still spends one of the agent's handful of steps, and earns a sentence in every write-up. TOOLS narrows that default (TOOLS=github,sentry, or TOOLS=-datadog); unset means everything configured. Adding a provider is one entry in src/tools/registry.ts.

Findings worth keeping are written back to the incident with annotateIncident, using the short-lived signed links that arrive with the alert — so the agent needs no standing credential to record what it concluded.

Where to go

PageWhat it covers
SetupDeploying it and wiring up the sources
SecurityWhat it holds, and what verifies each caller
ContributingConventions that are load-bearing rather than stylistic

The other half

clawdwatch is the monitor that sends the alerts. Its AI agents guide describes this integration from the sending side, including response-body capture and the alert payload versioning contract.

MIT. Source on GitHub.