Smart Agent Teams

Live updates

How every screen, CLI watcher and runner learns that something changed, without reloading.

SAT pushes changes to clients as they happen. Each company has one server-sent events (SSE) stream; every change in the company publishes a small event on it, such as task.updated or run.updated. Events are invalidations, not data: they say what changed, and each client refetches what it shows. That keeps the web app, sat watch, sat runs tail, the runner and the scheduler in step without polling every few seconds.

This page is the product view. The wire format, reconnect rules and examples for writing your own client are in Live events.

How it works

  1. A request changes something. The API commits the change and its activity log entry, then publishes an event. Publishing after the commit means a client that refetches never sees the old row.
  2. Every client subscribed to that company's stream (GET /api/companies/{company_id}/events) receives the event.
  3. The client decides what is stale and refetches it.

An event carries type (<entity>.<verb>), usually id, and sometimes extra fields such as agent_id and status on run.updated. All values are strings. Each feature page lists its events under Events.

What the web app refreshes

The web app keeps server data in SWR caches keyed by resource and company. On each event it takes the entity part of the type and revalidates every cached resource in this table, plus the activity feed, which refreshes on every event:

Event entityCaches refreshed
tasktasks, task, comments, dashboard, goals, projects, project, agents, agent
agentagents, agent, org chart, dashboard, costs
runruns, run, dashboard, agents, agent, costs, projects, project
approvalapprovals, dashboard, agents, agent
goalgoals
projectprojects, project
routineroutines
companycompany, the company list

The green or amber dot on your avatar in the sidebar shows the connection: "Live updates connected" or "Live updates reconnecting…". After a drop, the web app reconnects after 2, 4, 8, 16 and then 30 seconds.

Firebase Hosting and the events origin

In the hosted deployments the web app is served from Firebase Hosting, which proxies /api to the API on Cloud Run. Firebase Hosting buffers responses, so an SSE stream through it would deliver nothing until the connection closed. Clients therefore open the stream directly on the Cloud Run URL, while every other request still goes through Hosting:

ClientSetting
Web appVITE_EVENTS_ORIGIN, set at build time to the Cloud Run origin. Empty means "same origin" (local development).
CLI, runner, schedulerThe profile's eventsUrl (sat profile set prod --events-url https://…), or the SAT_EVENTS_URL environment variable. The built-in prod profile already points at Cloud Run. sat doctor reports whether the stream connects.

The stream needs the usual Authorization: Bearer header, so clients read it with fetch, not EventSource, and the API's CORS settings must allow the web app's origin.

One instance, or Redis

By default the event bus is in memory, inside each API process. An event published by one instance only reaches clients connected to that same instance. The hosted deployments run the API with a maximum of one Cloud Run instance for this reason. To run more than one instance, set EVENTS_BACKEND=redis (with REDIS_URL) so events go through Redis pub/sub and reach every instance.

What happens if an event is missed

Events are not stored or replayed. A client that is disconnected misses whatever happened meanwhile:

  • sat runner and sat scheduler also poll (the runner every 30 seconds by default, the scheduler reloads every 5 minutes), so a missed event only delays work.
  • sat runs tail polls every 5 seconds alongside the stream; sat watch redraws every 30 seconds.
  • The web app does not refetch when the stream reconnects, and it does not refetch on window focus. After a drop, a screen can show stale data until the next event touches it or you reload the page.

Use it

Nothing to configure: every company screen subscribes when you open the company. Watch the dot on your avatar for the connection state. If a screen looks out of date after a network drop, reload it.

Permissions

Only members can subscribe to a company's stream; others get 404 NOT_FOUND. Every member receives every event in the company. API keys work as well as session tokens.

Events

The full list is on each feature page. In summary: company.updated; agent.created, agent.updated; task.created, task.updated, task.commented, task.deleted; goal.created, goal.updated, goal.deleted; project.created, project.updated; routine.created, routine.updated, routine.deleted; run.created, run.updated; approval.created, approval.updated.

Limits and known gaps

  • No replay, no event ids and no Last-Event-ID support. Missed events are gone.
  • The web app does not resynchronise after reconnecting.
  • In-memory bus by default: more than one API instance needs EVENTS_BACKEND=redis.
  • Each subscriber has a queue of 1,000 events in the in-memory bus.
  • approval.created has no id, and some events (company creation, heartbeats) are not published at all.
  • Events cannot be filtered on the server; sat events --types filters on the client.

On this page