Headless mode
You can use AgentProvider + useAgentChat() without any SDK UI components to build a fully custom chat interface:
import { AgentProvider, useAgentChat } from '@kapaai/agent-react';
const CustomChat = () => {
const {
messages,
isStreaming,
inputValue,
setInputValue,
sendMessage,
resetConversation,
stopGeneration,
approveToolCall,
rejectToolCall,
} = useAgentChat();
return (
<div>
{messages.map((msg, i) => (
<div key={i}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage(inputValue);
}
}}
/>
</div>
);
};
function App() {
return (
<AgentProvider
getSessionToken={async () => {
const res = await fetch('/api/session', { method: 'POST' });
return res.json();
}}
projectId="your-project-id"
integrationId="your-integration-id"
model="kapa-agent-1.0"
>
<CustomChat />
</AgentProvider>
);
}
In headless mode you handle all rendering, including message bubbles, tool call cards, approval buttons, and streaming indicators. The hook gives you the data and actions, you build the UI.
For the message data structure (ConversationMessage, ContentBlock, ToolCallDisplay, ToolCallStatus), see Message format in the core SDK docs. The types are the same. @kapaai/agent-react re-exports them from @kapaai/agent-core.
useAgentChat() return value
| Field | Type | Description |
|---|---|---|
messages | ConversationMessage[] | All messages in the conversation. |
isStreaming | boolean | Whether the agent is currently streaming a response. |
threadId | string | null | Current conversation thread ID. |
inputValue | string | Current input field value. |
setInputValue | (value: string) => void | Update the input field value. |
sendMessage | (text: string) => Promise<void> | Send a message and trigger the agent loop. |
resetConversation | () => void | Clear messages and abort any in-progress request. |
stopGeneration | () => void | Abort the current streaming response. |
approveToolCall | (id: string) => void | Approve a tool waiting for confirmation. |
rejectToolCall | (id: string) => void | Reject a tool waiting for confirmation. |
getFaviconUrl | (sourceUrl: string) => Promise<string> | Fetch a favicon for a source URL. Returns a blob URL. |
listThreads | (options?) => Promise<ThreadListResult> | List past conversations. Requires external_owner_id on the session. See Conversation history. |
resumeThread | (threadId: string) => Promise<void> | Load a past conversation and replace the current chat state. |
deleteThread | (threadId: string) => Promise<void> | Delete a thread. Resets the chat if the thread is currently active. |
historyDisabled | boolean | Whether conversation history is disabled (mirrors the enableHistory prop on AgentProvider). |