JavaScript / TypeScript SDK
@kapaai/agent-core is the framework-agnostic foundation. It handles session tokens, streaming, tool execution, and the agent loop. No React, no DOM. Works in browsers, Node.js, Deno, and edge runtimes.
Use this package when you're not using React, or when you need full control over the UI. React users should use @kapaai/agent-react instead (install both @kapaai/agent-core and @kapaai/agent-react).
Installation
npm install @kapaai/agent-core
The Agent class
Agent is the main entry point. It combines session management, streaming, tool execution, and approval flow in a single class.
import { Agent } from '@kapaai/agent-core';
const agent = new Agent({
projectId: 'your-project-id',
integrationId: 'your-integration-id',
model: 'kapa-agent-1.0',
tools: [],
context: {},
getSessionToken: async () => {
const res = await fetch('/api/session', { method: 'POST' });
return res.json(); // SDK accepts the raw API response directly
},
onMessagesChange: (messages) => {
// Called whenever messages update (new text, tool calls, etc.)
renderMessages(messages);
},
onStreamingChange: (isStreaming) => {
// Called when streaming starts or stops
toggleLoadingIndicator(isStreaming);
},
});
// Send a message — triggers the agent loop
await agent.sendMessage('How do I get started?');
Methods
| Method | Description |
|---|---|
sendMessage(text) | Send a user message and run the agent loop. |
resetConversation() | Clear all messages and abort any in-progress request. |
stopGeneration() | Abort the current streaming response. |
approveToolCall(id) | Approve a tool that's waiting for user confirmation. |
rejectToolCall(id) | Reject a tool that's waiting for user confirmation. |
updateOptions(partial) | Update tools, context, or other options without resetting. |
getMessages() | Get the current messages array. |
getIsStreaming() | Whether the agent is currently streaming. |
getThreadId() | The current conversation thread ID. |
clearSession() | Force clear the cached session token. |
getFaviconUrl(sourceUrl) | Fetch a favicon for a source URL. Returns a blob URL. |
Options
type AgentOptions<TContext> = {
projectId: string;
integrationId: string;
model: string;
getSessionToken: () => Promise<{ token: string; expiresAt: number }>;
tools: ToolDefinition<TContext>[];
context: TContext;
builtinToolMeta?: Record<string, ToolDisplayMeta>;
customInstructions?: string;
user?: EndUserInfo;
onEvent?: OnAgentEvent;
onMessagesChange: (messages: AgentMessage[]) => void;
onStreamingChange: (isStreaming: boolean) => void;
onThreadIdChange?: (threadId: string | null) => void;
};
Models
The model option specifies which version of the Kapa agent to use. Each model version produces a specific answer style, tool-calling behavior, and response quality. Pinning a model version ensures your integration behaves consistently in production. When a new version is released (e.g. kapa-agent-2.0), your existing integration is unaffected. You can test the new version and upgrade when you are satisfied with the results.
| Model | Description |
|---|---|
kapa-agent-1.0 | First stable agent model. Optimized for knowledge base Q&A and multi-step tool use with streaming. |
Built-in tools
The agent includes a built-in search_knowledge_base tool that searches your project's knowledge base server-side, using the same sources you've configured in the Kapa dashboard. This tool is executed by Kapa's backend and cannot be customized. You can provide display metadata via builtinToolMeta so it shows a friendly name in your UI instead of the raw tool name.
Message format
Messages are either user or assistant messages:
type AgentMessage =
| { role: 'user'; content: string }
| {
role: 'assistant';
content: string;
blocks: ContentBlock[];
isError?: boolean;
};
Assistant messages contain blocks, an array of text and tool call blocks in the order they appeared during streaming:
type ContentBlock =
| { type: 'text'; content: string }
| { type: 'tool_calls'; toolCalls: ToolCallDisplay[] };
Each ToolCallDisplay tracks the lifecycle of a tool call:
type ToolCallDisplay = {
id: string;
name: string;
arguments: Record<string, unknown>;
status: ToolCallStatus;
result?: unknown;
error?: string;
durationMs?: number;
sources?: AgentSource[];
displayName?: string;
needsApproval?: boolean;
};
ToolCallStatus
| Status | Description |
|---|---|
pending | Tool call received, waiting to start. |
approval_requested | Waiting for user to approve or deny. |
executing | Tool's execute function is running. |
completed | Finished successfully. |
error | execute threw an error. |
denied | User clicked Deny. |
stopped | Generation was stopped while tool was in progress. |
Session management
The Agent class handles session tokens automatically:
- Tokens are fetched lazily (only when
sendMessageis first called) - Tokens are cached and refreshed 30 seconds before expiry
- On 401 errors, the token is cleared and retried once
- Concurrent refresh calls are deduplicated
You never need to manage tokens yourself. Just provide the getSessionToken function.
Events
The onEvent callback fires at key moments. Use it for analytics, logging, or monitoring.
const agent = new Agent({
// ...
onEvent: (event) => {
console.log(event.type, event.data);
},
});
| Event | Data | When |
|---|---|---|
message_sent | { messageLength } | User sends a message. |
response_completed | { threadId, toolCallCount } | Agent finishes responding. |
response_error | { error, threadId } | Agent response failed. |
generation_stopped | { threadId } | User stopped generation. |
tool_executed | { toolName, status, durationMs, error? } | A tool finished (success or error). |
tool_approved | { toolName, toolCallId } | User approved a tool. |
tool_denied | { toolName, toolCallId } | User denied a tool. |
conversation_reset | {} | Conversation was reset. |