Core SDK
AI Agents
Invoke agents in rooms, REST invoke, and handoff.
Agents are configured on the Worker (dashboard or admin API). Clients invoke them. There is no client.createAgent() in the SDK.
Configure on the Worker
Create agents with admin JWT:
curl -X POST "$FLUXY_BASE_URL/agents" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "Assistant",
"handle": "assistant",
"provider": "openai",
"model": "gpt-4o-mini",
"capabilities": ["chat"]
}'Agents with a handle (e.g. @assistant) auto-invoke when users mention them in a room message.
Invoke from useChat
const { invokeAgent, stopAgentStream } = useChat({ roomId });
await invokeAgent("What is our refund policy?", { agentId: "agt_…" });
// Token edits land on the chat row (`streaming: true`). Stop keeps text already shown.
stopAgentStream();Invoke via REST
await client.invokeAgentRest(agentId, roomId, "Summarize the last 10 messages");Requires member JWT on the client.
Schedule (Cloudflare Agents schedule() HOW)
One-shot delay or 5-field UTC cron on the room Durable Object — not a second Agent DO.
await client.scheduleAgent(roomId, { agentId, kind: "delay", delayMs: 60_000, prompt: "Standup recap" });
await client.scheduleAgent(roomId, { agentId, kind: "cron", cron: "0 9 * * 1-5", prompt: "Daily briefing" });
await client.scheduleAgent(roomId, { agentId, kind: "at", runAt: Date.now() + 120_000, prompt: "Later" });
await client.scheduleAgent(roomId, { agentId, kind: "interval", intervalMs: 60_000, prompt: "Heartbeat" });
await client.getAgentSchedule(roomId, scheduleId);
const pending = await client.listAgentSchedules(roomId);
await client.cancelAgentSchedule(roomId, pending[0].id);Several agents in one room
invokeAgent can run more than one agent on the same roomId. Each call counts against the project's monthly agent_invokes quota (Free includes 5k). Handles (@assistant) auto-invoke from mentions and count the same way.
Four concurrent agents (a restaurant floor, a war room) is a product pattern, not a second protocol. Watch quota and rate limits in the operator console.
One turn at a time per room
Every room-bound turn — REST/SDK invokeAgent, @handle auto-invoke, agent schedules — runs on the same per-room agent instance, FIFO: the next turn starts when the current one finishes, so two turns never stream into one room at the same time. Concurrent callers wait their turn (queue of 8):
POST /agents/:id/invokepast the queue answers429 { "error": "turn_queue_full" }withRetry-After(default 5s).- A mention past the queue is dropped and logged (
agent.mention_turn_queue_full) — the message gets no agent reply.
Self-host without the AGENT binding runs invokes inline in the request (no turn queue).
Handoff
Check whether a human should take over:
const handoff = await client.getRoomHandoff(roomId);
// { shouldHandoff, reason?, suggestedAssignee? }Streaming
When the provider supports it, agent replies stream as message edits over WebSocket (streaming: true on the chat row). stopAgentStream() sends { type: "stream", op: "stop" } on the room socket. The Worker finalizes that chat row with text already shown and aborts the in-flight LLM fetch. Tool calls show as cards when toolExecuteUrl is set. That is not Stream’s ai_indicator.* protocol, and it is not Cloudflare Think fiber recover after eviction.
If the room Durable Object hibernates while the model is still generating, that LLM call does not resume. Reconnect still loads messages that already hit D1. Think’s object is the agent. Ours is the room.
Next steps
- Copilots (side panel, not this timeline)
- Feeds (workflow logs)
- Agent platform
- Handoff guide
- Use case: Assistant room