Skip to content

Notifications

SwarmKit persists and delivers notifications for runtime events. Notifications are stored locally and optionally delivered to external services (Slack, Discord, Telegram, webhooks).

Architecture

Runtime fires event (HITL request, error, skill gap)
    ├─→ NotificationStore (SQLite) — persisted, queryable
    │       ↕
    │   CLI: swarmkit notifications --last 10
    │   Web UI: GET /notifications?status=pending
    └─→ External providers (best-effort delivery)
            Slack / Discord / Telegram / Webhook
            Delivery status tracked in store

The primary record lives in the NotificationStore. External delivery is a side effect — if Slack is down, the notification is still queryable via CLI and web UI.

Configuration

Add a notifications: block to workspace.yaml:

notifications:
  - provider: slack
    config:
      webhook_url: https://hooks.slack.com/services/T.../B.../xxx
      channel: "#swarmkit-alerts"
    events: [hitl_requested, run_ended_error]

  - provider: discord
    config:
      webhook_url: https://discord.com/api/webhooks/123/abc
    events: [hitl_requested]

  - provider: telegram
    config:
      bot_token: "123456:ABC-DEF"
      chat_id: "-100123456"

  - provider: terminal
    events: [hitl_requested]

Providers

Provider Config Description
terminal (none) Prints to stderr. Default in TTY mode.
webhook url, headers, timeout Generic HTTP POST with JSON payload
slack webhook_url, channel Slack incoming webhook
discord webhook_url, username Discord webhook with color-coded embeds
telegram bot_token, chat_id Telegram Bot API with Markdown

Events

Event Fires when
hitl_requested A human approval gate is triggered
run_ended_error A topology run fails with an error
skill_gap_surfaced A new skill gap is detected

Event filtering

Each provider can subscribe to specific event types via the events list. If omitted, the provider receives all events.

Notification store

All notifications are persisted to .swarmkit/notifications.sqlite with delivery tracking:

Field Description
id Unique notification ID
event_type hitl_requested / run_ended_error / skill_gap_surfaced
run_id Associated run
topology_id Topology that generated the event
summary Human-readable summary
status pending / delivered / failed
provider Which provider delivered (or failed)
delivered_at Timestamp of successful delivery
error Error message if delivery failed
created_at When the notification was created

CLI access (planned)

swarmkit notifications --last 10        # recent notifications
swarmkit notifications --pending        # undelivered
swarmkit notifications --run <run-id>   # for a specific run

Web UI access (planned)

The web UI reads from the same NotificationStore via the WorkspaceRuntime service layer. Same data, same API — no separate notification backend needed.

Webhook payload format

The generic webhook provider sends:

{
  "event_type": "hitl_requested",
  "run_id": "run-001",
  "topology_id": "code-review",
  "summary": "Deploy approval needed for high-risk change",
  "metadata": {
    "agent_id": "resolution-agent",
    "review_queue_id": "rq-42"
  }
}

Adding custom providers

Implement the NotificationProvider ABC:

from swarmkit_runtime.notifications import NotificationProvider, NotificationEvent

class MyProvider(NotificationProvider):
    provider_id = "my-service"

    async def notify(self, event: NotificationEvent) -> bool:
        # Send to your service
        # Return True if delivered, False otherwise
        ...

Provider errors are caught by the registry — a failed delivery never crashes the runtime.