diff --git a/packages/app/src/app/index.tsx b/packages/app/src/app/index.tsx index dd643fcfd..35bc25d39 100644 --- a/packages/app/src/app/index.tsx +++ b/packages/app/src/app/index.tsx @@ -1,4 +1,4 @@ -import { View, ActivityIndicator, Text } from "react-native"; +import { View, ActivityIndicator } from "react-native"; import { useState, useCallback, useMemo, useRef, useEffect } from "react"; import { useFocusEffect } from "@react-navigation/native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -19,7 +19,8 @@ export default function HomeScreen() { const { agents: aggregatedAgents, isInitialLoad, - isRevalidating + isRevalidating, + refreshAll, } = useAggregatedAgents(); const [showCreateModal, setShowCreateModal] = useState(false); const [showImportModal, setShowImportModal] = useState(false); @@ -123,16 +124,11 @@ export default function HomeScreen() { ) : hasAgents ? ( - - - - {isRevalidating && ( - - - Reconnecting... - - )} - + ) : ( ({ alignItems: "center", justifyContent: "center", }, - revalidatingBadge: { - position: "absolute", - top: 8, - right: 8, - flexDirection: "row", - alignItems: "center", - gap: 8, - paddingHorizontal: 12, - paddingVertical: 6, - backgroundColor: theme.colors.muted, - borderRadius: 16, - opacity: 0.9, - }, - revalidatingText: { - fontSize: 12, - color: theme.colors.mutedForeground, - }, })); diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx index 580464d06..03b5ee47b 100644 --- a/packages/app/src/components/agent-list.tsx +++ b/packages/app/src/components/agent-list.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable, FlatList, Modal, type ListRenderItem } from "react-native"; +import { View, Text, Pressable, FlatList, Modal, RefreshControl, type ListRenderItem } from "react-native"; import { useCallback, useState } from "react"; import { router } from "expo-router"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; @@ -11,9 +11,11 @@ import { buildAgentNavigationKey, startNavigationTiming } from "@/utils/navigati interface AgentListProps { agents: AggregatedAgent[]; + isRefreshing?: boolean; + onRefresh?: () => void; } -export function AgentList({ agents }: AgentListProps) { +export function AgentList({ agents, isRefreshing = false, onRefresh }: AgentListProps) { const { theme } = useUnistyles(); const [actionAgent, setActionAgent] = useState(null); const actionSession = useDaemonSession(actionAgent?.serverId, { @@ -150,6 +152,16 @@ export function AgentList({ agents }: AgentListProps) { renderItem={renderAgentItem} showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="handled" + refreshControl={ + onRefresh ? ( + + ) : undefined + } /> void; initializeAgent: (params: { agentId: string; requestId?: string }) => void; refreshAgent: (params: { agentId: string; requestId?: string }) => void; + refreshSession: () => void; cancelAgentRun: (agentId: string) => void; sendAgentMessage: ( agentId: string, @@ -1589,6 +1590,17 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid return targetPath; }, [updateExplorerState, ws]); + const refreshSession = useCallback(() => { + console.log(`[Session] Manual refresh requested for ${serverId}`); + ws.send({ + type: "session", + message: { + type: "load_conversation_request", + conversationId: ws.conversationId ?? "", + }, + }); + }, [ws, serverId]); + // Cleanup on unmount useEffect(() => { return () => { @@ -1610,6 +1622,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid restartServer, initializeAgent, refreshAgent, + refreshSession, cancelAgentRun, deleteAgent, sendAgentMessage, @@ -1632,6 +1645,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid restartServer, initializeAgent, refreshAgent, + refreshSession, cancelAgentRun, deleteAgent, sendAgentMessage, @@ -1656,6 +1670,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid restartServer, initializeAgent, refreshAgent, + refreshSession, cancelAgentRun, sendAgentMessage, sendAgentAudio, @@ -1674,6 +1689,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid restartServer, initializeAgent, refreshAgent, + refreshSession, cancelAgentRun, sendAgentMessage, sendAgentAudio, diff --git a/packages/app/src/hooks/use-aggregated-agents.ts b/packages/app/src/hooks/use-aggregated-agents.ts index 8ccbd2c5b..be5cf791a 100644 --- a/packages/app/src/hooks/use-aggregated-agents.ts +++ b/packages/app/src/hooks/use-aggregated-agents.ts @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, useCallback } from "react"; import { useDaemonConnections } from "@/contexts/daemon-connections-context"; import { useSessionStore } from "@/stores/session-store"; import type { AgentDirectoryEntry } from "@/types/agent-directory"; @@ -13,13 +13,24 @@ export interface AggregatedAgentsResult { isLoading: boolean; isInitialLoad: boolean; isRevalidating: boolean; + refreshAll: () => void; } export function useAggregatedAgents(): AggregatedAgentsResult { const { connectionStates } = useDaemonConnections(); const sessions = useSessionStore((state) => state.sessions); - return useMemo(() => { + const refreshAll = useCallback(() => { + console.log('[useAggregatedAgents] Manual refresh triggered for all sessions'); + for (const [serverId, session] of Object.entries(sessions)) { + if (session?.methods?.refreshSession) { + console.log(`[useAggregatedAgents] Refreshing session ${serverId}`); + session.methods.refreshSession(); + } + } + }, [sessions]); + + const result = useMemo(() => { const allAgents: AggregatedAgent[] = []; // Derive agent directory from all sessions @@ -124,4 +135,9 @@ export function useAggregatedAgents(): AggregatedAgentsResult { isRevalidating, }; }, [sessions, connectionStates]); + + return { + ...result, + refreshAll, + }; } diff --git a/packages/app/src/hooks/use-daemon-session.ts b/packages/app/src/hooks/use-daemon-session.ts index f0d852354..fe9c1d2b5 100644 --- a/packages/app/src/hooks/use-daemon-session.ts +++ b/packages/app/src/hooks/use-daemon-session.ts @@ -30,6 +30,7 @@ export type DaemonSession = SessionState & { restartServer: (reason?: string) => void; initializeAgent: (params: { agentId: string; requestId?: string }) => void; refreshAgent: (params: { agentId: string; requestId?: string }) => void; + refreshSession: () => void; cancelAgentRun: (agentId: string) => void; sendAgentMessage: ( agentId: string, diff --git a/packages/app/src/stores/session-store.ts b/packages/app/src/stores/session-store.ts index 48f75af5c..08707f7c5 100644 --- a/packages/app/src/stores/session-store.ts +++ b/packages/app/src/stores/session-store.ts @@ -169,6 +169,7 @@ export interface SessionState { restartServer: (reason?: string) => void; initializeAgent: (params: { agentId: string; requestId?: string }) => void; refreshAgent: (params: { agentId: string; requestId?: string }) => void; + refreshSession: () => void; cancelAgentRun: (agentId: string) => void; sendAgentMessage: ( agentId: string,