### Overview New built-in toolset `webhook` with one tool — `send_webhook(url, message, provider?)` — that POSTs a message to a webhook, shaping the JSON payload per target: Slack / Mattermost / Rocket.Chat / Google Chat / Microsoft Teams / generic → `{"text": …}`; Discord → `{"content": …}`; IFTTT → `{"value1": …, "value2": …, "value3": …}`; Telegram → `{"chat_id": …, "text": …}`. ### Motivation Agents can act but can't *tell anyone*. A webhook tool sends alerts/updates to where people already are (Slack/Discord) or into automation (IFTTT), and composes with the `scheduler` toolset ("check X hourly, `send_webhook` if broken"). No agent-callable equivalent exists today — `pkg/hooks/builtins/http_post` posts on lifecycle *events* from config, not when the agent decides to. ### Use cases 1. Alert from a scheduled check (build broke → notify #ops). 2. Progress/summary updates to a Slack channel during a long run. 3. Trigger an IFTTT applet. ### Proposed solution New toolset in `pkg/tools/builtin/webhook/`, registered in the toolsets registry + catalog (the catalog drift-guard test enforces the entry). Reuses `httpclient.NewSafeClient` (refuses non-public IPs; defeats DNS-rebinding to loopback / RFC1918 / cloud metadata) — the same client the hooks `http_post` uses. Payload construction is a pure function and the HTTP client is injectable, so it is unit-tested with a fake — no network. ### Alternatives A general `http_request` tool could POST too, but it is a broad SSRF/exfiltration surface; `webhook` is the safe, message-shaped subset purpose-built for notifications. ### Related issues _No response_ ### Additional context Config-defined named/allowlisted webhooks (agent references `slack-ops` without seeing the URL) are noted as future work.