Files
paseo/packages/app/src/hooks/use-daemon-session.ts
T
Mohamed BoudraandClaude 4ab79b1a50 feat: add multi-daemon support with session routing and connection management
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>
2025-11-26 08:25:02 +01:00

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;
}
}