import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import type { AgentProvider, AgentSessionConfig } from "@server/server/agent/agent-sdk-types"; import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime"; import { mergeProviderPreferences, useFormPreferences } from "./use-form-preferences"; import { applyFeatureValues, pruneFeatureValues, resolveFeatureValues, } from "./feature-preferences"; type DraftFeatureConfig = Pick< AgentSessionConfig, "provider" | "cwd" | "modeId" | "model" | "thinkingOptionId" >; export function useDraftAgentFeatures(input: { serverId: string | null | undefined; provider: AgentProvider | null; cwd: string | null | undefined; modeId: string | null | undefined; modelId: string | null | undefined; thinkingOptionId: string | null | undefined; initialFeatureValues?: Record; }) { const { serverId, provider, cwd, modeId, modelId, thinkingOptionId, initialFeatureValues } = input; const [localFeatureValues, setLocalFeatureValues] = useState>( () => initialFeatureValues ?? {}, ); const client = useHostRuntimeClient(serverId ?? ""); const isConnected = useHostRuntimeIsConnected(serverId ?? ""); const { preferences, updatePreferences } = useFormPreferences(); const normalizedCwd = cwd?.trim() || ""; const normalizedProvider = provider ?? null; const previousProviderRef = useRef(normalizedProvider); const persistedFeatureValues = useMemo( () => (provider ? (preferences.providerPreferences?.[provider]?.featureValues ?? {}) : {}), [preferences.providerPreferences, provider], ); const draftConfig = useMemo(() => { if (!normalizedProvider || !normalizedCwd) { return null; } return { provider: normalizedProvider, cwd: normalizedCwd, ...(modeId ? { modeId } : {}), ...(modelId ? { model: modelId } : {}), ...(thinkingOptionId ? { thinkingOptionId } : {}), }; }, [modeId, modelId, normalizedCwd, normalizedProvider, thinkingOptionId]); const featuresQuery = useQuery({ queryKey: [ "providerFeatures", serverId ?? null, normalizedProvider, normalizedCwd || null, modeId ?? null, modelId ?? null, thinkingOptionId ?? null, ], enabled: Boolean(serverId && client && isConnected && draftConfig), staleTime: 5 * 60 * 1000, queryFn: async () => { if (!client || !draftConfig) { throw new Error("Host is not connected"); } const payload = await client.listProviderFeatures(draftConfig); if (payload.error) { throw new Error(payload.error); } return payload.features ?? []; }, }); const availableFeaturesRaw = featuresQuery.data; const availableFeatures = useMemo(() => availableFeaturesRaw ?? [], [availableFeaturesRaw]); const featureValues = useMemo( () => resolveFeatureValues({ features: availableFeatures, persistedFeatureValues, localFeatureValues, }), [availableFeatures, localFeatureValues, persistedFeatureValues], ); const features = useMemo(() => { return applyFeatureValues(availableFeatures, featureValues); }, [availableFeatures, featureValues]); useEffect(() => { const previousProvider = previousProviderRef.current; previousProviderRef.current = normalizedProvider; if (previousProvider === null) { return; } if (previousProvider !== normalizedProvider) { setLocalFeatureValues({}); } }, [normalizedProvider]); useEffect(() => { if (availableFeaturesRaw === undefined) { return; } const next = pruneFeatureValues(localFeatureValues, availableFeatures); if (next !== localFeatureValues) { setLocalFeatureValues(next); } }, [availableFeatures, availableFeaturesRaw, localFeatureValues]); const effectiveFeatureValues = Object.keys(featureValues).length > 0 ? featureValues : undefined; const setFeatureValue = useCallback( (featureId: string, value: unknown) => { setLocalFeatureValues((current) => { if (Object.is(current[featureId], value)) { return current; } return { ...current, [featureId]: value }; }); if (!provider) { return; } void updatePreferences((current) => mergeProviderPreferences({ preferences: current, provider, updates: { featureValues: { [featureId]: value, }, }, }), ).catch((error) => { console.warn("[useDraftAgentFeatures] persist feature preference failed", error); }); }, [provider, updatePreferences], ); return { features, featureValues: effectiveFeatureValues, isLoading: featuresQuery.isLoading, setFeatureValue, }; }