fix(app): load History after a browser reload

Consume host connection statuses in History instead of a version-only dependency that the React Compiler removes. Keep saved sessions reachable when the host connects after the screen mounts.
This commit is contained in:
paseo-bot[bot]
2026-11-07 16:47:56 +00:00
parent ab8e1e61ff
commit d9de2f860e
2 changed files with 24 additions and 12 deletions
@@ -60,6 +60,15 @@ async function verifyChronologicalFiltering(page: Page): Promise<void> {
});
}
async function verifyHistorySurvivesReload(page: Page): Promise<void> {
await test.step("reloads History without visiting a workspace to reconnect", async () => {
await expectVisibleTitles(page, [TITLES.unbilled, TITLES.billing, TITLES.terminal]);
await page.reload();
await expectVisibleTitles(page, [TITLES.unbilled, TITLES.billing, TITLES.terminal]);
await expect(page.getByTestId("sessions-host-errors")).toBeHidden();
});
}
async function verifyRecencyBeforeMatchStrength(page: Page): Promise<void> {
await test.step("keeps newer partial matches before older stronger matches", async () => {
await search(page, `${NONCE} bill`);
@@ -151,6 +160,8 @@ test.describe("History search", () => {
await verifyChronologicalFiltering(page);
await verifyHistorySurvivesReload(page);
await verifyRecencyBeforeMatchStrength(page);
await verifyExactAndTypoHighlights(page);
+13 -12
View File
@@ -4,10 +4,14 @@ import type {
FetchAgentHistoryPageInfo,
} from "@getpaseo/client/internal/daemon-client";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useCallback, useMemo, useSyncExternalStore } from "react";
import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
import { getHostRuntimeStore, isHostRuntimeConnected, useHosts } from "@/runtime/host-runtime";
import {
getHostRuntimeStore,
useHostRuntimeConnectionStatuses,
useHosts,
} from "@/runtime/host-runtime";
import { useSessionStore } from "@/stores/session-store";
import { buildAgentDirectoryState } from "@/utils/agent-directory-sync";
import { agentHistoryQueryKey, allAgentHistoryQueryKey } from "./agent-history-query-key";
@@ -222,32 +226,29 @@ export function useAgentHistory(options: {
const { t } = useTranslation();
const daemons = useHosts();
const runtime = getHostRuntimeStore();
const runtimeVersion = useSyncExternalStore(
(onStoreChange) => runtime.subscribeAll(onStoreChange),
() => runtime.getVersion(),
() => runtime.getVersion(),
);
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 serverIds = useMemo(
() => (serverId ? [serverId] : daemons.map((daemon) => daemon.serverId)),
[daemons, serverId],
);
const connectionStatuses = useHostRuntimeConnectionStatuses(serverIds);
// A host the user asked about splits two ways: one this fetch can reach, and
// one whose sessions will be missing from the answer. Both come out of here,
// because dropping the unreachable ones silently is what lets the list — and
// worse, "No sessions match" — overstate what was actually searched.
const { targetHosts, unreachableHosts } = useMemo(() => {
void runtimeVersion;
const serverLabelById = new Map(daemons.map((daemon) => [daemon.serverId, daemon.label]));
const serverIds = serverId ? [serverId] : daemons.map((daemon) => daemon.serverId);
const hosts: AgentHistoryHost[] = [];
const unreachable: AgentHistoryHostError[] = [];
for (const targetServerId of serverIds) {
const snapshot = runtime.getSnapshot(targetServerId);
const client = runtime.getClient(targetServerId);
const serverName = serverLabelById.get(targetServerId) ?? targetServerId;
if (!client || !isHostRuntimeConnected(snapshot)) {
if (!client || connectionStatuses.get(targetServerId) !== "online") {
unreachable.push({ serverId: targetServerId, serverName });
continue;
}
@@ -255,7 +256,7 @@ export function useAgentHistory(options: {
}
return { targetHosts: hosts, unreachableHosts: unreachable };
}, [daemons, runtime, runtimeVersion, serverId]);
}, [daemons, runtime, connectionStatuses, serverIds]);
const targetServerIds = useMemo(() => targetHosts.map((host) => host.serverId), [targetHosts]);
// One gate, checked before the field is offered: a fleet where any host
// predates search has no search, rather than a list that silently omits that