mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Add `packages/app/src/constants/platform.ts` with canonical gates (isWeb, isNative, getIsElectron) and refactor all ~100 ad-hoc Platform.OS checks across 69 files to use shared imports. Fix sidebar hover crash on native (onPointerEnter → onHoverIn), fix tooltip to use breakpoint instead of platform detection, make sidebar action buttons always visible on native where hover doesn't work, and document the platform gating decision matrix in CLAUDE.md.
122 lines
3.8 KiB
TypeScript
122 lines
3.8 KiB
TypeScript
import { useCallback } from "react";
|
|
import { useSessionStore } from "@/stores/session-store";
|
|
import type { DaemonClient } from "@server/client/daemon-client";
|
|
import {
|
|
attachInitTimeout,
|
|
createInitDeferred,
|
|
getInitDeferred,
|
|
getInitKey,
|
|
rejectInitDeferred,
|
|
} from "@/utils/agent-initialization";
|
|
import { deriveInitialTimelineRequest } from "@/contexts/session-timeline-bootstrap-policy";
|
|
import { isWeb } from "@/constants/platform";
|
|
|
|
const INIT_TIMEOUT_MS = 5 * 60_000;
|
|
const NATIVE_INITIAL_TIMELINE_LIMIT = 200;
|
|
const UNBOUNDED_TIMELINE_LIMIT = 0;
|
|
|
|
function resolveInitialTimelineLimit(): number {
|
|
return isWeb ? UNBOUNDED_TIMELINE_LIMIT : NATIVE_INITIAL_TIMELINE_LIMIT;
|
|
}
|
|
|
|
export const __private__ = {
|
|
deriveInitialTimelineRequest,
|
|
resolveInitialTimelineLimit,
|
|
};
|
|
|
|
export function useAgentInitialization({
|
|
serverId,
|
|
client,
|
|
}: {
|
|
serverId: string;
|
|
client: DaemonClient | null;
|
|
}) {
|
|
const setInitializingAgents = useSessionStore((state) => state.setInitializingAgents);
|
|
const setAgentInitializing = useCallback(
|
|
(agentId: string, initializing: boolean) => {
|
|
setInitializingAgents(serverId, (prev) => {
|
|
if (prev.get(agentId) === initializing) {
|
|
return prev;
|
|
}
|
|
const next = new Map(prev);
|
|
next.set(agentId, initializing);
|
|
return next;
|
|
});
|
|
},
|
|
[serverId, setInitializingAgents],
|
|
);
|
|
|
|
const ensureAgentIsInitialized = useCallback(
|
|
(agentId: string): Promise<void> => {
|
|
const key = getInitKey(serverId, agentId);
|
|
const existing = getInitDeferred(key);
|
|
if (existing) {
|
|
return existing.promise;
|
|
}
|
|
|
|
const session = useSessionStore.getState().sessions[serverId];
|
|
const cursor = session?.agentTimelineCursor.get(agentId);
|
|
const initialTimelineLimit = resolveInitialTimelineLimit();
|
|
const hasAuthoritativeHistory =
|
|
session?.agentAuthoritativeHistoryApplied.get(agentId) === true;
|
|
const timelineRequest = deriveInitialTimelineRequest({
|
|
cursor: cursor ? { epoch: cursor.epoch, seq: cursor.endSeq } : null,
|
|
hasAuthoritativeHistory,
|
|
initialTimelineLimit,
|
|
});
|
|
const initRequestDirection = timelineRequest.direction === "after" ? "after" : "tail";
|
|
|
|
const deferred = createInitDeferred(key, initRequestDirection);
|
|
const timeoutId = setTimeout(() => {
|
|
setAgentInitializing(agentId, false);
|
|
rejectInitDeferred(
|
|
key,
|
|
new Error(`History sync timed out after ${Math.round(INIT_TIMEOUT_MS / 1000)}s`),
|
|
);
|
|
}, INIT_TIMEOUT_MS);
|
|
attachInitTimeout(key, timeoutId);
|
|
|
|
setAgentInitializing(agentId, true);
|
|
|
|
if (!client) {
|
|
setAgentInitializing(agentId, false);
|
|
rejectInitDeferred(key, new Error("Host is not connected"));
|
|
return deferred.promise;
|
|
}
|
|
|
|
client.fetchAgentTimeline(agentId, timelineRequest).catch((error) => {
|
|
setAgentInitializing(agentId, false);
|
|
rejectInitDeferred(key, error instanceof Error ? error : new Error(String(error)));
|
|
});
|
|
|
|
return deferred.promise;
|
|
},
|
|
[client, serverId, setAgentInitializing],
|
|
);
|
|
|
|
const refreshAgent = useCallback(
|
|
async (agentId: string) => {
|
|
if (!client) {
|
|
throw new Error("Host is not connected");
|
|
}
|
|
setAgentInitializing(agentId, true);
|
|
|
|
try {
|
|
await client.refreshAgent(agentId);
|
|
const initialTimelineLimit = resolveInitialTimelineLimit();
|
|
await client.fetchAgentTimeline(agentId, {
|
|
direction: "tail",
|
|
limit: initialTimelineLimit,
|
|
projection: "canonical",
|
|
});
|
|
} catch (error) {
|
|
setAgentInitializing(agentId, false);
|
|
throw error;
|
|
}
|
|
},
|
|
[client, setAgentInitializing],
|
|
);
|
|
|
|
return { ensureAgentIsInitialized, refreshAgent };
|
|
}
|