Introduce DaemonRegistry and DaemonConnections contexts for managing multiple server connections. Add session directory for aggregating agents across daemons, with automatic routing based on agent/server context. Key changes: - Add daemon profile management (add/edit/remove/set default) - Implement connection state tracking with reconnection and exponential backoff - Create useAggregatedAgents hook for unified agent list across daemons - Add useDaemonSession and useDaemonRequest hooks for daemon-scoped operations - Update agent routes to include serverId for proper session routing - Add connection health indicators on home screen and settings - Persist session snapshots per daemon for faster hydration on reconnect - Guard screens gracefully when target daemon is offline - Add React Query for consistent loading/error state management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
import { useRef } from "react";
|
|
import { Alert } from "react-native";
|
|
import type { SessionContextValue } from "@/contexts/session-context";
|
|
import { useSession } from "@/contexts/session-context";
|
|
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
|
import { useSessionDirectory } from "./use-session-directory";
|
|
|
|
export class DaemonSessionUnavailableError extends Error {
|
|
serverId: string;
|
|
|
|
constructor(serverId: string) {
|
|
super(`Daemon session "${serverId}" is unavailable`);
|
|
this.name = "DaemonSessionUnavailableError";
|
|
this.serverId = serverId;
|
|
}
|
|
}
|
|
|
|
export function getSessionForServer(
|
|
serverId: string,
|
|
directory: Map<string, SessionContextValue | null>
|
|
): SessionContextValue {
|
|
const session = directory.get(serverId) ?? null;
|
|
if (!session) {
|
|
throw new DaemonSessionUnavailableError(serverId);
|
|
}
|
|
return session;
|
|
}
|
|
|
|
type UseDaemonSessionOptions = {
|
|
suppressUnavailableAlert?: boolean;
|
|
};
|
|
|
|
export function useDaemonSession(serverId?: string, options?: UseDaemonSessionOptions) {
|
|
const activeSession = useSession();
|
|
const sessionDirectory = useSessionDirectory();
|
|
const { connectionStates } = useDaemonConnections();
|
|
const alertedDaemonsRef = useRef<Set<string>>(new Set());
|
|
const loggedDaemonsRef = useRef<Set<string>>(new Set());
|
|
const { suppressUnavailableAlert = false } = options ?? {};
|
|
|
|
const targetServerId = serverId ?? activeSession.serverId;
|
|
const isActiveSession = targetServerId === activeSession.serverId;
|
|
|
|
if (isActiveSession || !targetServerId) {
|
|
return activeSession;
|
|
}
|
|
|
|
try {
|
|
return getSessionForServer(targetServerId, sessionDirectory);
|
|
} catch (error) {
|
|
if (error instanceof DaemonSessionUnavailableError) {
|
|
const connection = connectionStates.get(targetServerId);
|
|
const label = connection?.daemon.label ?? targetServerId;
|
|
const status = connection?.status ?? "unknown";
|
|
const lastError = connection?.lastError ? `\n${connection.lastError}` : "";
|
|
const message = `${label} isn't connected yet (${status}). Switch to it or enable auto-connect so Paseo can reach it.${lastError}`;
|
|
|
|
if (!suppressUnavailableAlert && !alertedDaemonsRef.current.has(targetServerId)) {
|
|
alertedDaemonsRef.current.add(targetServerId);
|
|
Alert.alert("Daemon unavailable", message.trim());
|
|
}
|
|
|
|
if (!loggedDaemonsRef.current.has(targetServerId)) {
|
|
loggedDaemonsRef.current.add(targetServerId);
|
|
console.warn(`[useDaemonSession] Session unavailable for daemon "${label}" (${status}).`);
|
|
}
|
|
}
|
|
throw error;
|
|
}
|
|
}
|