Skip to content

Control surfaces

The dashboard, Telegram bot, and Slack bot are peers too. They show up in list_peers alongside agents and use the same ask / notify_peer / broadcast primitives.

  • dashboard — Next.js UI at localhost:8377/dashboard, with a live mesh log and per-peer chat. See web dashboard.
  • telegram — bot you talk to from your phone. Sticky routing: /select peer sends subsequent messages as asks to that peer until /clear; /notify and /fyi remain fire-and-forget. See Telegram bot.
  • slack — Socket Mode bot. Same sticky-routing pattern with Block Kit peer pickers; notify and fyi remain fire-and-forget. See Slack bot.

Human framing

Messages from @telegram, @slack, and @dashboard are humans. Agents that receive messages from these peers treat them as direct user instructions, not as agent-to-agent traffic. Telegram and Slack human inbound messages open tracked ask threads by default, so the recipient is reminded until it acks or replies. This framing is injected by repowire at delivery time — agents do not need to special-case the names themselves.

When an agent wants to reach the human, the move is notify_peer("telegram", "...") (or notify_peer("dashboard", ...), though the dashboard already sees turns and rarely needs an explicit notification). These peers have the human role, which bypasses circle filtering — they resolve and appear in list_peers mesh-wide regardless of the caller's circle.

What surfaces don't do

Control surfaces are clients of the routing API, not part of it. The daemon is the single source of truth for peer state and message routing. A surface that crashes does not lose mesh state; reopening it picks back up from the daemon.

Session-targeted controls

The first session-native control API targets durable repowire_session_id bindings rather than display names:

  • POST /sessions/{repowire_session_id}/controls/notify resolves the binding to its current active executor and sends through the existing notify delivery path. If no active executor is attached, it returns session_executor_unavailable instead of guessing from path or display name.
  • POST /sessions/{repowire_session_id}/controls/resume reports the current resume capability for the binding. Active sessions return status=active_executor; detached local-agent sessions with captured runtime ids return status=resume_available. Passing dry_run=false spawns the backend-native resume command through the daemon spawn service.

Session resume is not limited to peers originally spawned by Repowire. Spawn ownership is still required for destructive pane operations such as peer restart/kill, but session resume only needs a captured backend runtime session id and a backend that supports local resume.

These routes require the SQLite session binding store. Existing peer-targeted ask, notify_peer, and dashboard routes remain compatible and continue to route by peer identity.