@agentbuilder/react
v0.7.6
Published
React composables for AgentBuilder
Readme
@agentbuilder/react
React hooks and components for AgentBuilder - connect to AI agent threads with real-time updates, send messages, and listen for custom events.
Installation
npm install @agentbuilder/react
# or
pnpm add @agentbuilder/react
# or
yarn add @agentbuilder/reactQuick Start
import {
AgentBuilderProvider,
ThreadProvider,
useThread,
sendMessage,
} from "@agentbuilder/react"
function App() {
return (
<AgentBuilderProvider config={{ endpoint: "https://your-api.com" }}>
<ThreadProvider threadId="thread-123">
<ChatInterface />
</ThreadProvider>
</AgentBuilderProvider>
)
}
function ChatInterface() {
// No thread ID needed - inherited from ThreadProvider context
const messages = useThread()
const handleSend = async (text: string) => {
await sendMessage("thread-123", {
role: "user",
content: text,
})
}
return (
<div>
{messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
<input onSubmit={(e) => handleSend(e.currentTarget.value)} />
</div>
)
}Authentication
The package reads the authentication token from localStorage using the key agentbuilder_auth_token:
// Set the token before using the hooks
localStorage.setItem("agentbuilder_auth_token", "your-token-here")All API requests and WebSocket connections will automatically include this token.
API Reference
AgentBuilderProvider
Context provider that configures the AgentBuilder client for all child components.
Props:
config.endpoint: string- The API endpoint URL (e.g.,https://api.example.com)
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
{children}
</AgentBuilderProvider>ThreadProvider
Context provider that establishes a WebSocket connection to a specific thread. Must be nested inside AgentBuilderProvider. Provides thread context to child hooks like useThread and onThreadEvent.
Props:
threadId: string- The thread ID to connect topreload?: boolean- Fetch existing messages on mount (default:true)live?: boolean- Enable WebSocket for real-time updates (default:true)depth?: number- Message depth level for nested conversations (default:0)includeSilent?: boolean- Include silent messages (default:false)endpoint?: string- Override the endpoint from context
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
<ThreadProvider threadId="thread-123" live={true} depth={0}>
<YourComponents />
</ThreadProvider>
</AgentBuilderProvider>useThread(options?)
Hook to subscribe to thread messages. Must be used within a ThreadProvider.
Parameters:
options?: UseThreadOptions- Configuration options
Options:
useWorkblocks?: boolean- Group tool calls into workblocks (default:true)
Returns: ThreadMessage[] - Array of messages or workblocks
Example:
function ThreadView() {
// Thread ID inherited from ThreadProvider context
const messages = useThread()
return (
<div>
{messages.map((item) => {
if (item.type === "workblock") {
return <WorkblockView key={item.id} workblock={item} />
}
return <MessageView key={item.id} message={item} />
})}
</div>
)
}
// Disable workblocks transformation
function RawMessagesView() {
const messages = useThread({ useWorkblocks: false })
// Returns raw messages without workblock grouping
}useThreadId()
Hook to get the current thread ID from context. Must be used within a ThreadProvider.
Returns: string - The thread ID
Example:
function CurrentThreadId() {
const threadId = useThreadId()
return <span>Thread: {threadId}</span>
}sendMessage(threadId, payload, options?)
Send a message to a thread. This is a standalone function that works outside of React components.
Parameters:
threadId: string- The thread IDpayload: SendMessagePayload- The message to sendrole: 'user' | 'assistant'- Message rolecontent: string | null- Message contentsilent?: boolean- Silent message (not shown to user, default:false)
options?: { endpoint?: string }- Override endpoint
Returns: Promise<Message> - The created message
Example:
import { sendMessage, useThreadId } from "@agentbuilder/react"
function SendButton() {
const threadId = useThreadId()
const handleSend = async () => {
await sendMessage(threadId, {
role: "user",
content: "Hello, agent!",
})
}
return <button onClick={handleSend}>Send</button>
}
// Send a silent message (for context injection)
await sendMessage("thread-123", {
role: "user",
content: "Additional context",
silent: true,
})
// Send an assistant message (for injecting responses)
await sendMessage("thread-123", {
role: "assistant",
content: "Custom assistant response",
})stopThread(threadId, options?)
Cancel an in-flight thread execution.
Parameters:
threadId: string- The thread IDoptions?: { endpoint?: string }- Override endpoint
Returns: Promise<void>
Example:
import { stopThread, useThreadId } from "@agentbuilder/react"
function StopButton() {
const threadId = useThreadId()
const [stopping, setStopping] = useState(false)
const handleStop = async () => {
setStopping(true)
try {
await stopThread(threadId)
} catch (error) {
console.error("Failed to stop thread:", error)
} finally {
setStopping(false)
}
}
return (
<button onClick={handleStop} disabled={stopping}>
{stopping ? "Stopping..." : "Stop Execution"}
</button>
)
}onThreadEvent<T>(eventType)
Hook to listen for custom events emitted by the agent via WebSocket. Must be used within a ThreadProvider.
Parameters:
eventType: string- The custom event type to listen for
Type Parameter:
T- The expected shape of the event data
Returns: T | null - The latest event value, or null if no event received yet
Example:
import { onThreadEvent } from "@agentbuilder/react"
function TodoProgress() {
// Thread ID inherited from ThreadProvider context
const todos = onThreadEvent<{ todos: string[]; completed: number }>(
"todo-updated"
)
if (!todos) return <div>Waiting for updates...</div>
return (
<div>
<p>
Progress: {todos.completed} / {todos.todos.length}
</p>
<ul>
{todos.todos.map((todo, i) => (
<li key={i}>{todo}</li>
))}
</ul>
</div>
)
}Backend Event Emission:
// In your agent's tool or hook (using @agentbuilder/vite)
import { emitThreadEvent } from "@agentbuilder/vite"
// Emit an event to all connected clients
emitThreadEvent(flow, "todo-updated", {
todos: ["Task 1", "Task 2"],
completed: 1,
})Message Types
Regular Message
interface Message {
id: string
role: "user" | "assistant" | "system" | "tool"
content: string | null
created_at: number // microseconds
reasoning_content?: string | null
silent?: boolean
depth?: number
}Workblock
When useWorkblocks: true, tool calls and results are grouped:
interface WorkMessage {
type: "workblock"
id: string
content: string | null
reasoning_content?: string | null
status: "pending" | "completed"
workItems: WorkItem[]
created_at: number
depth?: number
}
interface WorkItem {
id: string
type: "tool_call" | "tool_result"
name: string
input?: string
content?: string
status: "success" | "error" | null
tool_call_id?: string
}Complete Example
import {
AgentBuilderProvider,
ThreadProvider,
useThread,
useThreadId,
sendMessage,
stopThread,
onThreadEvent,
} from "@agentbuilder/react"
import { useState, useEffect } from "react"
function App() {
// Set auth token
useEffect(() => {
localStorage.setItem("agentbuilder_auth_token", "your-token")
}, [])
return (
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
<ThreadProvider threadId="thread-123" live={true}>
<AgentChat />
</ThreadProvider>
</AgentBuilderProvider>
)
}
function AgentChat() {
const [input, setInput] = useState("")
const [isExecuting, setIsExecuting] = useState(false)
// Get thread ID from context
const threadId = useThreadId()
// Subscribe to thread messages (no threadId needed)
const messages = useThread({ useWorkblocks: true })
// Listen for custom progress events (no threadId needed)
const progress = onThreadEvent<{ step: string; percent: number }>("progress")
const handleSend = async () => {
if (!input.trim()) return
setIsExecuting(true)
try {
await sendMessage(threadId, {
role: "user",
content: input,
})
setInput("")
} catch (error) {
console.error("Failed to send message:", error)
} finally {
setIsExecuting(false)
}
}
const handleStop = async () => {
try {
await stopThread(threadId)
setIsExecuting(false)
} catch (error) {
console.error("Failed to stop thread:", error)
}
}
return (
<div>
{/* Progress indicator */}
{progress && (
<div>
<p>{progress.step}</p>
<progress value={progress.percent} max={100} />
</div>
)}
{/* Message list */}
<div>
{messages.map((item) => {
if (item.type === "workblock") {
return (
<div key={item.id}>
<p>Executing tools...</p>
{item.workItems.map((work) => (
<div key={work.id}>
{work.type === "tool_call" && `🔧 ${work.name}`}
{work.type === "tool_result" && `✅ ${work.status}`}
</div>
))}
</div>
)
}
return (
<div key={item.id}>
<strong>{item.role}:</strong> {item.content}
</div>
)
})}
</div>
{/* Input */}
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
disabled={isExecuting}
/>
<button onClick={handleSend} disabled={isExecuting}>
Send
</button>
{isExecuting && <button onClick={handleStop}>Stop</button>}
</div>
</div>
)
}TypeScript Support
The package is written in TypeScript and includes full type definitions.
import type {
Message,
WorkMessage,
ThreadMessage,
SendMessagePayload,
UseThreadOptions,
ThreadProviderOptions,
} from "@agentbuilder/react"Local Development
Building the Package
# Install dependencies
pnpm install
# Build package
pnpm build
# Watch mode (rebuilds on changes)
pnpm dev
# Type check
pnpm typecheck
# Run tests
pnpm testLinking for Local Development
Using file: protocol in your app's package.json:
{
"dependencies": {
"@agentbuilder/react": "file:../path/to/agentbuilder/packages/react"
}
}Or using pnpm link:
# In this package
cd packages/react
pnpm link --global
# In your app
pnpm link --global @agentbuilder/react