AI SDK UI (useChat)
Runcell speaks the AI SDK UI Message Stream protocol, so useChat consumes a runcell agent exactly as it consumes a streamText backend. No adapter is needed.
Backend
ts
// app/api/chat/route.ts
import { createAgent } from 'runcell';
const agent = createAgent({ model: 'anthropic/claude-sonnet-4-5' });
export async function POST(req: Request): Promise<Response> {
const { messages } = await req.json();
return agent
.stream({ messages, signal: req.signal })
.toUIMessageStreamResponse();
}Frontend
tsx
import { useChat } from '@ai-sdk/react';
export function Chat() {
const { messages, sendMessage } = useChat(); // posts to /api/chat by default
return (
<>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, i) =>
part.type === 'text' ? <span key={i}>{part.text}</span> : null,
)}
</div>
))}
</>
);
}Messages carry the full part structure: text, reasoning, and tool-* parts with their input, output, and state. message.metadata carries the run's usage (token counts, costUsd, costMeasured) and sessionId from the finish chunk.
Controlling what crosses the wire
toUIMessageStreamResponse accepts the same option names as the AI SDK's, plus tool redaction:
ts
return agent.stream({ messages }).toUIMessageStreamResponse({
sendReasoning: false, // keep thinking server-side
sendTools: 'names-only', // tool names without payloads
onError: error => 'Something went wrong.', // masked by default
});See wire-level controls for the full option reference, and threads for durable server-side conversation state as an alternative to replaying messages.