function normalizeText(value: unknown): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed ? trimmed : null; } function hashText(value: string): string { let hash = 0; for (let index = 0; index < value.length; index += 1) { hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0; } return Math.abs(hash).toString(16).padStart(8, "0"); } function boundedIdentity(prefix: string, raw: string): string { const normalizedPrefix = prefix.trim().replace(/:+$/, ""); const normalizedRaw = raw.trim(); if (!normalizedRaw) { return `${normalizedPrefix}:${hashText(prefix)}`; } const candidate = `${normalizedPrefix}:${normalizedRaw}`; if (candidate.length <= 120) { return candidate; } return `${normalizedPrefix}:${hashText(normalizedRaw)}`; } function normalizeURLPath(pathname: string): string { const normalized = pathname.replace(/\/+$/, ""); return normalized === "/" ? "" : normalized; } function safeParseURL(input: string): URL | null { try { return new URL(input); } catch { return null; } } function genericAIConversationPath(pathname: string): boolean { return /^\/(?:chat|c|conversation)(?:\/|$)/i.test(pathname); } export interface GenericAIPageState { displayName: string | null; avatarUrl: string | null; fingerprint: string | null; credentialFingerprint: string | null; currentPath: string | null; strongAuthSignalCount: number; loginSignalCount: number; loggedOutSignalCount: number; challengeSignalCount: number; } export function genericAIPageLooksAuthenticated( currentURL: string, pageState: GenericAIPageState | null, ): boolean { const current = safeParseURL(currentURL); const currentPath = normalizeURLPath(current?.pathname ?? pageState?.currentPath ?? ""); const strongAuthSignalCount = pageState?.strongAuthSignalCount ?? 0; const loginSignalCount = pageState?.loginSignalCount ?? 0; const loggedOutSignalCount = pageState?.loggedOutSignalCount ?? 0; const challengeSignalCount = pageState?.challengeSignalCount ?? 0; const hasCredentialFingerprint = Boolean(normalizeText(pageState?.credentialFingerprint)); const onConversationPath = genericAIConversationPath(currentPath); if (challengeSignalCount > 0) { return false; } if (loggedOutSignalCount > 0) { return false; } if (loginSignalCount > 0 && strongAuthSignalCount === 0 && !onConversationPath && !hasCredentialFingerprint) { return false; } if (loginSignalCount > 0) { return (strongAuthSignalCount >= 2 || hasCredentialFingerprint) && !onConversationPath; } return strongAuthSignalCount > 0 || onConversationPath || hasCredentialFingerprint; } export function buildGenericAIPageFingerprintFallback( platformId: string, pageState?: GenericAIPageState | null, ): string | null { const credentialFingerprint = normalizeText(pageState?.credentialFingerprint); if (credentialFingerprint) { return boundedIdentity(`${platformId}-session`, credentialFingerprint); } const fallbackFingerprint = normalizeText(pageState?.fingerprint); if (!fallbackFingerprint) { return null; } const strongAuthSignalCount = pageState?.strongAuthSignalCount ?? 0; if (strongAuthSignalCount <= 0) { return null; } return boundedIdentity(`${platformId}-page`, fallbackFingerprint); }