Agent presence

Your users see when the agent is thinking, streaming, idle, or offline. AI Transport channels carry Ably Presence, so an agent self-reports its state and every client sees it in real time.

Agent presence gives session participants a real-time view of which agents are active and what they are doing. Agent presence uses Ably's native Presence API on the AI Transport session channel. This works for a single orchestrator agent or a fleet of sub-agents, and conveys whether the agent is streaming, thinking, idle, or offline.

Diagram showing presence-aware agent status updates

How it works

The agent enters presence on the AI Transport session channel with status data. As the agent moves through its turn lifecycle (receiving a message, thinking, streaming, finishing), it updates its presence data. Every connected client receives these updates in real time.

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// Server: agent enters presence when it connects.
const channel = ably.channels.get(sessionChannelName);
await channel.presence.enter({ status: 'idle' });

app.post('/api/chat', async (req, res) => {
  const { turnId, clientId, messages } = req.body;
  const turn = transport.newTurn({ turnId, clientId });

  await channel.presence.update({ status: 'thinking' });

  const result = streamText({
    model: openai('gpt-4o'),
    messages,
    abortSignal: turn.abortSignal,
  });

  await channel.presence.update({ status: 'streaming' });
  await turn.streamResponse(result.toUIMessageStream());
  await channel.presence.update({ status: 'idle' });
  res.json({ ok: true });
});

Subscribe to agent status

On the client, subscribe to presence events to track the agent's current state:

JavaScript

1

2

3

4

5

6

7

8

9

10

const channel = ably.channels.get(sessionChannelName);

channel.presence.subscribe((member) => {
  if (member.clientId === 'agent') {
    console.log(`Agent is ${member.data.status}`);
  }
});

const members = await channel.presence.get();
const agent = members.find((m) => m.clientId === 'agent');

Combine presence with active turns

For richer status indicators, combine presence data with useActiveTurns. Presence tells you the agent's self-reported state. Active turns tell you which turns are in progress:

JavaScript

1

2

3

4

5

6

const activeTurns = useActiveTurns({ transport });
const agentStatus = useAgentPresence(channel); // your custom hook

const isStreaming = activeTurns.has('agent');
const isIdle = agentStatus === 'idle' && !isStreaming;
const isOffline = agentStatus === null;

This is enough information for the UI to show a typing indicator while the agent thinks, a streaming animation while tokens arrive, and an offline badge when the agent disconnects.

Edge cases and unhappy paths

  • An agent that exits without calling presence.leave() (for example, a crashed process) is automatically removed from presence after a timeout. The agent is treated as present until the timeout fires. Wire a graceful shutdown that calls leave for the best user experience.
  • A serverless agent that comes up for one turn and tears down should enter and leave presence per turn; entering once and leaving once at the end is fine for a long-running agent.
  • Presence updates do not guarantee strict ordering with channel messages. A streaming presence update sometimes arrives slightly after the first token. Drive the UI off active turns for token-level state and use presence for higher-level status.
  • Multi-agent setups need unique clientId per agent. Two agents with the same clientId collide in the presence set.
  • A client without presence capability cannot subscribe to updates. Capability scoping is part of authentication.

FAQ

Does presence cost a message?

Presence enter, update, and leave each consume a message on the channel. See the platform pricing for current rates.

Can clients enter presence too?

Yes. Presence is symmetric. A client that enters presence shows up alongside agents in the presence set. Use the clientId to distinguish.

How long does presence persist after a disconnect?

Until Ably's presence timeout fires (currently around 15 seconds). Active connections are not affected; this is for ungraceful disconnects.

What is the difference between presence and useActiveTurns?

Presence is self-reported by the agent. Active turns are observable from the channel by inspecting turn lifecycle events. Presence reports intent; active turns report fact. Both together produce richer status.

Can I pause inference when no users are connected?

Yes. Subscribe to presence and check whether any non-agent participants are present. If none, end the turn or short-circuit the LLM call. This is one of the cost-saving patterns presence enables.