Chat SDK
Overview
Agents don't just generate text - they execute tools, manipulate files, call APIs, and perform complex operations. The status event system lets you monitor these actions and build custom UI around them.
Why Status Events Matter
When agents take actions in your application, you need to:
- Show progress indicators while tools execute
- Display custom UI for specific operations
- Track performance metrics and errors
- Build activity logs for debugging
- Create interactive workflows based on agent actions
Basic Usage
Single Action Monitoring
Listen to specific agent actions:
import { useStatusSubscription } from 'react-chat-agent';
function ToolMonitor() {
useStatusSubscription('tool_call', (statusData) => {
console.log('Tool:', statusData.tool_name);
console.log('State:', statusData.status.state); // 'started' | 'finished'
console.log('Result:', statusData.result);
});
return <div>Monitoring agent tools...</div>;
}Multiple Actions
Track different operations:
function AgentMonitor() {
const [activeOperation, setActiveOperation] = useState(null);
useStatusSubscription({
tool_call: (data) => {
setActiveOperation(`Executing tool: ${data.tool_name}`);
},
edit_file: (data) => {
setActiveOperation(`Editing: ${data.file_path}`);
},
api_call: (data) => {
setActiveOperation(`API: ${data.endpoint}`);
}
});
return <div className="status-bar">{activeOperation}</div>;
}Real-World Examples
Progress Indicators
Show live progress for agent operations:
function LiveProgress() {
const [operations, setOperations] = useState(new Map());
useStatusSubscription({
tool_call: handleToolStatus,
edit_file: handleFileStatus,
deploy: handleDeployStatus
});
function handleToolStatus(data) {
const { taskId, tool_name, status } = data;
setOperations(prev => new Map(prev).set(taskId, {
type: 'tool',
name: tool_name,
status: status.state,
startTime: Date.now()
}));
if (status.state === 'finished') {
setTimeout(() => {
setOperations(prev => {
const next = new Map(prev);
next.delete(taskId);
return next;
});
}, 2000);
}
}
return (
<div className="progress-panel">
{Array.from(operations.values()).map(op => (
<div key={op.name} className="progress-item">
<Spinner />
<span>{op.name}</span>
<span className="status">{op.status}</span>
</div>
))}
</div>
);
}File Operation Tracker
Track file modifications in real-time:
function FileTracker() {
const [fileOps, setFileOps] = useState([]);
useStatusSubscription('edit_file', (data) => {
const { file_path, status, content } = data;
if (status.state === 'started') {
setFileOps(prev => [...prev, {
file: file_path,
status: 'editing',
timestamp: new Date().toISOString()
}]);
} else if (status.state === 'finished') {
setFileOps(prev => prev.map(op =>
op.file === file_path
? { ...op, status: 'completed', size: content?.length }
: op
));
}
});
return (
<div className="file-tracker">
<h3>File Operations</h3>
{fileOps.map((op, i) => (
<div key={i} className={`file-op ${op.status}`}>
<FileIcon type={getFileType(op.file)} />
<span>{op.file}</span>
<Badge>{op.status}</Badge>
{op.size && <span>{formatBytes(op.size)}</span>}
</div>
))}
</div>
);
}Build Pipeline Monitor
Monitor multi-step processes:
function BuildMonitor() {
const [steps, setSteps] = useState({
install: 'pending',
build: 'pending',
test: 'pending',
deploy: 'pending'
});
useStatusSubscription({
install_dependencies: (data) => {
setSteps(prev => ({
...prev,
install: data.status.state
}));
},
build_project: (data) => {
setSteps(prev => ({
...prev,
build: data.status.state
}));
},
run_tests: (data) => {
setSteps(prev => ({
...prev,
test: data.status.state
}));
},
deploy: (data) => {
setSteps(prev => ({
...prev,
deploy: data.status.state
}));
}
});
return (
<div className="build-pipeline">
{Object.entries(steps).map(([step, status]) => (
<div key={step} className={`step ${status}`}>
<StatusIcon status={status} />
<span>{step}</span>
</div>
))}
</div>
);
}API Call Monitor
Track all API calls made by agent:
function APIMonitor() {
const [apiCalls, setApiCalls] = useState([]);
useStatusSubscription('api_call', (data) => {
const { endpoint, method, status, duration, response } = data;
if (status.state === 'finished') {
setApiCalls(prev => [...prev, {
endpoint,
method,
duration,
statusCode: response?.status,
timestamp: Date.now()
}]);
}
});
return (
<div className="api-monitor">
<h3>API Calls</h3>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Method</th>
<th>Status</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{apiCalls.map((call, i) => (
<tr key={i}>
<td>{call.endpoint}</td>
<td>{call.method}</td>
<td className={call.statusCode === 200 ? 'success' : 'error'}>
{call.statusCode}
</td>
<td>{call.duration}ms</td>
</tr>
))}
</tbody>
</table>
</div>
);
}Error Tracking
Monitor and display errors:
function ErrorTracker() {
const [errors, setErrors] = useState([]);
useStatusSubscription('*', (data) => {
if (data.error || data.status?.state === 'error') {
setErrors(prev => [...prev, {
action: data.action,
error: data.error?.message,
timestamp: Date.now()
}]);
}
});
return (
<div className="error-panel">
{errors.map((error, i) => (
<div key={i} className="error-item">
<ErrorIcon />
<div>
<strong>{error.action}</strong>
<p>{error.error}</p>
<small>{new Date(error.timestamp).toLocaleString()}</small>
</div>
</div>
))}
</div>
);
}Status Data Structure
interface StatusData {
action: string; // Action type: 'tool_call', 'edit_file', etc.
statusType: 'started' | 'finished' | 'error';
status: {
state: 'started' | 'processing' | 'finished' | 'error';
};
taskId: string; // Unique task identifier
messageId: string; // Associated message ID
// Action-specific data
tool_name?: string; // For tool_call
file_path?: string; // For edit_file
endpoint?: string; // For api_call
result?: any; // Operation result
error?: { // If error occurred
message: string;
code?: string;
};
}Global Listener
Listen to ALL agent actions:
function GlobalMonitor() {
const [allActions, setAllActions] = useState([]);
useStatusSubscription('*', (data) => {
setAllActions(prev => [...prev, {
action: data.action,
status: data.status.state,
timestamp: Date.now()
}].slice(-50)); // Keep last 50 actions
});
return (
<div className="activity-log">
{allActions.map((action, i) => (
<div key={i}>
<span>{action.action}</span>
<Badge>{action.status}</Badge>
<time>{new Date(action.timestamp).toLocaleTimeString()}</time>
</div>
))}
</div>
);
}Toast Notifications
Show notifications for agent actions:
import { toast } from 'react-hot-toast';
function AgentNotifications() {
useStatusSubscription({
tool_call: (data) => {
if (data.statusType === 'started') {
toast.loading(`Executing ${data.tool_name}...`, {
id: data.taskId
});
} else if (data.statusType === 'finished') {
toast.success(`${data.tool_name} completed`, {
id: data.taskId
});
}
},
error: (data) => {
toast.error(data.error.message);
}
});
return null;
}Analytics Tracking
Track agent performance metrics:
function AnalyticsTracker() {
const [metrics, setMetrics] = useState(new Map());
useStatusSubscription('*', (data) => {
const { action, statusType, taskId } = data;
if (statusType === 'started') {
setMetrics(prev => new Map(prev).set(taskId, {
action,
startTime: Date.now()
}));
} else if (statusType === 'finished') {
const metric = metrics.get(taskId);
if (metric) {
const duration = Date.now() - metric.startTime;
// Send to analytics
trackEvent('agent_action', {
action: metric.action,
duration,
success: !data.error
});
setMetrics(prev => {
const next = new Map(prev);
next.delete(taskId);
return next;
});
}
}
});
return null;
}Workflow Automation
Trigger actions based on agent events:
function WorkflowAutomation() {
useStatusSubscription({
code_generated: async (data) => {
// Auto-format generated code
if (data.statusType === 'finished') {
await formatCode(data.result);
await runLinter(data.result);
}
},
tests_written: async (data) => {
// Auto-run tests
if (data.statusType === 'finished') {
await runTests();
}
},
deploy_ready: async (data) => {
// Auto-deploy if tests pass
const testsPass = await checkTests();
if (testsPass) {
await deploy();
}
}
});
return <div>Automation active</div>;
}Combining with Forwarding
Use both hooks together for complete control:
function CompleteControl() {
const [code, setCode] = useState('');
const [status, setStatus] = useState('idle');
// Forward code to editor
useForwarding('write_code', (inc, full) => {
setCode(full);
}, { mode: 'replace' });
// Monitor the operation
useStatusSubscription('write_code', (data) => {
setStatus(data.status.state);
if (data.statusType === 'finished') {
// Code complete, run linter
lintCode(code);
}
});
return (
<div>
<StatusBar status={status} />
<CodeEditor value={code} />
</div>
);
}Best Practices
Memory Management
// ✅ Clean up old data
useStatusSubscription('tool_call', (data) => {
if (data.statusType === 'finished') {
setTimeout(() => cleanupTask(data.taskId), 5000);
}
});Performance
// ✅ Batch updates
const [operations, setOperations] = useState([]);
useStatusSubscription('*', (data) => {
setOperations(prev => [...prev, data].slice(-100)); // Keep last 100
});Error Handling
// ✅ Handle errors gracefully
useStatusSubscription('*', (data) => {
if (data.error) {
logError(data.error);
showUserFriendlyMessage(data.action);
}
});TypeScript Support
import type { StatusHandler, StatusHandlers } from 'react-chat-agent';
const handler: StatusHandler = (data) => {
// Type-safe handler
};
const handlers: StatusHandlers = {
tool_call: (data) => {
// Auto-complete for action-specific data
}
};Next Steps
- Forwarding - Control agent output
- Multi-Instance - Run multiple agents
- Customization - Style your UI