import { buildAgentMainSessionKey, isSameSessionKey } from "@/lib/gateway/GatewayClient"; import { type GatewayModelPolicySnapshot } from "@/lib/gateway/models"; import { type StudioSettings, type StudioSettingsPublic } from "@/lib/studio/settings"; import { type SummaryPreviewSnapshot, type SummarySnapshotPatch, type SummaryStatusSnapshot, } from "@/features/agents/state/runtimeEventBridge"; import type { AgentStoreSeed } from "@/features/agents/state/store"; import { deriveHydrateAgentFleetResult } from "@/features/agents/operations/agentFleetHydrationDerivation"; type GatewayClientLike = { call: (method: string, params: unknown) => Promise; getLastHello?: () => { snapshot?: unknown } | null; }; type AgentsListResult = { defaultId: string; mainKey: string; scope?: string; agents: Array<{ id: string; name?: string; identity?: { name?: string; theme?: string; emoji?: string; avatar?: string; avatarUrl?: string; }; }>; }; type SessionsListEntry = { key: string; updatedAt?: number | null; displayName?: string; origin?: { label?: string | null; provider?: string | null } | null; thinkingLevel?: string; modelProvider?: string; model?: string; execHost?: string | null; execSecurity?: string | null; execAsk?: string | null; }; type SessionsListResult = { sessions?: SessionsListEntry[]; }; type ExecApprovalsSnapshot = { file?: { agents?: Record; }; }; const isRecord = (value: unknown): value is Record => Boolean(value && typeof value === "object" && !Array.isArray(value)); const resolveAgentsListFromHelloSnapshot = (snapshot: unknown): AgentsListResult | null => { if (!isRecord(snapshot)) return null; const health = isRecord(snapshot.health) ? snapshot.health : null; const sessionDefaults = isRecord(snapshot.sessionDefaults) ? snapshot.sessionDefaults : null; const rawAgents = Array.isArray(health?.agents) ? health.agents : []; const agents = rawAgents.flatMap((entry) => { if (!isRecord(entry)) return []; const id = typeof entry.agentId === "string" ? entry.agentId.trim() : ""; if (!id) return []; const name = typeof entry.name === "string" ? entry.name.trim() : ""; return [ { id, ...(name ? { name } : {}), }, ]; }); if (agents.length === 0) return null; const defaultId = typeof health?.defaultAgentId === "string" ? health.defaultAgentId.trim() : agents.find((entry, index) => { const raw = rawAgents[index]; return isRecord(raw) && raw.isDefault === true; })?.id ?? agents[0]?.id ?? "main"; const mainKey = typeof sessionDefaults?.mainKey === "string" ? sessionDefaults.mainKey.trim() || "main" : "main"; const scope = typeof sessionDefaults?.scope === "string" ? sessionDefaults.scope.trim() || undefined : undefined; return { defaultId, mainKey, ...(scope ? { scope } : {}), agents, }; }; export type HydrateAgentFleetResult = { seeds: AgentStoreSeed[]; sessionCreatedAgentIds: string[]; sessionSettingsSyncedAgentIds: string[]; summaryPatches: SummarySnapshotPatch[]; suggestedSelectedAgentId: string | null; configSnapshot: GatewayModelPolicySnapshot | null; }; export async function hydrateAgentFleetFromGateway(params: { client: GatewayClientLike; gatewayUrl: string; cachedConfigSnapshot: GatewayModelPolicySnapshot | null; loadStudioSettings: () => Promise; isDisconnectLikeError: (err: unknown) => boolean; logError?: (message: string, error: unknown) => void; }): Promise { const logError = params.logError ?? ((message, error) => console.error(message, error)); let configSnapshot = params.cachedConfigSnapshot; if (!configSnapshot) { try { configSnapshot = (await params.client.call( "config.get", {} )) as GatewayModelPolicySnapshot; } catch (err) { if (!params.isDisconnectLikeError(err)) { logError("Failed to load gateway config while loading agents.", err); } } } const gatewayKey = params.gatewayUrl.trim(); let settings: StudioSettings | StudioSettingsPublic | null = null; if (gatewayKey) { try { settings = await params.loadStudioSettings(); } catch (err) { logError("Failed to load studio settings while loading agents.", err); } } let execApprovalsSnapshot: ExecApprovalsSnapshot | null = null; try { execApprovalsSnapshot = (await params.client.call( "exec.approvals.get", {} )) as ExecApprovalsSnapshot; } catch (err) { if (!params.isDisconnectLikeError(err)) { logError("Failed to load exec approvals while loading agents.", err); } } let agentsResult = (await params.client.call("agents.list", {})) as AgentsListResult; if (!Array.isArray(agentsResult?.agents) || agentsResult.agents.length === 0) { const fallback = resolveAgentsListFromHelloSnapshot(params.client.getLastHello?.()?.snapshot); if (fallback) { agentsResult = fallback; } } const mainKey = agentsResult.mainKey?.trim() || "main"; const mainSessionKeyByAgent = new Map(); await Promise.all( agentsResult.agents.map(async (agent) => { try { const expectedMainKey = buildAgentMainSessionKey(agent.id, mainKey); const sessions = (await params.client.call("sessions.list", { agentId: agent.id, includeGlobal: false, includeUnknown: false, search: expectedMainKey, limit: 4, })) as SessionsListResult; const entries = Array.isArray(sessions.sessions) ? sessions.sessions : []; const mainEntry = entries.find((entry) => isSameSessionKey(entry.key ?? "", expectedMainKey)) ?? null; mainSessionKeyByAgent.set(agent.id, mainEntry); } catch (err) { if (!params.isDisconnectLikeError(err)) { logError("Failed to list sessions while resolving agent session.", err); } mainSessionKeyByAgent.set(agent.id, null); } }) ); let statusSummary: SummaryStatusSnapshot | null = null; let previewResult: SummaryPreviewSnapshot | null = null; try { const sessionKeys = Array.from( new Set( agentsResult.agents .filter((agent) => Boolean(mainSessionKeyByAgent.get(agent.id))) .map((agent) => buildAgentMainSessionKey(agent.id, mainKey)) .filter((key) => key.trim().length > 0) ) ).slice(0, 64); if (sessionKeys.length > 0) { const snapshot = await Promise.all([ params.client.call("status", {}) as Promise, params.client.call("sessions.preview", { keys: sessionKeys, limit: 8, maxChars: 240, }) as Promise, ]); statusSummary = snapshot[0] ?? null; previewResult = snapshot[1] ?? null; } } catch (err) { if (!params.isDisconnectLikeError(err)) { logError("Failed to load initial summary snapshot.", err); } } const derived = deriveHydrateAgentFleetResult({ gatewayUrl: params.gatewayUrl, configSnapshot: configSnapshot ?? null, settings, execApprovalsSnapshot, agentsResult, mainSessionByAgentId: mainSessionKeyByAgent, statusSummary, previewResult, }); return derived; }