Unify app connection state around host runtime
This commit is contained in:
@@ -18,6 +18,10 @@ import { shortenPath } from "@/utils/shorten-path";
|
||||
import { deriveBranchLabel, deriveProjectPath } from "@/utils/agent-display-info";
|
||||
import { type AggregatedAgent } from "@/hooks/use-aggregated-agents";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import {
|
||||
getHostRuntimeStore,
|
||||
isHostRuntimeConnected,
|
||||
} from "@/runtime/host-runtime";
|
||||
import { AgentStatusDot } from "@/components/agent-status-dot";
|
||||
import {
|
||||
CHECKOUT_STATUS_STALE_TIME,
|
||||
@@ -154,9 +158,9 @@ export function AgentList({
|
||||
continue;
|
||||
}
|
||||
|
||||
const session = useSessionStore.getState().sessions[agent.serverId];
|
||||
const client = session?.client ?? null;
|
||||
const isConnected = session?.connection.isConnected ?? false;
|
||||
const runtime = getHostRuntimeStore();
|
||||
const client = runtime.getClient(agent.serverId);
|
||||
const isConnected = isHostRuntimeConnected(runtime.getSnapshot(agent.serverId));
|
||||
if (!client || !isConnected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ function ManagedDaemonSession({ daemon }: { daemon: HostProfile }) {
|
||||
);
|
||||
}, [daemon.serverId, hasConnectionRecord, snapshot, updateConnectionStatus]);
|
||||
|
||||
if (!snapshot?.activeConnection || !client) {
|
||||
if (!client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ import {
|
||||
DraggableList,
|
||||
type DraggableRenderItemInfo,
|
||||
} from "./draggable-list";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import {
|
||||
getHostRuntimeStore,
|
||||
isHostRuntimeConnected,
|
||||
} from "@/runtime/host-runtime";
|
||||
import {
|
||||
buildAgentNavigationKey,
|
||||
startNavigationTiming,
|
||||
@@ -490,7 +493,6 @@ export function SidebarAgentList({
|
||||
}, [projectOptions, selectedProjectKeys]);
|
||||
const showProjectFilters = projectOptions.length > 0;
|
||||
|
||||
const sessions = useSessionStore((state) => state.sessions);
|
||||
const projectIconRequests = useMemo(() => {
|
||||
const unique = new Map<string, { serverId: string; cwd: string }>();
|
||||
for (const option of projectOptions) {
|
||||
@@ -517,8 +519,7 @@ export function SidebarAgentList({
|
||||
queries: projectIconRequests.map((request) => ({
|
||||
queryKey: projectIconQueryKey(request.serverId, request.cwd),
|
||||
queryFn: async () => {
|
||||
const client =
|
||||
useSessionStore.getState().sessions[request.serverId]?.client ?? null;
|
||||
const client = getHostRuntimeStore().getClient(request.serverId);
|
||||
if (!client) {
|
||||
return null;
|
||||
}
|
||||
@@ -526,8 +527,10 @@ export function SidebarAgentList({
|
||||
return result.icon;
|
||||
},
|
||||
enabled: Boolean(
|
||||
sessions[request.serverId]?.client &&
|
||||
sessions[request.serverId]?.connection.isConnected &&
|
||||
getHostRuntimeStore().getClient(request.serverId) &&
|
||||
isHostRuntimeConnected(
|
||||
getHostRuntimeStore().getSnapshot(request.serverId)
|
||||
) &&
|
||||
request.cwd
|
||||
),
|
||||
staleTime: Infinity,
|
||||
|
||||
@@ -17,7 +17,7 @@ import Svg, {
|
||||
} from "react-native-svg";
|
||||
import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles";
|
||||
import type { ListTerminalsResponse } from "@server/shared/messages";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import {
|
||||
hasPendingTerminalModifiers,
|
||||
normalizeTerminalTransportKey,
|
||||
@@ -135,10 +135,7 @@ export function TerminalPane({ serverId, cwd }: TerminalPaneProps) {
|
||||
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const client = useSessionStore((state) => state.sessions[serverId]?.client ?? null);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
|
||||
const scopeKey = useMemo(() => terminalScopeKey({ serverId, cwd }), [serverId, cwd]);
|
||||
const terminalsQueryKey = useMemo(() => ["terminals", serverId, cwd] as const, [cwd, serverId]);
|
||||
|
||||
@@ -18,7 +18,7 @@ export type ConnectionStatus = ConnectionState["status"];
|
||||
export type ConnectionStateUpdate =
|
||||
| { status: "idle" }
|
||||
| { status: "connecting"; activeConnection?: ActiveConnection | null; lastOnlineAt?: string | null }
|
||||
| { status: "online"; activeConnection?: ActiveConnection | null; lastOnlineAt: string; agentListReady?: boolean }
|
||||
| { status: "online"; activeConnection?: ActiveConnection | null; lastOnlineAt: string }
|
||||
| { status: "offline"; activeConnection?: ActiveConnection | null; lastError?: string | null; lastOnlineAt?: string | null }
|
||||
| { status: "error"; activeConnection?: ActiveConnection | null; lastError: string; lastOnlineAt?: string | null };
|
||||
|
||||
@@ -30,6 +30,7 @@ interface DaemonConnectionsContextValue {
|
||||
connectionStates: Map<string, DaemonConnectionRecord>;
|
||||
isLoading: boolean;
|
||||
updateConnectionStatus: (serverId: string, update: ConnectionStateUpdate) => void;
|
||||
markAgentListReady: (serverId: string, ready: boolean) => void;
|
||||
}
|
||||
|
||||
const DaemonConnectionsContext = createContext<DaemonConnectionsContextValue | null>(null);
|
||||
@@ -69,7 +70,8 @@ function resolveNextConnectionState(
|
||||
hasEverReceivedAgentList: existing.hasEverReceivedAgentList ?? false,
|
||||
};
|
||||
case "online":
|
||||
const currentAgentListReady = update.agentListReady ?? (existing.status === "online" ? existing.agentListReady : false);
|
||||
const currentAgentListReady =
|
||||
existing.status === "online" ? existing.agentListReady : false;
|
||||
|
||||
return {
|
||||
status: "online",
|
||||
@@ -173,10 +175,37 @@ export function DaemonConnectionsProvider({ children }: { children: ReactNode })
|
||||
[]
|
||||
);
|
||||
|
||||
const markAgentListReady = useCallback((serverId: string, ready: boolean) => {
|
||||
setConnectionStates((prev) => {
|
||||
const existing = prev.get(serverId);
|
||||
if (!existing) {
|
||||
return prev;
|
||||
}
|
||||
if (existing.status !== "online") {
|
||||
return prev;
|
||||
}
|
||||
if (
|
||||
existing.agentListReady === ready &&
|
||||
(ready ? existing.hasEverReceivedAgentList : true)
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
const next = new Map(prev);
|
||||
next.set(serverId, {
|
||||
...existing,
|
||||
agentListReady: ready,
|
||||
hasEverReceivedAgentList:
|
||||
ready || existing.hasEverReceivedAgentList,
|
||||
});
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const value: DaemonConnectionsContextValue = {
|
||||
connectionStates,
|
||||
isLoading: registryLoading,
|
||||
updateConnectionStatus,
|
||||
markAgentListReady,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
useSessionStore,
|
||||
type Agent,
|
||||
type SessionState,
|
||||
type DaemonConnectionSnapshot,
|
||||
} from "@/stores/session-store";
|
||||
import { useDraftStore } from "@/stores/draft-store";
|
||||
import type { AgentDirectoryEntry } from "@/types/agent-directory";
|
||||
@@ -74,6 +73,12 @@ const derivePendingPermissionKey = (
|
||||
|
||||
const HISTORY_STALE_AFTER_MS = 60_000;
|
||||
|
||||
type SessionConnectionSnapshot = {
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
lastError: string | null;
|
||||
};
|
||||
|
||||
const findLatestAssistantMessageText = (items: StreamItem[]): string | null => {
|
||||
for (let i = items.length - 1; i >= 0; i -= 1) {
|
||||
const item = items[i];
|
||||
@@ -87,7 +92,7 @@ const findLatestAssistantMessageText = (items: StreamItem[]): string | null => {
|
||||
const mapConnectionState = (
|
||||
state: ConnectionState,
|
||||
lastError: string | null
|
||||
): DaemonConnectionSnapshot => ({
|
||||
): SessionConnectionSnapshot => ({
|
||||
isConnected: state.status === "connected",
|
||||
isConnecting: state.status === "connecting",
|
||||
lastError: state.status === "disconnected" ? state.reason ?? lastError : null,
|
||||
@@ -264,10 +269,10 @@ function SessionProviderInternal({
|
||||
}: SessionProviderClientProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const [connectionSnapshot, setConnectionSnapshot] =
|
||||
useState<DaemonConnectionSnapshot>(() =>
|
||||
useState<SessionConnectionSnapshot>(() =>
|
||||
mapConnectionState(client.getConnectionState(), client.lastError)
|
||||
);
|
||||
const { updateConnectionStatus } = useDaemonConnections();
|
||||
const { markAgentListReady } = useDaemonConnections();
|
||||
|
||||
// Zustand store actions
|
||||
const initializeSession = useSessionStore((state) => state.initializeSession);
|
||||
@@ -312,9 +317,6 @@ function SessionProviderInternal({
|
||||
const clearDraftInput = useDraftStore((state) => state.clearDraftInput);
|
||||
const setQueuedMessages = useSessionStore((state) => state.setQueuedMessages);
|
||||
const updateSessionClient = useSessionStore((state) => state.updateSessionClient);
|
||||
const updateSessionConnection = useSessionStore(
|
||||
(state) => state.updateSessionConnection
|
||||
);
|
||||
const updateSessionServerInfo = useSessionStore((state) => state.updateSessionServerInfo);
|
||||
|
||||
// Track focused agent for heartbeat
|
||||
@@ -470,10 +472,6 @@ function SessionProviderInternal({
|
||||
wasConnectedRef.current = connectionSnapshot.isConnected;
|
||||
}, [serverId, connectionSnapshot.isConnected, bumpHistorySyncGeneration]);
|
||||
|
||||
useEffect(() => {
|
||||
updateSessionConnection(serverId, connectionSnapshot);
|
||||
}, [serverId, connectionSnapshot, updateSessionConnection]);
|
||||
|
||||
// Initialize session in store
|
||||
useEffect(() => {
|
||||
initializeSession(serverId, client, audioPlayer);
|
||||
@@ -855,11 +853,7 @@ function SessionProviderInternal({
|
||||
setPendingPermissions(serverId, nextPendingPermissions);
|
||||
setInitializingAgents(serverId, new Map());
|
||||
setHasHydratedAgents(serverId, true);
|
||||
updateConnectionStatus(serverId, {
|
||||
status: "online",
|
||||
lastOnlineAt: new Date().toISOString(),
|
||||
agentListReady: true,
|
||||
});
|
||||
markAgentListReady(serverId, true);
|
||||
} catch (err) {
|
||||
if (cancelled) {
|
||||
return;
|
||||
@@ -885,7 +879,7 @@ function SessionProviderInternal({
|
||||
setHasHydratedAgents,
|
||||
setInitializingAgents,
|
||||
setPendingPermissions,
|
||||
updateConnectionStatus,
|
||||
markAgentListReady,
|
||||
]);
|
||||
|
||||
// Daemon message handlers - directly update Zustand store
|
||||
@@ -1420,7 +1414,6 @@ function SessionProviderInternal({
|
||||
setPendingPermissions,
|
||||
setFileExplorer,
|
||||
setHasHydratedAgents,
|
||||
updateConnectionStatus,
|
||||
clearDraftInput,
|
||||
notifyAgentAttention,
|
||||
applyAgentUpdatePayload,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createContext, useContext, useState, ReactNode, useCallback, useEffect,
|
||||
import { useSpeechmaticsAudio } from "@/hooks/use-speechmatics-audio";
|
||||
import type { SessionState } from "@/stores/session-store";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import { resolveVoiceUnavailableMessage } from "@/utils/server-info-capabilities";
|
||||
import { activateKeepAwakeAsync, deactivateKeepAwake } from "expo-keep-awake";
|
||||
import { REALTIME_VOICE_VAD_CONFIG } from "@/voice/realtime-voice-config";
|
||||
@@ -45,6 +46,9 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
|
||||
const getSession = useSessionStore((state) => state.getSession);
|
||||
const [activeServerId, setActiveServerId] = useState<string | null>(null);
|
||||
const [activeAgentId, setActiveAgentId] = useState<string | null>(null);
|
||||
const { client: runtimeClient, isConnected: activeRuntimeConnected } = useHostRuntimeSession(
|
||||
activeServerId ?? ""
|
||||
);
|
||||
const activeSession = useSessionStore(
|
||||
useCallback(
|
||||
(state: ReturnType<typeof useSessionStore.getState>) => {
|
||||
@@ -274,8 +278,8 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
|
||||
}, [isVoiceMode, realtimeAudio.isDetecting, realtimeAudio.isSpeaking]);
|
||||
|
||||
useEffect(() => {
|
||||
const connected = activeSession?.connection.isConnected ?? false;
|
||||
const client = activeSession?.client ?? null;
|
||||
const connected = activeRuntimeConnected;
|
||||
const client = runtimeClient;
|
||||
if (!connected) {
|
||||
voiceTransportReadyRef.current = false;
|
||||
}
|
||||
@@ -313,7 +317,7 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
|
||||
}
|
||||
|
||||
wasVoiceSocketConnectedRef.current = connected;
|
||||
}, [activeAgentId, activeServerId, activeSession?.client, activeSession?.connection.isConnected, isVoiceMode]);
|
||||
}, [activeAgentId, activeRuntimeConnected, activeServerId, isVoiceMode, runtimeClient]);
|
||||
|
||||
const isPlayingAudio = activeSession?.isPlayingAudio ?? false;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useAgentCommandsQuery, type DraftCommandConfig } from './use-agent-comm
|
||||
import { orderAutocompleteOptions } from '@/components/ui/autocomplete-utils'
|
||||
import { useAutocomplete } from './use-autocomplete'
|
||||
import { useSessionStore } from '@/stores/session-store'
|
||||
import { useHostRuntimeSession } from '@/runtime/host-runtime'
|
||||
import {
|
||||
applyFileMentionReplacement,
|
||||
findActiveFileMention,
|
||||
@@ -137,10 +138,7 @@ export function useAgentAutocomplete(input: UseAgentAutocompleteInput): AgentAut
|
||||
return queryDraftConfig?.cwd ?? ''
|
||||
}, [agentCwd, isRealAgent, queryDraftConfig])
|
||||
|
||||
const client = useSessionStore((state) => state.sessions[serverId]?.client ?? null)
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
)
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId)
|
||||
|
||||
const mode: 'command' | 'file' | null = showFileAutocomplete
|
||||
? 'file'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { AgentProvider } from "@server/server/agent/agent-sdk-types";
|
||||
|
||||
const COMMANDS_STALE_TIME = 60_000; // Commands rarely change, cache for 1 minute
|
||||
@@ -48,12 +48,7 @@ export function useAgentCommandsQuery({
|
||||
enabled = true,
|
||||
draftConfig,
|
||||
}: UseAgentCommandsQueryOptions) {
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: commandsQueryKey(serverId, agentId, draftConfig),
|
||||
|
||||
@@ -9,8 +9,8 @@ import type {
|
||||
AgentModelDefinition,
|
||||
AgentProvider,
|
||||
} from "@server/server/agent/agent-sdk-types";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useDaemonRegistry } from "@/contexts/daemon-registry-context";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import { useFormPreferences, type FormPreferences } from "./use-form-preferences";
|
||||
|
||||
// Explicit overrides from URL params or "New Agent" button
|
||||
@@ -364,11 +364,7 @@ export function useAgentFormState(
|
||||
}, [isVisible]);
|
||||
|
||||
// Session state for provider model listing
|
||||
const sessionState = useSessionStore((state) =>
|
||||
formState.serverId ? state.sessions[formState.serverId] : undefined
|
||||
);
|
||||
const client = sessionState?.client ?? null;
|
||||
const isConnected = sessionState?.connection?.isConnected ?? false;
|
||||
const { client, isConnected } = useHostRuntimeSession(formState.serverId ?? "");
|
||||
|
||||
const availableProvidersQuery = useQuery({
|
||||
queryKey: ["availableProviders", formState.serverId],
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useCallback } from "react";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import type { FetchAgentTimelineOptions } from "@server/client/daemon-client";
|
||||
import type {
|
||||
DaemonClient,
|
||||
FetchAgentTimelineOptions,
|
||||
} from "@server/client/daemon-client";
|
||||
import {
|
||||
attachInitTimeout,
|
||||
createInitDeferred,
|
||||
@@ -37,8 +40,13 @@ function buildInitialTimelineRequest(
|
||||
};
|
||||
}
|
||||
|
||||
export function useAgentInitialization(serverId: string) {
|
||||
const client = useSessionStore((state) => state.sessions[serverId]?.client ?? null);
|
||||
export function useAgentInitialization({
|
||||
serverId,
|
||||
client,
|
||||
}: {
|
||||
serverId: string;
|
||||
client: DaemonClient | null;
|
||||
}) {
|
||||
const setInitializingAgents = useSessionStore((state) => state.setInitializingAgents);
|
||||
const setAgentInitializing = useCallback(
|
||||
(agentId: string, initializing: boolean) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useMemo } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { AggregatedAgent, AggregatedAgentsResult } from "@/hooks/use-aggregated-agents";
|
||||
import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
|
||||
|
||||
@@ -45,8 +46,7 @@ export function useAllAgentsList(options?: {
|
||||
const session = useSessionStore((state) =>
|
||||
serverId ? state.sessions[serverId] : undefined
|
||||
);
|
||||
const client = session?.client ?? null;
|
||||
const isConnected = session?.connection.isConnected ?? false;
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId ?? "");
|
||||
const liveAgents = session?.agents ?? null;
|
||||
const canFetch = Boolean(serverId && client && isConnected);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useCallback, useEffect, useId, useMemo } from "react";
|
||||
import { UnistylesRuntime } from "react-native-unistyles";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { SubscribeCheckoutDiffResponse } from "@server/shared/messages";
|
||||
import { orderCheckoutDiffFiles } from "./checkout-diff-order";
|
||||
|
||||
@@ -56,12 +56,7 @@ export function useCheckoutDiffQuery({
|
||||
enabled = true,
|
||||
}: UseCheckoutDiffQueryOptions) {
|
||||
const queryClient = useQueryClient();
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
const isMobile =
|
||||
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||
const mobileView = usePanelStore((state) => state.mobileView);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { CheckoutPrStatusResponse } from "@server/shared/messages";
|
||||
|
||||
const CHECKOUT_PR_STATUS_STALE_TIME = 20_000;
|
||||
@@ -21,12 +21,7 @@ export function useCheckoutPrStatusQuery({
|
||||
cwd,
|
||||
enabled = true,
|
||||
}: UseCheckoutPrStatusQueryOptions) {
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: checkoutPrStatusQueryKey(serverId, cwd),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { UnistylesRuntime } from "react-native-unistyles";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { CheckoutStatusResponse } from "@server/shared/messages";
|
||||
import {
|
||||
checkoutStatusRevalidationKey,
|
||||
@@ -30,12 +30,7 @@ function fetchCheckoutStatus(
|
||||
}
|
||||
|
||||
export function useCheckoutStatusQuery({ serverId, cwd }: UseCheckoutStatusQueryOptions) {
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
const isMobile =
|
||||
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||
const mobileView = usePanelStore((state) => state.mobileView);
|
||||
@@ -92,9 +87,7 @@ export function useCheckoutStatusQuery({ serverId, cwd }: UseCheckoutStatusQuery
|
||||
* only the visible agents.
|
||||
*/
|
||||
export function useCheckoutStatusCacheOnly({ serverId, cwd }: UseCheckoutStatusQueryOptions) {
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const { client } = useHostRuntimeSession(serverId);
|
||||
|
||||
return useQuery({
|
||||
queryKey: checkoutStatusQueryKey(serverId, cwd),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { ProjectIcon } from "@server/shared/messages";
|
||||
|
||||
export function projectIconQueryKey(serverId: string, cwd: string) {
|
||||
@@ -12,12 +12,7 @@ interface UseProjectIconQueryOptions {
|
||||
}
|
||||
|
||||
export function useProjectIconQuery({ serverId, cwd }: UseProjectIconQueryOptions) {
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
);
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: projectIconQueryKey(serverId, cwd),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useMemo } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
|
||||
import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
|
||||
import {
|
||||
@@ -170,9 +171,8 @@ export function useSidebarAgentsGrouped(options?: {
|
||||
const session = useSessionStore((state) =>
|
||||
serverId ? state.sessions[serverId] : undefined
|
||||
);
|
||||
const client = session?.client ?? null;
|
||||
const { client, isConnected } = useHostRuntimeSession(serverId ?? "");
|
||||
const liveAgents = session?.agents ?? null;
|
||||
const isConnected = session?.connection.isConnected ?? false;
|
||||
const canFetch = Boolean(serverId && client && isConnected);
|
||||
|
||||
const agentsQuery = useQuery({
|
||||
|
||||
@@ -32,6 +32,12 @@ export type HostRuntimeSnapshot = {
|
||||
clientGeneration: number;
|
||||
};
|
||||
|
||||
export function isHostRuntimeConnected(
|
||||
snapshot: HostRuntimeSnapshot | null
|
||||
): boolean {
|
||||
return snapshot?.connectionStatus === "online";
|
||||
}
|
||||
|
||||
export type HostRuntimeControllerDeps = {
|
||||
createClient: (input: {
|
||||
host: HostProfile;
|
||||
@@ -564,6 +570,7 @@ export function useHostRuntimeSnapshot(
|
||||
export function useHostRuntimeSession(serverId: string): {
|
||||
snapshot: HostRuntimeSnapshot | null;
|
||||
client: DaemonClient | null;
|
||||
isConnected: boolean;
|
||||
} {
|
||||
const store = getHostRuntimeStore();
|
||||
const snapshot = useHostRuntimeSnapshot(serverId);
|
||||
@@ -571,6 +578,7 @@ export function useHostRuntimeSession(serverId: string): {
|
||||
() => ({
|
||||
snapshot,
|
||||
client: store.getClient(serverId),
|
||||
isConnected: isHostRuntimeConnected(snapshot),
|
||||
}),
|
||||
[serverId, snapshot, store]
|
||||
);
|
||||
|
||||
@@ -41,8 +41,11 @@ import {
|
||||
} from "@/contexts/explorer-sidebar-animation-context";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import type { ConnectionStatus } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import {
|
||||
useHostRuntimeSession,
|
||||
type HostRuntimeConnectionStatus,
|
||||
} from "@/runtime/host-runtime";
|
||||
import { useCreateFlowStore } from "@/stores/create-flow-store";
|
||||
import type { Agent } from "@/contexts/session-context";
|
||||
import type { StreamItem } from "@/types/stream";
|
||||
@@ -75,7 +78,7 @@ import {
|
||||
} from "@/utils/agent-snapshots";
|
||||
import { mergePendingCreateImages } from "@/utils/pending-create-images";
|
||||
import { shouldClearAgentAttentionOnView } from "@/utils/agent-attention";
|
||||
import type { FetchAgentsEntry } from "@server/client/daemon-client";
|
||||
import type { DaemonClient, FetchAgentsEntry } from "@server/client/daemon-client";
|
||||
import { useExplorerOpenGesture } from "@/hooks/use-explorer-open-gesture";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -109,11 +112,12 @@ export function AgentReadyScreen({
|
||||
const resolvedAgentId = agentId?.trim() || undefined;
|
||||
const resolvedServerId = serverId?.trim() || undefined;
|
||||
const { connectionStates } = useDaemonConnections();
|
||||
|
||||
// Check if session exists for this serverId
|
||||
const hasSession = useSessionStore((state) =>
|
||||
resolvedServerId ? !!state.sessions[resolvedServerId] : false
|
||||
);
|
||||
const runtimeServerId = resolvedServerId ?? "";
|
||||
const {
|
||||
snapshot: runtimeSnapshot,
|
||||
client: runtimeClient,
|
||||
isConnected: runtimeIsConnected,
|
||||
} = useHostRuntimeSession(runtimeServerId);
|
||||
|
||||
const connectionServerId = resolvedServerId ?? null;
|
||||
const connection = connectionServerId
|
||||
@@ -122,9 +126,11 @@ export function AgentReadyScreen({
|
||||
const serverLabel =
|
||||
connection?.daemon.label ?? connectionServerId ?? "Selected host";
|
||||
const isUnknownDaemon = Boolean(connectionServerId && !connection);
|
||||
const connectionStatus =
|
||||
connection?.status ?? (isUnknownDaemon ? "offline" : "idle");
|
||||
const lastConnectionError = connection?.lastError ?? null;
|
||||
const connectionStatus: HostRuntimeConnectionStatus =
|
||||
runtimeSnapshot?.connectionStatus ??
|
||||
(isUnknownDaemon ? "offline" : "idle");
|
||||
const lastConnectionError = runtimeSnapshot?.lastError ?? null;
|
||||
const isRuntimeSessionAvailable = Boolean(resolvedServerId && runtimeClient);
|
||||
|
||||
const handleBackToHome = useCallback(() => {
|
||||
const targetServerId = resolvedServerId;
|
||||
@@ -154,7 +160,9 @@ export function AgentReadyScreen({
|
||||
}, [resolvedAgentId, resolvedServerId, router]);
|
||||
|
||||
const focusServerId = resolvedServerId;
|
||||
const navigationStatus = hasSession ? "ready" : "session_unavailable";
|
||||
const navigationStatus = isRuntimeSessionAvailable
|
||||
? "ready"
|
||||
: "session_unavailable";
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -172,7 +180,7 @@ export function AgentReadyScreen({
|
||||
}, [focusServerId, navigationStatus, resolvedAgentId])
|
||||
);
|
||||
|
||||
if (!hasSession || !resolvedServerId) {
|
||||
if (!resolvedServerId || !runtimeClient) {
|
||||
return (
|
||||
<AgentSessionUnavailableState
|
||||
onBack={handleBackToHome}
|
||||
@@ -189,6 +197,8 @@ export function AgentReadyScreen({
|
||||
<AgentScreenContent
|
||||
serverId={resolvedServerId}
|
||||
agentId={resolvedAgentId}
|
||||
client={runtimeClient}
|
||||
isConnected={runtimeIsConnected}
|
||||
connectionStatus={connectionStatus}
|
||||
/>
|
||||
</ExplorerSidebarAnimationProvider>
|
||||
@@ -198,7 +208,9 @@ export function AgentReadyScreen({
|
||||
type AgentScreenContentProps = {
|
||||
serverId: string;
|
||||
agentId?: string;
|
||||
connectionStatus: ConnectionStatus;
|
||||
client: DaemonClient;
|
||||
isConnected: boolean;
|
||||
connectionStatus: HostRuntimeConnectionStatus;
|
||||
};
|
||||
|
||||
function toErrorMessage(error: unknown): string {
|
||||
@@ -215,6 +227,8 @@ function isNotFoundErrorMessage(message: string): boolean {
|
||||
function AgentScreenContent({
|
||||
serverId,
|
||||
agentId,
|
||||
client,
|
||||
isConnected,
|
||||
connectionStatus,
|
||||
}: AgentScreenContentProps) {
|
||||
const { theme } = useUnistyles();
|
||||
@@ -465,16 +479,16 @@ function AgentScreenContent({
|
||||
return filtered;
|
||||
}, [allPendingPermissions, resolvedAgentId]);
|
||||
|
||||
const client = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.client ?? null
|
||||
);
|
||||
const isConnected = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.connection.isConnected ?? false
|
||||
const hasSession = useSessionStore(
|
||||
(state) => Boolean(state.sessions[serverId])
|
||||
);
|
||||
const focusedAgentId = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.focusedAgentId ?? null
|
||||
);
|
||||
const { ensureAgentIsInitialized, refreshAgent } = useAgentInitialization(serverId);
|
||||
const { ensureAgentIsInitialized, refreshAgent } = useAgentInitialization({
|
||||
serverId,
|
||||
client: hasSession ? client : null,
|
||||
});
|
||||
const [missingAgentState, setMissingAgentState] = useState<AgentScreenMissingState>({
|
||||
kind: "idle",
|
||||
});
|
||||
@@ -529,7 +543,7 @@ function AgentScreenContent({
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (!resolvedAgentId || !isConnected) {
|
||||
if (!resolvedAgentId || !isConnected || !hasSession) {
|
||||
return;
|
||||
}
|
||||
ensureAgentIsInitialized(resolvedAgentId).catch((error) => {
|
||||
@@ -538,7 +552,7 @@ function AgentScreenContent({
|
||||
error,
|
||||
});
|
||||
});
|
||||
}, [ensureAgentIsInitialized, isConnected, resolvedAgentId])
|
||||
}, [ensureAgentIsInitialized, hasSession, isConnected, resolvedAgentId])
|
||||
);
|
||||
|
||||
const isGitCheckout = activeExplorerCheckout?.isGit ?? false;
|
||||
@@ -745,7 +759,7 @@ function AgentScreenContent({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isConnected) {
|
||||
if (!isConnected || !hasSession) {
|
||||
return;
|
||||
}
|
||||
// On native clients, daemon stream forwarding is focused-agent only, so switching
|
||||
@@ -764,6 +778,7 @@ function AgentScreenContent({
|
||||
}, [
|
||||
resolvedAgentId,
|
||||
ensureAgentIsInitialized,
|
||||
hasSession,
|
||||
isConnected,
|
||||
needsAuthoritativeSync,
|
||||
]);
|
||||
@@ -784,7 +799,7 @@ function AgentScreenContent({
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!isConnected) {
|
||||
if (!isConnected || !hasSession) {
|
||||
return;
|
||||
}
|
||||
if (missingAgentState.kind === "resolving" || missingAgentState.kind === "not_found") {
|
||||
@@ -855,6 +870,7 @@ function AgentScreenContent({
|
||||
agent,
|
||||
client,
|
||||
ensureAgentIsInitialized,
|
||||
hasSession,
|
||||
isConnected,
|
||||
missingAgentState.kind,
|
||||
resolvedAgentId,
|
||||
@@ -898,13 +914,13 @@ function AgentScreenContent({
|
||||
]);
|
||||
|
||||
const handleRefreshAgent = useCallback(() => {
|
||||
if (!resolvedAgentId) {
|
||||
if (!resolvedAgentId || !hasSession) {
|
||||
return;
|
||||
}
|
||||
void refreshAgent(resolvedAgentId).catch((error) => {
|
||||
console.warn("[AgentScreen] refreshAgent failed", { agentId: resolvedAgentId, error });
|
||||
});
|
||||
}, [resolvedAgentId, refreshAgent]);
|
||||
}, [hasSession, refreshAgent, resolvedAgentId]);
|
||||
|
||||
const handleCopyMeta = useCallback(
|
||||
async (label: string, value: string | null | undefined) => {
|
||||
@@ -1262,7 +1278,7 @@ function AgentSessionUnavailableState({
|
||||
}: {
|
||||
onBack: () => void;
|
||||
serverLabel: string;
|
||||
connectionStatus: ConnectionStatus;
|
||||
connectionStatus: HostRuntimeConnectionStatus;
|
||||
lastError: string | null;
|
||||
isUnknownDaemon?: boolean;
|
||||
}) {
|
||||
@@ -1285,19 +1301,24 @@ function AgentSessionUnavailableState({
|
||||
}
|
||||
|
||||
const isConnecting = connectionStatus === "connecting";
|
||||
const isPreparingSession = connectionStatus === "online";
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<BackHeader title="Agent" onBack={onBack} />
|
||||
<View style={styles.centerState}>
|
||||
{isConnecting ? (
|
||||
{isConnecting || isPreparingSession ? (
|
||||
<>
|
||||
<ActivityIndicator size="large" />
|
||||
<Text style={styles.loadingText}>
|
||||
Connecting to {serverLabel}...
|
||||
{isPreparingSession
|
||||
? `Preparing ${serverLabel} session...`
|
||||
: `Connecting to ${serverLabel}...`}
|
||||
</Text>
|
||||
<Text style={styles.statusText}>
|
||||
We will show this agent once the host is online.
|
||||
{isPreparingSession
|
||||
? "We will show this agent in a moment."
|
||||
: "We will show this agent once the host is online."}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -35,6 +35,7 @@ import { buildWorkingDirectorySuggestions } from '@/utils/working-directory-sugg
|
||||
import { useExplorerOpenGesture } from '@/hooks/use-explorer-open-gesture'
|
||||
import { useSessionStore } from '@/stores/session-store'
|
||||
import { useCreateFlowStore } from '@/stores/create-flow-store'
|
||||
import { useHostRuntimeSession } from '@/runtime/host-runtime'
|
||||
import { ExplorerSidebarAnimationProvider } from '@/contexts/explorer-sidebar-animation-context'
|
||||
import { usePanelStore, type ExplorerCheckoutContext } from '@/stores/panel-store'
|
||||
import { MAX_CONTENT_WIDTH } from '@/constants/layout'
|
||||
@@ -368,18 +369,16 @@ function DraftAgentScreenContent({
|
||||
return collectAgentWorkingDirectorySuggestions([...liveSources, ...fetchedSources])
|
||||
}, [allAgents, sessionAgents])
|
||||
|
||||
const sessionClient = useSessionStore((state) =>
|
||||
selectedServerId ? (state.sessions[selectedServerId]?.client ?? null) : null
|
||||
)
|
||||
const isConnected = useSessionStore((state) =>
|
||||
selectedServerId ? (state.sessions[selectedServerId]?.connection.isConnected ?? false) : false
|
||||
const { client: runtimeClient, isConnected: isHostOnline } = useHostRuntimeSession(
|
||||
selectedServerId ?? ''
|
||||
)
|
||||
const sessionClient = runtimeClient
|
||||
const trimmedWorkingDir = workingDir.trim()
|
||||
const shouldInspectRepo = trimmedWorkingDir.length > 0
|
||||
const daemonAvailabilityError =
|
||||
!selectedServerId || hostEntry?.status !== 'online' ? 'Host is offline' : null
|
||||
!selectedServerId || !isHostOnline ? 'Host is offline' : null
|
||||
const repoAvailabilityError =
|
||||
shouldInspectRepo && (!hostEntry || hostEntry.status !== 'online' || !isConnected)
|
||||
shouldInspectRepo && !isHostOnline
|
||||
? (daemonAvailabilityError ??
|
||||
'Repository details will load automatically once the selected host is back online.')
|
||||
: null
|
||||
@@ -398,7 +397,7 @@ function DraftAgentScreenContent({
|
||||
Boolean(trimmedWorkingDir) &&
|
||||
!repoAvailabilityError &&
|
||||
Boolean(sessionClient) &&
|
||||
isConnected,
|
||||
isHostOnline,
|
||||
retry: false,
|
||||
staleTime: CHECKOUT_STATUS_STALE_TIME,
|
||||
refetchOnMount: 'always',
|
||||
@@ -461,7 +460,7 @@ function DraftAgentScreenContent({
|
||||
Boolean(worktreeListRoot) &&
|
||||
!repoAvailabilityError &&
|
||||
Boolean(sessionClient) &&
|
||||
isConnected &&
|
||||
isHostOnline &&
|
||||
!isNonGitDirectory,
|
||||
retry: false,
|
||||
staleTime: 0,
|
||||
@@ -526,7 +525,7 @@ function DraftAgentScreenContent({
|
||||
Boolean(trimmedWorkingDir) &&
|
||||
!repoAvailabilityError &&
|
||||
Boolean(sessionClient) &&
|
||||
isConnected,
|
||||
isHostOnline,
|
||||
retry: false,
|
||||
staleTime: 15_000,
|
||||
})
|
||||
@@ -559,7 +558,7 @@ function DraftAgentScreenContent({
|
||||
Boolean(selectedServerId) &&
|
||||
!daemonAvailabilityError &&
|
||||
Boolean(sessionClient) &&
|
||||
isConnected,
|
||||
isHostOnline,
|
||||
retry: false,
|
||||
staleTime: 15_000,
|
||||
})
|
||||
@@ -625,7 +624,7 @@ function DraftAgentScreenContent({
|
||||
Boolean(baseBranch) &&
|
||||
Boolean(trimmedWorkingDir) &&
|
||||
Boolean(sessionClient) &&
|
||||
isConnected,
|
||||
isHostOnline,
|
||||
retry: false,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
@@ -810,9 +809,7 @@ function DraftAgentScreenContent({
|
||||
})
|
||||
}, [baseBranch, branchSearchQuery, branchSuggestionsQuery.data, checkout, worktreeOptions])
|
||||
|
||||
const createAgentClient = useSessionStore((state) =>
|
||||
selectedServerId ? (state.sessions[selectedServerId]?.client ?? null) : null
|
||||
)
|
||||
const createAgentClient = sessionClient
|
||||
const draftCommandConfig = useMemo<DraftCommandConfig | undefined>(() => {
|
||||
const cwd = (
|
||||
isAttachWorktree && selectedWorktreePath ? selectedWorktreePath : workingDir
|
||||
|
||||
@@ -22,6 +22,7 @@ import { measureConnectionLatency } from "@/utils/test-daemon-connection";
|
||||
import { confirmDialog } from "@/utils/confirm-dialog";
|
||||
import { MenuHeader } from "@/components/headers/menu-header";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useHostRuntimeSession } from "@/runtime/host-runtime";
|
||||
import { AddHostMethodModal } from "@/components/add-host-method-modal";
|
||||
import { AddHostModal } from "@/components/add-host-modal";
|
||||
import { PairLinkModal } from "@/components/pair-link-modal";
|
||||
@@ -1035,10 +1036,11 @@ function HostDetailModal({
|
||||
);
|
||||
|
||||
// Restart logic (moved from DaemonCard)
|
||||
const daemonClient = useSessionStore((state) => host ? (state.sessions[host.serverId]?.client ?? null) : null);
|
||||
const daemonConnection = useSessionStore((state) => host ? (state.sessions[host.serverId]?.connection ?? null) : null);
|
||||
const { client: runtimeClient, isConnected } = useHostRuntimeSession(
|
||||
host?.serverId ?? ""
|
||||
);
|
||||
const daemonClient = runtimeClient;
|
||||
const daemonVersion = useSessionStore((state) => host ? (state.sessions[host.serverId]?.serverInfo?.version ?? null) : null);
|
||||
const isConnected = daemonConnection?.isConnected ?? false;
|
||||
const isConnectedRef = useRef(isConnected);
|
||||
const [isRestarting, setIsRestarting] = useState(false);
|
||||
|
||||
|
||||
@@ -147,12 +147,6 @@ export interface AgentFileExplorerState {
|
||||
selectedEntryPath: string | null;
|
||||
}
|
||||
|
||||
export interface DaemonConnectionSnapshot {
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
lastError: string | null;
|
||||
}
|
||||
|
||||
export type DaemonServerInfo = {
|
||||
serverId: string;
|
||||
hostname: string | null;
|
||||
@@ -173,9 +167,6 @@ export interface SessionState {
|
||||
// Daemon client (immutable reference)
|
||||
client: DaemonClient | null;
|
||||
|
||||
// Connection snapshot (mutable)
|
||||
connection: DaemonConnectionSnapshot;
|
||||
|
||||
// Server metadata (from server_info handshake)
|
||||
serverInfo: DaemonServerInfo | null;
|
||||
|
||||
@@ -233,7 +224,6 @@ interface SessionStoreActions {
|
||||
clearSession: (serverId: string) => void;
|
||||
getSession: (serverId: string) => SessionState | undefined;
|
||||
updateSessionClient: (serverId: string, client: DaemonClient) => void;
|
||||
updateSessionConnection: (serverId: string, connection: DaemonConnectionSnapshot) => void;
|
||||
updateSessionServerInfo: (serverId: string, info: DaemonServerInfo) => void;
|
||||
|
||||
// Audio state
|
||||
@@ -309,25 +299,11 @@ function logSessionStoreUpdate(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createDefaultConnectionSnapshot(client?: DaemonClient | null): DaemonConnectionSnapshot {
|
||||
if (!client) {
|
||||
return { isConnected: false, isConnecting: false, lastError: null };
|
||||
}
|
||||
const state = client.getConnectionState();
|
||||
return {
|
||||
isConnected: state.status === "connected",
|
||||
isConnecting: state.status === "connecting",
|
||||
lastError: state.status === "disconnected" ? state.reason ?? client.lastError ?? null : null,
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to create initial session state
|
||||
function createInitialSessionState(serverId: string, client: DaemonClient, audioPlayer: ReturnType<typeof useAudioPlayer>): SessionState {
|
||||
return {
|
||||
serverId,
|
||||
client,
|
||||
connection: createDefaultConnectionSnapshot(client),
|
||||
serverInfo: null,
|
||||
audioPlayer,
|
||||
hasHydratedAgents: false,
|
||||
@@ -420,33 +396,6 @@ export const useSessionStore = create<SessionStore>()(
|
||||
});
|
||||
},
|
||||
|
||||
updateSessionConnection: (serverId, connection) => {
|
||||
set((prev) => {
|
||||
const session = prev.sessions[serverId];
|
||||
if (!session) {
|
||||
return prev;
|
||||
}
|
||||
if (
|
||||
session.connection.isConnected === connection.isConnected &&
|
||||
session.connection.isConnecting === connection.isConnecting &&
|
||||
session.connection.lastError === connection.lastError
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
logSessionStoreUpdate("updateSessionConnection", serverId, connection);
|
||||
return {
|
||||
...prev,
|
||||
sessions: {
|
||||
...prev.sessions,
|
||||
[serverId]: {
|
||||
...session,
|
||||
connection,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
updateSessionServerInfo: (serverId, info) => {
|
||||
set((prev) => {
|
||||
const session = prev.sessions[serverId];
|
||||
|
||||
Reference in New Issue
Block a user