Improve startup provider snapshots and dev harness

This commit is contained in:
Mohamed Boudra
2026-04-22 10:52:19 +07:00
parent 27ddfb1ea7
commit 247983ee87
41 changed files with 2933 additions and 446 deletions
@@ -418,7 +418,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
error: snapshotError,
refresh: refreshSnapshot,
refetchIfStale: refetchSnapshotIfStale,
} = useProvidersSnapshot(formState.serverId, formState.workingDir);
} = useProvidersSnapshot(formState.serverId);
const allProviderEntries = useMemo(() => snapshotEntries ?? [], [snapshotEntries]);
const snapshotProviderDefinitions = useMemo(
@@ -0,0 +1,262 @@
/**
* @vitest-environment jsdom
*/
import React from "react";
import { act, renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { afterEach, describe, expect, it, vi } from "vitest";
import type {
DaemonClient,
FetchAgentHistoryEntry,
FetchAgentHistoryOptions,
} from "@server/client/daemon-client";
import { useSessionStore, type Agent } from "@/stores/session-store";
import { useAgentHistory } from "./use-agent-history";
const { mockClient, mockRuntimeStore } = vi.hoisted(() => {
const mockClient = {
fetchAgentHistory: vi.fn(),
};
const mockRuntimeStore = {
refreshAgentDirectory: vi.fn(),
};
return { mockClient, mockRuntimeStore };
});
vi.mock("@/runtime/host-runtime", () => ({
getHostRuntimeStore: () => mockRuntimeStore,
useHostRuntimeClient: () => mockClient,
useHostRuntimeIsConnected: () => true,
useHosts: () => [{ serverId: "server-1", label: "Local" }],
}));
function createQueryClient(): QueryClient {
return new QueryClient({
defaultOptions: {
queries: { retry: false },
},
});
}
function renderAgentHistoryHook(options?: { enabled?: boolean }) {
const queryClient = createQueryClient();
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
return renderHook(() => useAgentHistory({ serverId: "server-1", enabled: options?.enabled }), {
wrapper,
});
}
function makeHistoryPayload(input: {
entries: FetchAgentHistoryEntry[];
hasMore?: boolean;
nextCursor?: string | null;
}): Awaited<ReturnType<DaemonClient["fetchAgentHistory"]>> {
return {
requestId: "req_history",
entries: input.entries,
pageInfo: {
nextCursor: input.nextCursor ?? null,
prevCursor: null,
hasMore: input.hasMore ?? false,
},
};
}
function makeHistoryEntry(input: {
id: string;
cwd: string;
updatedAt: string;
title?: string | null;
archivedAt?: string | null;
}): FetchAgentHistoryEntry {
return {
agent: {
id: input.id,
provider: "codex",
status: "closed",
createdAt: input.updatedAt,
updatedAt: input.updatedAt,
lastUserMessageAt: null,
lastError: undefined,
runtimeInfo: {
provider: "codex",
sessionId: null,
},
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
title: input.title ?? null,
cwd: input.cwd,
model: null,
thinkingOptionId: null,
requiresAttention: false,
attentionReason: null,
attentionTimestamp: null,
archivedAt: input.archivedAt ?? null,
labels: {},
},
project: {
projectKey: input.cwd,
projectName: "workspace",
checkout: {
cwd: input.cwd,
isGit: false,
currentBranch: null,
remoteUrl: null,
worktreeRoot: null,
isPaseoOwnedWorktree: false,
mainRepoRoot: null,
},
},
};
}
function makeActiveAgent(): Agent {
const timestamp = new Date("2026-04-01T10:00:00.000Z");
return {
serverId: "server-1",
id: "active-1",
provider: "codex",
status: "idle",
createdAt: timestamp,
updatedAt: timestamp,
lastUserMessageAt: null,
lastActivityAt: timestamp,
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
title: "Active",
cwd: "/repo",
model: null,
labels: {},
archivedAt: null,
};
}
afterEach(() => {
mockClient.fetchAgentHistory.mockReset();
mockRuntimeStore.refreshAgentDirectory.mockReset();
useSessionStore.setState({ sessions: {}, agentLastActivity: new Map() });
});
describe("useAgentHistory", () => {
it("loads history one page at a time without refreshing active agents", async () => {
mockClient.fetchAgentHistory
.mockResolvedValueOnce(
makeHistoryPayload({
entries: [
makeHistoryEntry({
id: "history-1",
cwd: "/repo",
updatedAt: "2026-04-02T10:00:00.000Z",
title: "History one",
}),
],
hasMore: true,
nextCursor: "cursor-2",
}),
)
.mockResolvedValueOnce(
makeHistoryPayload({
entries: [
makeHistoryEntry({
id: "history-2",
cwd: "/repo",
updatedAt: "2026-04-01T10:00:00.000Z",
title: "History two",
archivedAt: "2026-04-01T10:05:00.000Z",
}),
],
}),
);
act(() => {
useSessionStore
.getState()
.initializeSession("server-1", mockClient as unknown as DaemonClient);
useSessionStore.getState().setAgents("server-1", new Map([["active-1", makeActiveAgent()]]));
});
const { result } = renderAgentHistoryHook();
await waitFor(() => {
expect(mockClient.fetchAgentHistory).toHaveBeenCalledTimes(1);
});
expect(mockClient.fetchAgentHistory.mock.calls.map(([options]) => options)).toEqual([
{
sort: [{ key: "updated_at", direction: "desc" }],
page: { limit: 200 },
},
] satisfies FetchAgentHistoryOptions[]);
await waitFor(() => {
expect(result.current.agents.map((agent) => agent.id)).toEqual(["history-1"]);
});
expect(result.current.hasMore).toBe(true);
await act(async () => {
result.current.loadMore();
});
await waitFor(() => {
expect(mockClient.fetchAgentHistory).toHaveBeenCalledTimes(2);
});
expect(mockClient.fetchAgentHistory.mock.calls.at(-1)?.[0]).toEqual({
sort: [{ key: "updated_at", direction: "desc" }],
page: { limit: 200, cursor: "cursor-2" },
} satisfies FetchAgentHistoryOptions);
await waitFor(() => {
expect(result.current.agents.map((agent) => agent.id)).toEqual(["history-1", "history-2"]);
});
expect(result.current.hasMore).toBe(false);
expect(
Array.from(useSessionStore.getState().sessions["server-1"]?.agents.keys() ?? []),
).toEqual(["active-1"]);
expect(mockRuntimeStore.refreshAgentDirectory).not.toHaveBeenCalled();
});
it("waits until history is enabled before calling the history RPC", async () => {
mockClient.fetchAgentHistory.mockResolvedValue(makeHistoryPayload({ entries: [] }));
const queryClient = createQueryClient();
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
let enabled = false;
const { rerender } = renderHook(() => useAgentHistory({ serverId: "server-1", enabled }), {
wrapper,
});
await act(async () => {
await Promise.resolve();
});
expect(mockClient.fetchAgentHistory).not.toHaveBeenCalled();
enabled = true;
rerender();
await waitFor(() => {
expect(mockClient.fetchAgentHistory).toHaveBeenCalledTimes(1);
});
});
});
+155
View File
@@ -0,0 +1,155 @@
import type {
DaemonClient,
FetchAgentHistoryOptions,
FetchAgentHistoryPageInfo,
} from "@server/client/daemon-client";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
import { useHostRuntimeClient, useHostRuntimeIsConnected, useHosts } from "@/runtime/host-runtime";
import { buildAgentDirectoryState } from "@/utils/agent-directory-sync";
const AGENT_HISTORY_PAGE_LIMIT = 200;
const AGENT_HISTORY_SORT: NonNullable<FetchAgentHistoryOptions["sort"]> = [
{ key: "updated_at", direction: "desc" },
];
export interface AgentHistoryResult {
agents: AggregatedAgent[];
isLoading: boolean;
isInitialLoad: boolean;
isRevalidating: boolean;
hasMore: boolean;
isLoadingMore: boolean;
refreshAll: () => void;
loadMore: () => void;
}
type AgentHistoryPage = {
agents: AggregatedAgent[];
pageInfo: FetchAgentHistoryPageInfo;
};
export function agentHistoryQueryKey(serverId: string | null) {
return ["agentHistory", serverId] as const;
}
async function fetchAgentHistoryPage(input: {
client: DaemonClient;
serverId: string;
cursor: string | null;
}): Promise<AgentHistoryPage> {
const payload = await input.client.fetchAgentHistory({
sort: AGENT_HISTORY_SORT,
page: input.cursor
? { limit: AGENT_HISTORY_PAGE_LIMIT, cursor: input.cursor }
: { limit: AGENT_HISTORY_PAGE_LIMIT },
});
const { agents } = buildAgentDirectoryState({
serverId: input.serverId,
entries: payload.entries,
});
return {
agents: Array.from(agents.values(), (agent) => ({
id: agent.id,
serverId: input.serverId,
serverLabel: input.serverId,
title: agent.title ?? null,
status: agent.status,
lastActivityAt: agent.lastActivityAt,
cwd: agent.cwd,
provider: agent.provider,
pendingPermissionCount: agent.pendingPermissions.length,
requiresAttention: agent.requiresAttention,
attentionReason: agent.attentionReason,
attentionTimestamp: agent.attentionTimestamp ?? null,
archivedAt: agent.archivedAt ?? null,
createdAt: agent.createdAt,
labels: agent.labels,
})),
pageInfo: payload.pageInfo,
};
}
export function useAgentHistory(options: {
serverId?: string | null;
enabled?: boolean;
}): AgentHistoryResult {
const daemons = useHosts();
const serverId = useMemo(() => {
const value = options.serverId;
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}, [options.serverId]);
const enabled = options.enabled ?? true;
const client = useHostRuntimeClient(serverId ?? "");
const isConnected = useHostRuntimeIsConnected(serverId ?? "");
const queryKey = useMemo(() => agentHistoryQueryKey(serverId), [serverId]);
const serverLabel = daemons.find((daemon) => daemon.serverId === serverId)?.label ?? serverId;
const historyQuery = useInfiniteQuery<
AgentHistoryPage,
Error,
{ pages: AgentHistoryPage[] },
ReturnType<typeof agentHistoryQueryKey>,
string | null
>({
queryKey,
enabled: Boolean(enabled && serverId && client && isConnected),
staleTime: 30_000,
initialPageParam: null as string | null,
getNextPageParam: (lastPage) =>
lastPage.pageInfo.hasMore ? lastPage.pageInfo.nextCursor : null,
queryFn: async ({ pageParam }) => {
if (!serverId || !client) {
throw new Error("Host is not connected");
}
return fetchAgentHistoryPage({ client, serverId, cursor: pageParam });
},
});
const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, isLoading, refetch } =
historyQuery;
const refreshAll = useCallback(() => {
if (!serverId || !client || !isConnected) {
return;
}
void refetch();
}, [client, isConnected, refetch, serverId]);
const loadMore = useCallback(() => {
if (!serverId || !client || !isConnected || !hasNextPage || isFetchingNextPage) {
return;
}
void fetchNextPage();
}, [client, fetchNextPage, hasNextPage, isConnected, isFetchingNextPage, serverId]);
const agents = useMemo(
() =>
(data?.pages ?? [])
.flatMap((page) => page.agents)
.map((agent) => ({
...agent,
serverLabel: serverLabel ?? agent.serverLabel,
})),
[data?.pages, serverLabel],
);
const isInitialLoad = isLoading && agents.length === 0;
const isRevalidating = isFetching && !isFetchingNextPage && agents.length > 0;
return {
agents,
isLoading,
isInitialLoad,
isRevalidating,
hasMore: Boolean(hasNextPage),
isLoadingMore: isFetchingNextPage,
refreshAll,
loadMore,
};
}
export const __private__ = {
fetchAgentHistoryPage,
};
@@ -9,11 +9,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import type { DaemonClient } from "@server/client/daemon-client";
import type { ProviderSnapshotEntry } from "@server/server/agent/agent-sdk-types";
import { useSessionStore } from "@/stores/session-store";
import {
providersSnapshotQueryKey,
shouldApplyProvidersSnapshotUpdate,
useProvidersSnapshot,
} from "./use-providers-snapshot";
import { providersSnapshotQueryKey, useProvidersSnapshot } from "./use-providers-snapshot";
type ProviderSnapshotUpdateMessage = {
type: "providers_snapshot_update";
@@ -80,12 +76,12 @@ function enableProvidersSnapshot(): void {
});
}
function renderProvidersSnapshotHook(cwd?: string | null) {
function renderProvidersSnapshotHook() {
const queryClient = createQueryClient();
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
return renderHook(() => useProvidersSnapshot(serverId, cwd), { wrapper });
return renderHook(() => useProvidersSnapshot(serverId), { wrapper });
}
const readyCodexModel = { provider: "codex", id: "gpt-5.4", label: "GPT-5.4" } as const;
@@ -124,7 +120,10 @@ async function waitForSnapshotEntries(
});
}
async function emitProvidersSnapshotUpdate(entries: ProviderSnapshotEntry[]): Promise<void> {
async function emitProvidersSnapshotUpdate(
entries: ProviderSnapshotEntry[],
cwd = "/repo",
): Promise<void> {
const listener = snapshotUpdateListeners.at(-1);
expect(listener).toBeDefined();
@@ -132,7 +131,7 @@ async function emitProvidersSnapshotUpdate(entries: ProviderSnapshotEntry[]): Pr
listener?.({
type: "providers_snapshot_update",
payload: {
cwd: "/repo",
cwd,
entries,
generatedAt: "2026-01-01T00:00:01.000Z",
},
@@ -155,23 +154,8 @@ afterEach(() => {
});
describe("providers snapshot hook cache scope", () => {
it("uses no cwd in the settings query key", () => {
expect(providersSnapshotQueryKey(serverId)).toEqual(["providersSnapshot", serverId, null]);
});
it("accepts concrete home update events for settings snapshots", () => {
expect(shouldApplyProvidersSnapshotUpdate(undefined, "/Users/alex")).toBe(true);
expect(shouldApplyProvidersSnapshotUpdate(null, "/home/alex")).toBe(true);
});
it("keeps workspace snapshot updates scoped to matching cwd keys", () => {
expect(shouldApplyProvidersSnapshotUpdate("/Users/alex/project", "/Users/alex/project")).toBe(
true,
);
expect(
shouldApplyProvidersSnapshotUpdate("/Users/alex/project-a", "/Users/alex/project-b"),
).toBe(false);
expect(shouldApplyProvidersSnapshotUpdate("/Users/alex/project", "/Users/alex")).toBe(false);
it("uses a global query key without cwd", () => {
expect(providersSnapshotQueryKey(serverId)).toEqual(["providersSnapshot", serverId]);
});
it("sends no cwd for settings snapshot loads and refreshes", async () => {
@@ -196,7 +180,7 @@ describe("providers snapshot hook cache scope", () => {
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
});
it("sends cwd for workspace snapshot loads and refreshes", async () => {
it("does not send cwd for repeated snapshot loads and refreshes", async () => {
enableProvidersSnapshot();
mockClient.getProvidersSnapshot.mockResolvedValue(providersSnapshot([]));
mockClient.refreshProvidersSnapshot.mockResolvedValue({
@@ -204,21 +188,30 @@ describe("providers snapshot hook cache scope", () => {
requestId: "workspace-refresh",
});
const { result } = renderProvidersSnapshotHook("/repo");
const { result } = renderProvidersSnapshotHook();
await waitFor(() => {
expect(mockClient.getProvidersSnapshot).toHaveBeenCalledWith({ cwd: "/repo" });
expect(mockClient.getProvidersSnapshot).toHaveBeenCalledWith({});
});
await act(async () => {
await result.current.refresh(["codex"]);
});
expect(mockClient.refreshProvidersSnapshot).toHaveBeenCalledWith({
cwd: "/repo",
providers: ["codex"],
});
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({ cwd: "/repo" });
expect(mockClient.refreshProvidersSnapshot).toHaveBeenCalledWith({ providers: ["codex"] });
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
});
it("applies provider snapshot updates from other cwd values to the global cache", async () => {
enableProvidersSnapshot();
mockClient.getProvidersSnapshot.mockResolvedValue(providersSnapshot([]));
const { result } = renderProvidersSnapshotHook();
await waitForSnapshotEntries(result, []);
await emitProvidersSnapshotUpdate([codexEntry("ready", [readyCodexModel])], "/repo-b");
await waitForSnapshotEntries(result, [codexEntry("ready", [readyCodexModel])]);
});
it("refetches loading snapshot updates through the read path but ignores empty updates", async () => {
@@ -227,7 +220,7 @@ describe("providers snapshot hook cache scope", () => {
.mockResolvedValueOnce(providersSnapshot([codexEntry("ready", [])]))
.mockResolvedValueOnce(providersSnapshot([codexEntry("ready", [readyCodexModel])]));
renderProvidersSnapshotHook("/repo");
renderProvidersSnapshotHook();
await waitForSnapshotReads(1);
await emitProvidersSnapshotUpdate([]);
@@ -237,7 +230,7 @@ describe("providers snapshot hook cache scope", () => {
await emitProvidersSnapshotUpdate([codexEntry("loading")]);
await waitForSnapshotReads(2);
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({ cwd: "/repo" });
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
expect(mockClient.refreshProvidersSnapshot).not.toHaveBeenCalled();
});
@@ -252,13 +245,13 @@ describe("providers snapshot hook cache scope", () => {
.mockResolvedValueOnce(providersSnapshot(entries))
.mockResolvedValueOnce(providersSnapshot([codexEntry("ready", [readyCodexModel])]));
const { result } = renderProvidersSnapshotHook("/repo");
const { result } = renderProvidersSnapshotHook();
await waitForSnapshotEntries(result, entries);
await openSelectorForSelectedProvider(result);
await waitForSnapshotReads(2);
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({ cwd: "/repo" });
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
expect(mockClient.refreshProvidersSnapshot).not.toHaveBeenCalled();
});
@@ -266,7 +259,7 @@ describe("providers snapshot hook cache scope", () => {
enableProvidersSnapshot();
mockClient.getProvidersSnapshot.mockResolvedValue(providersSnapshot([codexEntry("ready", [])]));
const { result } = renderProvidersSnapshotHook("/repo");
const { result } = renderProvidersSnapshotHook();
await waitForSnapshotEntries(result, [codexEntry("ready", [])]);
await openSelectorForSelectedProvider(result);
@@ -6,40 +6,8 @@ import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-
import { useSessionStore } from "@/stores/session-store";
import { queryClient as singletonQueryClient } from "@/query/query-client";
export function normalizeProvidersSnapshotCwdKey(cwd?: string | null): string | null {
const trimmed = cwd?.trim();
if (!trimmed) {
return null;
}
return trimmed.replace(/^\/(?:Users|home)\/[^/]+/, "~");
}
export function shouldApplyProvidersSnapshotUpdate(
currentCwd?: string | null,
messageCwd?: string | null,
): boolean {
const currentCwdKey = normalizeProvidersSnapshotCwdKey(currentCwd);
const messageCwdKey = normalizeProvidersSnapshotCwdKey(messageCwd);
return messageCwdKey === currentCwdKey || (currentCwdKey === null && messageCwdKey === "~");
}
export function providersSnapshotQueryKey(serverId: string | null, cwd?: string | null) {
return ["providersSnapshot", serverId, normalizeProvidersSnapshotCwdKey(cwd)] as const;
}
function providersSnapshotRequest(cwd?: string): { cwd?: string } {
return cwd ? { cwd } : {};
}
function refreshProvidersSnapshotRequest(
cwd: string | undefined,
providers: AgentProvider[] | undefined,
): { cwd?: string; providers?: AgentProvider[] } {
return {
...providersSnapshotRequest(cwd),
...(providers ? { providers } : {}),
};
export function providersSnapshotQueryKey(serverId: string | null) {
return ["providersSnapshot", serverId] as const;
}
interface UseProvidersSnapshotResult {
@@ -59,23 +27,17 @@ interface UseProvidersSnapshotOptions {
export function useProvidersSnapshot(
serverId: string | null,
cwd?: string | null,
options: UseProvidersSnapshotOptions = {},
): UseProvidersSnapshotResult {
const queryClient = useQueryClient();
const client = useHostRuntimeClient(serverId ?? "");
const isConnected = useHostRuntimeIsConnected(serverId ?? "");
const enabled = options.enabled ?? true;
const normalizedCwd = cwd?.trim() || undefined;
const normalizedCwdKey = normalizeProvidersSnapshotCwdKey(normalizedCwd);
const supportsSnapshot = useSessionStore(
(state) => state.sessions[serverId ?? ""]?.serverInfo?.features?.providersSnapshot === true,
);
const queryKey = useMemo(
() => providersSnapshotQueryKey(serverId, normalizedCwdKey),
[normalizedCwdKey, serverId],
);
const queryKey = useMemo(() => providersSnapshotQueryKey(serverId), [serverId]);
const snapshotQuery = useQuery({
queryKey,
@@ -85,7 +47,7 @@ export function useProvidersSnapshot(
if (!client) {
throw new Error("Host is not connected");
}
return client.getProvidersSnapshot(providersSnapshotRequest(normalizedCwd));
return client.getProvidersSnapshot({});
},
});
@@ -94,9 +56,7 @@ export function useProvidersSnapshot(
if (!client) {
return;
}
await client.refreshProvidersSnapshot(
refreshProvidersSnapshotRequest(normalizedCwd, providers),
);
await client.refreshProvidersSnapshot(providers ? { providers } : {});
},
});
const { mutateAsync: refreshSnapshot, isPending: isRefreshing } = refreshMutation;
@@ -110,9 +70,6 @@ export function useProvidersSnapshot(
if (message.type !== "providers_snapshot_update") {
return;
}
if (!shouldApplyProvidersSnapshotUpdate(normalizedCwd, message.payload.cwd)) {
return;
}
queryClient.setQueryData(queryKey, {
entries: message.payload.entries,
generatedAt: message.payload.generatedAt,
@@ -127,17 +84,7 @@ export function useProvidersSnapshot(
});
}
});
}, [
client,
enabled,
isConnected,
normalizedCwd,
normalizedCwdKey,
queryClient,
queryKey,
serverId,
supportsSnapshot,
]);
}, [client, enabled, isConnected, queryClient, queryKey, serverId, supportsSnapshot]);
const refresh = useCallback(
async (providers?: AgentProvider[]) => {
@@ -145,10 +92,10 @@ export function useProvidersSnapshot(
return;
}
await refreshSnapshot(providers);
const snapshot = await client.getProvidersSnapshot(providersSnapshotRequest(normalizedCwd));
const snapshot = await client.getProvidersSnapshot({});
queryClient.setQueryData(queryKey, snapshot);
},
[client, normalizedCwd, queryClient, queryKey, refreshSnapshot],
[client, queryClient, queryKey, refreshSnapshot],
);
const refetchIfStale = useCallback(
@@ -184,16 +131,11 @@ export function useProvidersSnapshot(
};
}
export function prefetchProvidersSnapshot(
serverId: string,
client: DaemonClient,
cwd?: string | null,
): void {
const normalizedCwd = cwd?.trim() || undefined;
const queryKey = providersSnapshotQueryKey(serverId, normalizedCwd);
export function prefetchProvidersSnapshot(serverId: string, client: DaemonClient): void {
const queryKey = providersSnapshotQueryKey(serverId);
void singletonQueryClient.prefetchQuery({
queryKey,
staleTime: 60_000,
queryFn: () => client.getProvidersSnapshot(providersSnapshotRequest(normalizedCwd)),
queryFn: () => client.getProvidersSnapshot({}),
});
}