Chats hooks

React hooks for Aeontel chats.

Hooks

useChat sse

Chat with any chattable entity. Pass the entity's prefixed id — agt_… for agents or swm_… for swarms. The API infers the entity type from the prefix.

Thin wrapper around AI SDK's useChat — wires the Aeontel transport, otherwise the API is identical to AI SDK's. Returns the AI SDK useChat shape (messages, sendMessage, status, …) plus isStreaming / isReady derivations. Manage your own input state with useState("") — matches AI SDK v6 conventions.

AI SDK v6 message shape — two field-name traps:

  1. sendMessage({ text })NOT sendMessage({ content }). The AI SDK auto-wraps text into a parts: [{ type: "text", text }] array internally. Passing content (the OpenAI / Anthropic convention) bypasses that wrap, the request reaches the server with parts: undefined, and Zod validation 500s with "expected array, received undefined" at path [0, "parts"].

  2. Rendering an incoming message — read msg.parts, NOT msg.content. AI SDK v6 messages have a parts array (mixed text/function-call/function-result segments). content does not exist on the message shape.

    TSX
    msg.parts
      .filter((p) => p.type === "text")
      .map((p) => p.text)
      .join("");

Both traps share the same root cause: content is the standard field name across OpenAI / Anthropic / older AI SDK versions, so the intuitive shape is wrong. The error surfaces only at runtime; TS accepts the wrong shape because the input/output types are broad.

TypeScript
useChat(id: ChatTargetId, options: UseChatOptions_ = {})
TypeScript
const { messages, sendMessage, isStreaming } = useChat("agt_...");
const [input, setInput] = useState("");

<input
  value={input}
  onChange={(e) => setInput(e.target.value)}
  onKeyDown={(e) => {
    if (e.key === "Enter" && !isStreaming && input.trim()) {
      sendMessage({ text: input });   // ✓ text — NOT content
      setInput("");
    }
  }}
/>

// NOT m.content — that field doesn't exist on AI SDK v6 messages.
{messages.map((m) => (
  <div key={m.id}>
    {m.parts.filter((p) => p.type === "text").map((p) => p.text)}
  </div>
))}

useKickoffChat composite

Programmatic chat kickoff. Opens a thread between an agent and a user, optionally seeded with a pre-baked opening message authored by the agent. Wraps client.chats.kickoff and invalidates the threads list cache on success so any open chat sidebar pops the new thread immediately.

TypeScript
useKickoffChat(options?: MutationOpts<KickoffChatResponse, KickoffChatParams>)

Types: KickoffChatResponse · KickoffChatParams

TypeScript
const kickoff = useKickoffChat();
kickoff.mutate({
  workspaceId: "wsp_…",
  agentId: "agt_…",
  userId: "usr_…",
  message: "Hi, I noticed your last invoice is due — want help?",
});