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