Skip to content

Instantly share code, notes, and snippets.

@Donavan
Created October 5, 2025 00:29
Show Gist options
  • Save Donavan/121a76ac09b5b947c7f65ff750791844 to your computer and use it in GitHub Desktop.
Save Donavan/121a76ac09b5b947c7f65ff750791844 to your computer and use it in GitHub Desktop.
Agent response to a handoff doc

Thanks for sharing this comprehensive handoff document! I've read through it carefully and understand the Phase 3 changes from the React team.

Summary of Changes Impacting Session Controls

The React hooks (useChat and useToolNotifications) now automatically handle:

  1. Message Updates: Messages can receive tool_calls metadata after initial render
  2. Notification Clearing: Tool notifications automatically clear on session switches and interaction ends

This opens up great UX opportunities for showing tool activity in the session list!

My Implementation Plan

🎯 Priority Decisions

Let me propose answers to the key questions from the handoff:

Q1: Notification Clearing Timing

My Recommendation: Clear DURING session switch animation (concurrent approach)

  • Rationale: Provides smoothest UX, no waiting for sequential operations
  • Implementation: Use CSS exit animations + Promise.all for concurrent operations

Q2: Tool Activity Display

My Recommendation: Start with Option C (both active count and last tool used), iterate based on feedback

  • Phase 1: Badge showing active tool count (global, since no sessionId yet)
  • Phase 2: Last tool used in preview text
  • Phase 3: Future enhancement for per-session tracking when sessionId available

Q3: Session Metadata Storage

My Recommendation: Client-side calculation with memoization for now

  • Rationale: Simpler, no server coordination required, works immediately
  • Future: Can migrate to server-side if performance becomes an issue with 1000+ sessions

πŸ“‹ Implementation Phases

Phase 1: P0 - Core Coordination (Today)

  • Review current session switching logic in ChatSessionList.tsx
  • Add transition state management for smooth clearing
  • Implement CSS exit animations for notification clearing
  • Test rapid session switching for race conditions
  • Ensure no visual glitches during transitions

Phase 2: P1 - Tool Activity Indicators (Next)

  • Add global active tool count badge to session list
  • Calculate tool usage from message history (memoized)
  • Show last tool used in session preview text
  • Add tool activity to session item hover states
  • Update session item types to include tool metadata

Phase 3: P2 - Enhanced Features (Future)

  • Add tool usage to session search/filter
  • Display tool usage timeline in session details
  • Show historical tool stats per session
  • Coordinate with backend for sessionId in notifications

πŸ” Components I'll Modify

  1. ChatSessionList.tsx

    • Add transition state for session switches
    • Integrate global tool notification count
    • Calculate and display tool activity per session
  2. SessionItem.tsx (or equivalent internal component)

    • Add tool activity badge
    • Show last tool used in preview
    • Add tool-related hover states
  3. ChatSidebar.tsx

    • Coordinate notification clearing with sidebar state
    • Ensure smooth transitions during session switches

πŸ“Š Type Additions

I'll create new types for tool activity tracking:

interface SessionToolActivity {
  sessionId: string;
  activeToolCount: number;        // From global notifications
  lastToolUsed?: string;          // From message tool_calls
  lastToolTimestamp?: string;
  totalHistoricalCalls: number;
  toolsUsed: string[];
}

Questions Before I Start

  1. Design System: Are there existing Censuite badge components for tool activity indicators, or should I create new ones following the design system?

  2. Animation Timing: What's the standard transition duration for session switches? Should I match that for notification clearing (300ms suggested in doc)?

  3. Coordination Point: Should I reach out to the UI Chat Controls specialist about consistent tool call rendering patterns before implementing, or proceed with session list implementation first?

  4. Testing Scope: Should I coordinate with my test partner NOW before implementing, or implement P0/P1 first and then hand off for testing?

Let me know if my approach looks good, and I'll start with Phase 1 (P0 coordination work)! πŸš€

React Team Handoff - Phase 3: React Hook Updates

Dev-to-Test Handoff: Tool Call Fix Phase 3

From: Eve (Event Stream Development Specialist)
To: React Development Team
Date: October 4, 2025
Work Unit: Update React hooks to handle new tool call events from Phase 1 & 2


🎯 Objective

Update React hooks to handle new events from the core package so that:

  1. Messages can be updated after initial render (backward tool attachment)
  2. Tool notifications can be cleared by session or globally
  3. Event signatures match the updated core package API

πŸ“‹ Context: What Phase 1 & 2 Accomplished

Phase 1: Session-Aware Tracking & Backward Attachment βœ…

  • Implemented session-aware tool tracking (composite keys: ${sessionId}:${toolId})
  • Added backward attachment logic (attach tools to previous message)
  • Added proper cleanup when interaction ends
  • Created safety net cleanup on user turn start
  • Added new events that React hooks need to handle

Phase 2: Resumed Session Tool Display βœ…

  • Fixed tool call display in resumed sessions
  • Tool calls from saved messages now attach to message metadata
  • Consistent structure between live and resumed sessions

Phase 3 (This Work): React Hook Updates πŸ”¨

  • Handle new events in React hooks
  • Update message state when backward attachment occurs
  • Clear notifications when requested by core
  • Match API signature changes

πŸ”₯ New Events from Core Package

Event 1: message-updated (NEW)

Purpose: Notify React layer that a message was updated with backward-attached tool calls

Signature:

'message-updated': {
  sessionId: string;
  messageIndex: number;
  message: Message;  // Updated message with tool calls in metadata
}

When Emitted:

  • In EventStreamProcessor.handleCompletion() when tool calls are backward-attached to a previous message
  • Happens when an interaction ends and tools need to be attached to the last message

Example:

// Core emits this after backward attachment
sessionManager.emit('message-updated', {
  sessionId: 'session-123',
  messageIndex: 5,
  message: {
    role: 'assistant',
    content: 'I will help you with that.',
    metadata: {
      toolCalls: [
        { id: 'toolu_abc', type: 'tool_use', name: 'workspace_read', input: {...} }
      ],
      toolResults: [
        { type: 'tool_result', tool_use_id: 'toolu_abc', content: '...' }
      ]
    }
  }
});

Event 2: tool-notifications-cleared (NEW)

Purpose: Clear tool notifications for a specific session or globally

Signature:

'tool-notifications-cleared': {
  sessionId?: string;  // If undefined, clear ALL sessions
  reason: 'interaction_ended' | 'user_turn_start';
}

When Emitted:

  1. With sessionId - When InteractionEndEvent with started: false is received

    • Clears tools for specific session
    • Reason: 'interaction_ended'
  2. Without sessionId - When user_turn_start event is received (safety net)

    • Clears ALL active notifications globally
    • Reason: 'user_turn_start'

Examples:

// Clear tools for specific session (proper cleanup)
sessionManager.emit('tool-notifications-cleared', {
  sessionId: 'session-123',
  reason: 'interaction_ended'
});

// Clear all tools globally (safety net)
sessionManager.emit('tool-notifications-cleared', {
  sessionId: undefined,
  reason: 'user_turn_start'
});

Event 3: tool-notification-removed (SIGNATURE CHANGED)

Purpose: Remove a specific tool notification (unchanged purpose, new signature)

OLD Signature:

'tool-notification-removed': string  // Just the toolId

NEW Signature:

'tool-notification-removed': {
  sessionId: string;
  toolId: string;
}

Why Changed: Session-aware tracking requires both IDs to identify the correct tool

Impact:

  • βœ… Already updated in Phase 1: useToolNotifications.ts line 89
  • No further changes needed, but documented here for completeness

πŸ“ Files That Need Updates

File 1: packages/react/src/hooks/useMessages.ts πŸ”¨ PRIMARY

Current State: Listens to messages event to receive new messages from core

Change Needed: Add listener for message-updated event to update existing messages in state

Location: Line ~57 (in useEffect where event listeners are set up)


File 2: packages/react/src/hooks/useToolNotifications.ts πŸ”¨ PRIMARY

Current State:

  • Listens to tool-notification event to add notifications
  • Listens to tool-notification-removed event to remove individual notifications
  • Has local clearNotifications() method but no listener for global clear

Change Needed: Add listener for tool-notifications-cleared event

Location: Line ~66 (in useEffect where event listeners are set up)


πŸ”¨ Implementation Details

Implementation 1: Handle message-updated in useMessages.ts

Current Code Structure (Lines ~30-80)

useEffect(() => {
  if (!client) return;

  const handleMessages = (newMessages: Message[]) => {
    // Adds new messages to state
    setMessages(prev => [...prev, ...newMessages]);
  };

  client.on('messages', handleMessages);

  return () => {
    client.off('messages', handleMessages);
  };
}, [client]);

Required Changes

Add New Handler:

const handleMessageUpdated = useCallback((data: { 
  sessionId: string; 
  messageIndex: number; 
  message: Message;
}) => {
  setMessages(prev => {
    // Create a new array to trigger React re-render
    const updated = [...prev];
    
    // Replace the message at the specified index
    if (data.messageIndex >= 0 && data.messageIndex < updated.length) {
      updated[data.messageIndex] = data.message;
    }
    
    return updated;
  });
}, []);

Register Event Listener:

useEffect(() => {
  if (!client) return;

  const handleMessages = (newMessages: Message[]) => {
    setMessages(prev => [...prev, ...newMessages]);
  };

  // NEW: Add message-updated listener
  const handleMessageUpdated = (data: { 
    sessionId: string; 
    messageIndex: number; 
    message: Message;
  }) => {
    setMessages(prev => {
      const updated = [...prev];
      if (data.messageIndex >= 0 && data.messageIndex < updated.length) {
        updated[data.messageIndex] = data.message;
      }
      return updated;
    });
  };

  client.on('messages', handleMessages);
  client.on('message-updated', handleMessageUpdated);  // NEW

  return () => {
    client.off('messages', handleMessages);
    client.off('message-updated', handleMessageUpdated);  // NEW
  };
}, [client]);

Edge Cases to Consider

  1. Invalid Index: Check bounds before updating

    • If messageIndex is out of bounds, log warning and skip update
  2. Session Mismatch: Should we validate sessionId?

    • Current implementation doesn't track session per message
    • For now, trust the core package (it only emits for current session)
    • Future: Consider validating if session tracking added to hook
  3. Message Already Has Metadata:

    • This is valid - backward attachment adds metadata to existing message
    • Replace entire message (don't merge) as core sends complete updated message
  4. Empty Messages Array:

    • Bounds check handles this (messageIndex >= messages.length returns false)

Implementation 2: Handle tool-notifications-cleared in useToolNotifications.ts

Current Code Structure (Lines ~30-100)

useEffect(() => {
  if (!client) return;

  const handleToolNotification = (notification: ToolNotification) => {
    setNotifications(prev => {
      // Add or update notification
      const existing = prev.find(n => n.id === notification.id);
      if (existing) {
        return prev.map(n => n.id === notification.id ? notification : n);
      }
      return [...prev, notification];
    });
  };

  const handleToolNotificationRemoved = (data: { sessionId: string; toolId: string }) => {
    setNotifications(prev => prev.filter(n => n.id !== data.toolId));
  };

  client.on('tool-notification', handleToolNotification);
  client.on('tool-notification-removed', handleToolNotificationRemoved);

  return () => {
    client.off('tool-notification', handleToolNotification);
    client.off('tool-notification-removed', handleToolNotificationRemoved);
  };
}, [client]);

Required Changes

Add New Handler:

const handleToolNotificationsCleared = useCallback((data: { 
  sessionId?: string; 
  reason: 'interaction_ended' | 'user_turn_start';
}) => {
  if (data.sessionId) {
    // Clear notifications for specific session
    setNotifications(prev => 
      prev.filter(n => n.sessionId !== data.sessionId)
    );
  } else {
    // Clear ALL notifications
    setNotifications([]);
  }
}, []);

Register Event Listener:

useEffect(() => {
  if (!client) return;

  const handleToolNotification = (notification: ToolNotification) => {
    setNotifications(prev => {
      const existing = prev.find(n => n.id === notification.id);
      if (existing) {
        return prev.map(n => n.id === notification.id ? notification : n);
      }
      return [...prev, notification];
    });
  };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment