mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
* Enhance E2E build process and backend URL handling - Updated `e2e-build.sh` to set CI environment variable correctly for compatibility with various CI runners. - Refactored `backendUrl.ts` to support dynamic backend URL resolution from Vite environment variables, improving flexibility in different deployment contexts. * Implement web channel functionality and enhance chat features - Introduced new web channel commands for sending and canceling chat messages, improving user interaction with the chat system. - Added support for server-sent events (SSE) to handle real-time updates for chat interactions, enhancing responsiveness. - Refactored existing code to improve readability and maintainability, including updates to the core HTTP router and JSON-RPC handling. - Implemented comprehensive end-to-end tests for the web channel flow, ensuring robust functionality and user experience. - Enhanced the overall structure of the web channel module, including event publishing and subscription mechanisms for better scalability. * Refactor socket handling to unify communication across environments - Removed Tauri-specific socket handling from various components, streamlining the codebase to use a single Socket.IO client for both web and desktop environments. - Updated the `useIntelligenceSocket` hook to eliminate Tauri checks and directly utilize the socket service for message sending and chat initialization. - Refactored metadata synchronization in Gmail and Notion services to use the unified socket service, enhancing consistency in socket communication. - Improved error handling and logging across socket interactions to provide clearer feedback during connection issues. - Enhanced the Conversations component to manage socket connection status, ensuring a more robust user experience during chat interactions. * Refactor socket service and enhance core URL resolution - Replaced the deprecated `getBackendUrl` function with `resolveCoreSocketBaseUrl` to improve backend URL resolution for socket connections, accommodating both Tauri and non-Tauri environments. - Introduced `coreSocketBaseFromRpcUrl` to streamline the processing of RPC URLs. - Removed the `tauriSocket.ts` file, consolidating socket handling into a unified service for better maintainability and clarity. - Updated the socket connection logging to provide more informative messages during connection attempts. * Add engineioxide and socketioxide packages to Cargo.lock and update Cargo.toml - Introduced new packages: `engineioxide` and `socketioxide`, both at version 0.15.2, enhancing the project's capabilities. - Updated `Cargo.toml` to include `socketioxide` with the "extensions" feature, ensuring proper integration with the existing codebase. - Added dependencies for both packages, improving modularity and functionality across the application. * Refactor Conversations component and streamline chat handling - Removed unused imports and functions related to Notion context and socket handling, simplifying the Conversations component. - Enhanced error handling during chat interactions, ensuring clearer feedback for users. - Updated chatSend function parameters to eliminate unnecessary context, improving the clarity of the API call. - Consolidated logic for managing chat message history and context, enhancing maintainability and readability of the code. * Enhance core RPC client with improved logging and error handling - Introduced debug logging for socket RPC requests and responses, providing better visibility into the communication process. - Enhanced error handling in the core RPC client to log detailed error messages, improving debugging capabilities. - Updated socket service to log inbound events, ensuring comprehensive tracking of socket interactions. - Removed deprecated MCP handlers from the socket service, streamlining the codebase and improving maintainability. * Refactor apiClient and socketService for improved maintainability - Changed the declaration of `_getToken` from `let` to `var` in `apiClient.ts` to enhance variable scoping. - Removed unused imports in `socketService.ts`, streamlining the code and improving readability. * Refactor deep link handling and enhance E2E testing capabilities - Updated the deep link configuration in `lib.rs` to use a more generalized `#[cfg(desktop)]` directive, improving compatibility across desktop platforms. - Enhanced the `triggerDeepLink` function in `deep-link-helpers.ts` to include macOS-specific app activation and launching logic, ensuring better integration with the macOS environment. - Introduced a new `buildBypassJwt` function to generate bypass JWT tokens for E2E testing, improving the flexibility of authentication flows in tests. - Updated E2E tests in `conversations-web-channel-flow.spec.ts` to utilize the new `triggerAuthDeepLinkBypass` function, enhancing the testing of authentication scenarios. * Enhance deep link handling and E2E test configuration - Updated `triggerAuthDeepLink` in `deep-link-helpers.ts` to support environment-based bypass tokens, improving flexibility in authentication flows during E2E tests. - Modified the `serve_on_ephemeral` function call in `json_rpc_e2e.rs` to disable the core HTTP router's default behavior, enhancing test isolation and control over the testing environment. * Refactor deep link handling and enhance E2E test utilities - Updated `authFlow.e2e.test.tsx` to utilize `window.__simulateDeepLink` for simulating deep links, improving test accuracy and alignment with real-world scenarios. - Modified `lib.rs` to restrict deep link registration to Windows and Linux platforms, clarifying platform-specific behavior. - Enhanced `deep-link-helpers.ts` with a new function to simulate deep links in the WebView, providing a fallback mechanism for E2E tests. - Updated `conversations-web-channel-flow.spec.ts` to reflect changes in deep link triggering, improving logging and test clarity. * Update dependencies in Cargo.lock and Cargo.toml for improved project stability - Removed several unused dependencies from `Cargo.lock` and `Cargo.toml`, including `aes-gcm`, `argon2`, and `async-imap`, streamlining the project and reducing potential security vulnerabilities. - Updated existing dependencies to their latest versions, ensuring compatibility and leveraging improvements in performance and security. - Simplified the dependency structure by consolidating features and removing unnecessary comments, enhancing clarity and maintainability of the configuration files. * Refactor App component and remove unused providers - Removed `AIProvider` and `SkillProvider` from the `App` component, streamlining the component structure and improving readability. - Updated the `UserProvider` to focus solely on bootstrapping the authentication token, enhancing its clarity and purpose. - Adjusted the layout within the `App` component to maintain functionality without the removed providers, ensuring a smooth user experience. * Remove deprecated AI components and types - Deleted the `index.ts`, `loader.ts`, `types.ts`, and related test files from the AI module, streamlining the codebase by removing unused and outdated components. - Eliminated the `default-constitution.md` and associated constitution files, which were no longer relevant to the current architecture. - This cleanup enhances maintainability and reduces complexity within the AI system. * Refactor user data fetching logic and remove legacy tools cache watcher - Enhanced the `useUser` hook to prevent infinite retry loops by implementing a token check with a reference to track the last auto-fetch token. - Removed the legacy file watcher for `TOOLS.md`, transitioning to a core RPC and socket event-based refresh mechanism for tool/config updates. - Updated test state structure to include `isAuthBootstrapComplete` and `channelConnections`, improving test coverage and state management. * Refactor channel module structure and add new providers - Consolidated channel modules under a new `providers` directory, improving organization and clarity. - Introduced new channel providers for DingTalk, Discord, Email, iMessage, IRC, and Lark, expanding the messaging capabilities of the application. - Updated the `mod.rs` file to reflect the new structure and ensure proper module exports, enhancing maintainability and ease of use. * refactor(ui): remove legacy providers/lib logic and stabilize auth bootstrap * chore: apply pre-push auto-fixes
362 lines
9.7 KiB
TypeScript
362 lines
9.7 KiB
TypeScript
import { useCallback, useEffect, useRef } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
|
|
import { socketService } from '../services/socketService';
|
|
import type { RootState } from '../store';
|
|
import {
|
|
addMessage,
|
|
setExecutionResult,
|
|
setTyping,
|
|
updateExecutionProgress,
|
|
} from '../store/intelligenceSlice';
|
|
import { createChatMessage } from '../utils/intelligenceTransforms';
|
|
|
|
/**
|
|
* WebSocket event payloads for Intelligence system
|
|
*/
|
|
interface ProcessMessagePayload {
|
|
message: string;
|
|
threadId: string;
|
|
sessionId?: string;
|
|
context?: Record<string, unknown>;
|
|
}
|
|
|
|
interface AgentResponsePayload {
|
|
message: string;
|
|
threadId: string;
|
|
sessionId?: string;
|
|
shouldExecute?: boolean;
|
|
executionPlan?: unknown;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
interface ExecutionProgressPayload {
|
|
executionId: string;
|
|
sessionId: string;
|
|
step: {
|
|
id: string;
|
|
label: string;
|
|
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
timestamp: string;
|
|
};
|
|
progress: Array<{
|
|
id: string;
|
|
label: string;
|
|
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
timestamp?: Date;
|
|
}>;
|
|
}
|
|
|
|
interface ExecutionCompletePayload {
|
|
executionId: string;
|
|
sessionId: string;
|
|
status: 'completed' | 'failed';
|
|
result?: unknown;
|
|
error?: string;
|
|
artifacts?: Array<{ type: string; url: string; title: string; description?: string }>;
|
|
}
|
|
|
|
interface ChatInitPayload {
|
|
tools: Record<string, unknown>[];
|
|
threadId: string;
|
|
sessionId?: string;
|
|
context?: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Hook for Intelligence WebSocket integration
|
|
* Handles real-time communication for chat and task execution
|
|
*/
|
|
export const useIntelligenceSocket = () => {
|
|
const dispatch = useDispatch();
|
|
const socket = socketService.getSocket();
|
|
const eventHandlersRegistered = useRef(false);
|
|
|
|
// Get current socket connection status
|
|
const isSocketConnected = useSelector((state: RootState) => {
|
|
const userId = state.user.user?._id || '__pending__';
|
|
return state.socket.byUser[userId]?.status === 'connected';
|
|
});
|
|
|
|
/**
|
|
* Send message to AI agent via WebSocket
|
|
*/
|
|
const sendMessage = useCallback(
|
|
async (payload: ProcessMessagePayload) => {
|
|
if (socket?.connected) {
|
|
socket.emit('processMessageForUser', payload);
|
|
} else {
|
|
console.warn('Cannot send message - socket not connected');
|
|
throw new Error('Socket not connected');
|
|
}
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
/**
|
|
* Initialize chat session with tools
|
|
*/
|
|
const sendChatInit = useCallback(
|
|
async (payload: ChatInitPayload) => {
|
|
if (socket?.connected) {
|
|
socket.emit('chat:init', payload);
|
|
} else {
|
|
console.warn('Cannot initialize chat - socket not connected');
|
|
throw new Error('Socket not connected');
|
|
}
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
/**
|
|
* Send typing indicator
|
|
*/
|
|
const sendTyping = useCallback(
|
|
(threadId: string, isTyping: boolean) => {
|
|
const payload = { threadId, isTyping };
|
|
|
|
if (socket?.connected) {
|
|
socket.emit('chat:typing', payload);
|
|
}
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
/**
|
|
* Register WebSocket event handlers
|
|
*/
|
|
const registerEventHandlers = useCallback(() => {
|
|
if (!socket || eventHandlersRegistered.current) return;
|
|
|
|
// Agent response handler
|
|
const handleAgentResponse = (data: AgentResponsePayload) => {
|
|
console.log('Intelligence: Received agent response', {
|
|
threadId: data.threadId,
|
|
hasMessage: !!data.message,
|
|
shouldExecute: data.shouldExecute,
|
|
});
|
|
|
|
if (data.message && data.threadId) {
|
|
const aiMessage = createChatMessage(data.message, 'ai');
|
|
dispatch(addMessage({ threadId: data.threadId, message: aiMessage }));
|
|
}
|
|
|
|
// Stop typing indicator
|
|
dispatch(setTyping({ threadId: data.threadId, isTyping: false }));
|
|
|
|
// Handle execution trigger
|
|
if (data.shouldExecute && data.executionPlan) {
|
|
console.log('Intelligence: Execution requested', {
|
|
threadId: data.threadId,
|
|
plan: data.executionPlan,
|
|
});
|
|
// Execution will be handled by the component
|
|
}
|
|
};
|
|
|
|
// Execution progress handler
|
|
const handleExecutionProgress = (data: ExecutionProgressPayload) => {
|
|
console.log('Intelligence: Execution progress', {
|
|
executionId: data.executionId,
|
|
step: data.step?.label,
|
|
status: data.step?.status,
|
|
});
|
|
|
|
if (data.progress) {
|
|
dispatch(
|
|
updateExecutionProgress({ executionId: data.executionId, progress: data.progress })
|
|
);
|
|
}
|
|
};
|
|
|
|
// Execution complete handler
|
|
const handleExecutionComplete = (data: ExecutionCompletePayload) => {
|
|
console.log('Intelligence: Execution complete', {
|
|
executionId: data.executionId,
|
|
status: data.status,
|
|
hasResult: !!data.result,
|
|
hasArtifacts: !!data.artifacts?.length,
|
|
});
|
|
|
|
dispatch(
|
|
setExecutionResult({
|
|
executionId: data.executionId,
|
|
result: data.result,
|
|
status: data.status,
|
|
error: data.error,
|
|
})
|
|
);
|
|
|
|
// Send completion message if we have artifacts
|
|
if (data.artifacts?.length && data.sessionId) {
|
|
// This would need the threadId - we'd need to track execution to thread mapping
|
|
// For now, we'll let the component handle the completion message
|
|
console.log('Intelligence: Task completed with artifacts', {
|
|
artifactCount: data.artifacts.length,
|
|
sessionId: data.sessionId,
|
|
});
|
|
}
|
|
};
|
|
|
|
// Typing indicator handler
|
|
const handleTyping = (data: { threadId: string; isTyping: boolean }) => {
|
|
dispatch(setTyping({ threadId: data.threadId, isTyping: data.isTyping }));
|
|
};
|
|
|
|
// Register all handlers
|
|
socket.on('agentResponse', handleAgentResponse);
|
|
socket.on('execution:step_progress', handleExecutionProgress);
|
|
socket.on('execution:complete', handleExecutionComplete);
|
|
socket.on('chat:typing', handleTyping);
|
|
|
|
eventHandlersRegistered.current = true;
|
|
|
|
// Return cleanup function
|
|
return () => {
|
|
socket.off('agentResponse', handleAgentResponse);
|
|
socket.off('execution:step_progress', handleExecutionProgress);
|
|
socket.off('execution:complete', handleExecutionComplete);
|
|
socket.off('chat:typing', handleTyping);
|
|
eventHandlersRegistered.current = false;
|
|
};
|
|
}, [socket, dispatch]);
|
|
|
|
/**
|
|
* Register event handlers when socket is available
|
|
*/
|
|
useEffect(() => {
|
|
if (socket && isSocketConnected) {
|
|
const cleanup = registerEventHandlers();
|
|
return cleanup;
|
|
}
|
|
}, [socket, isSocketConnected, registerEventHandlers]);
|
|
|
|
/**
|
|
* Cleanup on unmount
|
|
*/
|
|
useEffect(() => {
|
|
return () => {
|
|
eventHandlersRegistered.current = false;
|
|
};
|
|
}, []);
|
|
|
|
return {
|
|
// Connection status
|
|
isConnected: isSocketConnected,
|
|
|
|
// Message sending
|
|
sendMessage,
|
|
sendChatInit,
|
|
sendTyping,
|
|
|
|
// Utility functions
|
|
isReady: isSocketConnected && !!socket,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Hook for managing Intelligence WebSocket connection lifecycle
|
|
*/
|
|
export const useIntelligenceSocketManager = () => {
|
|
const token = useSelector((state: RootState) => state.auth.token);
|
|
const isConnected = useSelector((state: RootState) => {
|
|
const userId = state.user.user?._id || '__pending__';
|
|
return state.socket.byUser[userId]?.status === 'connected';
|
|
});
|
|
|
|
/**
|
|
* Initialize Intelligence socket connection
|
|
*/
|
|
const connect = useCallback(() => {
|
|
if (token && !isConnected) {
|
|
console.log('Intelligence: Initializing socket connection');
|
|
socketService.connect(token);
|
|
}
|
|
}, [token, isConnected]);
|
|
|
|
/**
|
|
* Disconnect Intelligence socket
|
|
*/
|
|
const disconnect = useCallback(() => {
|
|
console.log('Intelligence: Disconnecting socket');
|
|
socketService.disconnect();
|
|
}, []);
|
|
|
|
/**
|
|
* Auto-connect when token is available
|
|
*/
|
|
useEffect(() => {
|
|
if (token && !isConnected) {
|
|
connect();
|
|
}
|
|
}, [token, isConnected, connect]);
|
|
|
|
return { connect, disconnect, isConnected, isReady: isConnected && !!token };
|
|
};
|
|
|
|
/**
|
|
* Hook for Intelligence-specific event subscriptions
|
|
*/
|
|
export const useIntelligenceEvents = () => {
|
|
const socket = socketService.getSocket();
|
|
|
|
/**
|
|
* Subscribe to agent responses for a specific thread
|
|
*/
|
|
const onAgentResponse = useCallback(
|
|
(threadId: string, callback: (data: AgentResponsePayload) => void) => {
|
|
if (!socket) return () => {};
|
|
|
|
const handler = (data: AgentResponsePayload) => {
|
|
if (data.threadId === threadId) {
|
|
callback(data);
|
|
}
|
|
};
|
|
|
|
socket.on('agentResponse', handler);
|
|
return () => socket.off('agentResponse', handler);
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
/**
|
|
* Subscribe to execution progress for a specific execution
|
|
*/
|
|
const onExecutionProgress = useCallback(
|
|
(executionId: string, callback: (data: ExecutionProgressPayload) => void) => {
|
|
if (!socket) return () => {};
|
|
|
|
const handler = (data: ExecutionProgressPayload) => {
|
|
if (data.executionId === executionId) {
|
|
callback(data);
|
|
}
|
|
};
|
|
|
|
socket.on('execution:step_progress', handler);
|
|
return () => socket.off('execution:step_progress', handler);
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
/**
|
|
* Subscribe to execution completion for a specific execution
|
|
*/
|
|
const onExecutionComplete = useCallback(
|
|
(executionId: string, callback: (data: ExecutionCompletePayload) => void) => {
|
|
if (!socket) return () => {};
|
|
|
|
const handler = (data: ExecutionCompletePayload) => {
|
|
if (data.executionId === executionId) {
|
|
callback(data);
|
|
}
|
|
};
|
|
|
|
socket.on('execution:complete', handler);
|
|
return () => socket.off('execution:complete', handler);
|
|
},
|
|
[socket]
|
|
);
|
|
|
|
return { onAgentResponse, onExecutionProgress, onExecutionComplete };
|
|
};
|