Chat¶
The browser chat at sudohomes.com/chat. It's the simplest surface to reason about and a
great place to test the agent without a Pi.
The two-connection design¶
Chat uses two connections at once:
- A persistent SSE stream for everything coming out of the agent —
GET /v1/me/chat/stream?token=<jwt>. It stays open for the whole session. - Fire-and-forget POSTs for everything going in —
POST /v1/me/chat/turn.
Why the token is on the query string
EventSource (the browser SSE client) can't set custom headers, so the Supabase JWT
rides as ?token=<jwt> on the stream URL. The POST turns use a normal
Authorization header.
Fan-out: multi-tab and proactive for free¶
The sudo_chat plugin's send() / edit_message() POST per-token deltas back to
POST /v1/internal/chat/push, and sudo-api fans them out to every open SSE consumer
for that user. Two consequences fall out of this design:
- Multi-tab just works — open
/chatin three tabs, they all show the same stream. - Proactive messages appear without typing — a
cronjob(deliver="sudo_chat")orsend_message(target="sudo_chat")rides the same fan-out, so a scheduled nudge pops into any open tab.
Reactive vs proactive¶
| Direction | Path |
|---|---|
| Reactive (you type) | browser → POST /v1/me/chat/turn → sudo_chat:8652/inbound → agent → /v1/internal/chat/push → SSE |
| Proactive (agent starts) | cronjob/send_message(target="sudo_chat") → plugin send() → /v1/internal/chat/push → SSE |
Where to look¶
| Concern | File |
|---|---|
| Chat routes + SSE fan-out | cloud/api/main.py (/v1/me/chat/*, /v1/internal/chat/push) |
| The plugin adapter | cloud/hermes/plugins/sudo_chat/ |
| The page | cloud/api/templates/chat.html |