refactor(app): simplify session directory with direct snapshots and add sessionReady state
Replace subscription-based session accessor pattern with direct snapshot updates in daemon connections context. Add sessionReady flag to connection state to track when initial agent list has been received. Auto-select first ready host in create agent modal. Migrate from app.json to app.config.js. Add android:release script and Playwright testing docs.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
import type { Agent } from "@/contexts/session-context";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import { useSessionDirectory } from "@/hooks/use-session-directory";
|
||||
|
||||
export interface AggregatedAgent extends Agent {
|
||||
serverId: string;
|
||||
@@ -14,25 +13,24 @@ export interface AggregatedAgentsResult {
|
||||
}
|
||||
|
||||
export function useAggregatedAgents(): AggregatedAgentsResult {
|
||||
const { connectionStates, isLoading: registryLoading } = useDaemonConnections();
|
||||
const sessionDirectory = useSessionDirectory();
|
||||
const { connectionStates, sessionSnapshots } = useDaemonConnections();
|
||||
|
||||
return useMemo(() => {
|
||||
const allAgents: AggregatedAgent[] = [];
|
||||
|
||||
sessionDirectory.forEach((session, serverId) => {
|
||||
if (!session) {
|
||||
return;
|
||||
for (const [serverId, snapshot] of sessionSnapshots) {
|
||||
if (snapshot.agents.size === 0) {
|
||||
continue;
|
||||
}
|
||||
const serverLabel = connectionStates.get(serverId)?.daemon.label ?? serverId;
|
||||
for (const agent of session.agents.values()) {
|
||||
for (const agent of snapshot.agents.values()) {
|
||||
allAgents.push({
|
||||
...agent,
|
||||
serverId,
|
||||
serverLabel,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by: running agents first, then by most recent activity
|
||||
allAgents.sort((left, right) => {
|
||||
@@ -49,19 +47,18 @@ export function useAggregatedAgents(): AggregatedAgentsResult {
|
||||
return rightTime - leftTime;
|
||||
});
|
||||
|
||||
// Loading if registry is still loading, or if any host is connecting and hasn't
|
||||
// reported agents yet (idle hosts that haven't connected don't block loading)
|
||||
let isLoading = registryLoading;
|
||||
if (!isLoading && connectionStates.size > 0) {
|
||||
for (const [serverId, record] of connectionStates) {
|
||||
const hasSession = sessionDirectory.has(serverId);
|
||||
if (record.status === "connecting" && !hasSession) {
|
||||
isLoading = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const isLoading = Array.from(connectionStates.values()).some(c => {
|
||||
// Only these states count as "loading":
|
||||
// 1. Actively connecting
|
||||
// 2. Online but session not yet received
|
||||
if (c.status === 'connecting') return true;
|
||||
if (c.status === 'online' && !c.sessionReady) return true;
|
||||
|
||||
// Offline/error = not loading, just unavailable (normal state)
|
||||
return false;
|
||||
});
|
||||
|
||||
console.log("[useAggregatedAgents] aggregated agent count:", allAgents.length, "isLoading:", isLoading);
|
||||
return { agents: allAgents, isLoading };
|
||||
}, [sessionDirectory, connectionStates, registryLoading]);
|
||||
}, [sessionSnapshots, connectionStates]);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class DaemonSessionUnavailableError extends Error {
|
||||
|
||||
export function getSessionForServer(
|
||||
serverId: string,
|
||||
directory: Map<string, SessionContextValue | null>
|
||||
directory: Map<string, SessionContextValue>
|
||||
): SessionContextValue {
|
||||
const session = directory.get(serverId) ?? null;
|
||||
if (!session) {
|
||||
|
||||
@@ -1,35 +1,20 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
|
||||
import type { SessionContextValue } from "@/contexts/session-context";
|
||||
|
||||
export function useSessionDirectory(): Map<string, SessionContextValue | null> {
|
||||
const { sessionAccessors, subscribeToSessionDirectory } = useDaemonConnections();
|
||||
const [revision, setRevision] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
return subscribeToSessionDirectory(() => {
|
||||
setRevision((current) => current + 1);
|
||||
});
|
||||
}, [subscribeToSessionDirectory]);
|
||||
export function useSessionDirectory(): Map<string, SessionContextValue> {
|
||||
const { sessionSnapshots } = useDaemonConnections();
|
||||
|
||||
return useMemo(() => {
|
||||
const entries = new Map<string, SessionContextValue | null>();
|
||||
sessionAccessors.forEach((entry, serverId) => {
|
||||
try {
|
||||
entries.set(serverId, entry.getSnapshot());
|
||||
} catch (error) {
|
||||
console.error(`[useSessionDirectory] Failed to read session accessor for "${serverId}"`, error);
|
||||
entries.set(serverId, null);
|
||||
}
|
||||
const entries = new Map<string, SessionContextValue>();
|
||||
sessionSnapshots.forEach((snapshot, serverId) => {
|
||||
entries.set(serverId, snapshot);
|
||||
});
|
||||
return entries;
|
||||
}, [sessionAccessors, revision]);
|
||||
}, [sessionSnapshots]);
|
||||
}
|
||||
|
||||
export function useSessionForServer(serverId: string | null): SessionContextValue | null {
|
||||
const directory = useSessionDirectory();
|
||||
if (!serverId) {
|
||||
return null;
|
||||
}
|
||||
return directory.get(serverId) ?? null;
|
||||
const { sessionSnapshots } = useDaemonConnections();
|
||||
return serverId ? sessionSnapshots.get(serverId) ?? null : null;
|
||||
}
|
||||
|
||||
@@ -207,7 +207,10 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
|
||||
}, []);
|
||||
|
||||
const on = useCallback(
|
||||
(type: SessionOutboundMessage["type"], handler: (message: SessionOutboundMessage) => void) => {
|
||||
(
|
||||
type: SessionOutboundMessage["type"],
|
||||
handler: (message: SessionOutboundMessage) => void
|
||||
) => {
|
||||
if (!handlersRef.current.has(type)) {
|
||||
handlersRef.current.set(type, new Set());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user