"use client"; import { createContext, useContext, useEffect, useRef, useState } from "react"; import { NATIVE_SUBSCRIPTIONS, type NativeSubscriptionProviderId, type NativeSubscriptionStatus } from "@/src/core"; import { Button } from "@/components/ui/button"; import { notifySettingsSaved } from "@/components/settings-signal"; import { ConnectionDialog, ConnectionFooter, ConnectionRow } from "./connection-controls"; import { ChatGPTConnection } from "./chatgpt-connection"; import { Toggle } from "./controls"; import { useNativeSubscriptionBridge } from "./subscription-client"; export const COPILOT_POLICY_NOTICE = "Pi's native Copilot sign-in may enable additional models whose policy is not yet configured. Models explicitly disabled by policy are not enabled. Organization restrictions still apply."; export const SUBSCRIPTION_BACKGROUND_HINT = "Allow LearnHarness helper agents and automatic follow-ups to use this connection."; export function subscriptionNotice(label: string, web = false): string { return `Your prompts, tool results, and relevant session context are sent to ${label} to run the work you select. Credentials are stored ${web ? "on the machine running LearnHarness" : "locally"} in an owner-only plaintext file. Provider terms, data-use policies, and plan limits apply. This connection never falls back to an API key.`; } export function subscriptionDisconnectCopy(label: string, web = false): string { return `Disconnect ${label} from LearnHarness? This stops work using this connection and removes its saved credentials from ${web ? "the machine running LearnHarness" : "this device"}. It does not cancel your subscription or revoke access at the provider. Saved model choices will remain unavailable until you reconnect or change them.`; } export function emptySubscriptionStatus(providerId: NativeSubscriptionProviderId): NativeSubscriptionStatus { return { providerId, available: false, connected: false, pending: false, allowBackgroundUse: true, connectionId: null, epoch: 0, authorityRevision: 0, revision: 0 }; } const Context = createContext({ statuses: [] as NativeSubscriptionStatus[], busy: false, open: (_providerId: NativeSubscriptionProviderId) => {} }); const CONTROL_ERROR = "The connection could not be changed. Try again."; export function NativeSubscriptionUIProvider({ children }: { children: React.ReactNode }) { const bridge = useNativeSubscriptionBridge(); const web = typeof window !== "undefined" && bridge !== window.lh?.subscriptions; const [statuses, setStatuses] = useState([]); const [dialog, setDialog] = useState(null); const [view, setView] = useState<"notice" | "connection" | "manage">("notice"); const [confirm, setConfirm] = useState(false), [background, setBackground] = useState(true), [backgroundDirty, setBackgroundDirty] = useState(false), [promptValue, setPromptValue] = useState(""); const [operation, setOperation] = useState<"login" | "control" | null>(null), [error, setError] = useState(null); const mounted = useRef(false), sequence = useRef(0), generation = useRef(0), statusesRef = useRef(statuses); const descriptor = NATIVE_SUBSCRIPTIONS.find((p) => p.providerId === dialog); const status = statuses.find((s) => s.providerId === dialog) ?? (dialog ? emptySubscriptionStatus(dialog) : undefined); const interaction = status?.interaction, prompt = interaction?.prompt; const management = view === "manage"; const busy = operation === "control" || status?.committing === true; async function refresh() { const request = ++sequence.current; try { const values = await bridge?.status(); if (!mounted.current || request !== sequence.current || !Array.isArray(values)) return; const accepted = NATIVE_SUBSCRIPTIONS.map(({ providerId }) => { const next = values.find((s) => s.providerId === providerId), old = statusesRef.current.find((s) => s.providerId === providerId); return next && (!old || next.revision >= old.revision) ? next : old ?? emptySubscriptionStatus(providerId); }); statusesRef.current = accepted; setStatuses(accepted); } catch { if (mounted.current && request === sequence.current) setError("The desktop connection is unavailable. Retry when the app is ready."); } } useEffect(() => { mounted.current = true; void refresh(); const off = bridge?.onChange(() => { void refresh(); notifySettingsSaved("keys"); }); return () => { mounted.current = false; generation.current++; off?.(); }; // The root-lifetime subscriber reads current state through refs. }, [bridge]); useEffect(() => { setPromptValue(""); }, [prompt?.id]); function open(providerId: NativeSubscriptionProviderId) { if (operation || statusesRef.current.some((s) => s.pending)) return; generation.current++; setError(null); setConfirm(false); setBackgroundDirty(false); const current = statusesRef.current.find((s) => s.providerId === providerId); setBackground(current?.allowBackgroundUse ?? true); setView(current?.connectionId ? "manage" : "notice"); setDialog(providerId); } async function control(action: () => Promise, close = false) { if (busy) return; const owner = ++generation.current; setOperation("control"); setError(null); try { await action(); await refresh(); if (mounted.current && owner === generation.current && close) setDialog(null); } catch { if (mounted.current && owner === generation.current) setError(CONTROL_ERROR); } finally { if (mounted.current && owner === generation.current) setOperation(null); } } function close() { if (!dialog || busy) return; if (confirm) { setConfirm(false); setError(null); return; } if ((status?.pending || operation === "login") && bridge) { void control(() => bridge.cancel(dialog), true); return; } generation.current++; setDialog(null); setError(null); } function beginSignIn() { if (operation || busy || !status) return; generation.current++; setError(null); setConfirm(false); setBackground(status.allowBackgroundUse); setBackgroundDirty(false); setView("notice"); } async function login() { if (!dialog || !bridge || operation || !status?.available) return; const owner = ++generation.current; setOperation("login"); setError(null); try { await bridge.login(dialog, backgroundDirty ? background : undefined); await refresh(); if (mounted.current && generation.current === owner) setDialog(null); } catch { if (mounted.current && generation.current === owner) setError("Sign-in did not complete. Try connecting again."); } finally { if (mounted.current && generation.current === owner) setOperation(null); } } return {children} {dialog && descriptor && status &&

{descriptor.label}

{confirm ? <>

Disconnect {descriptor.label}?

{subscriptionDisconnectCopy(descriptor.label, web)}

: view === "notice" ? <>

{subscriptionNotice(descriptor.label, web)}

{dialog === "github-copilot" &&

{COPILOT_POLICY_NOTICE}

} : <>
{ setBackground(enabled); if (management && bridge) void control(() => bridge.consent(dialog, enabled)); else setBackgroundDirty(true); }} /> Allow background use{SUBSCRIPTION_BACKGROUND_HINT}
{!status.available &&

Subscription sign-in requires the desktop app's private server connection. API-key connections remain available here.

} {view === "connection" && interaction &&
{interaction.progress &&

{interaction.progress}

} {interaction.userCode &&

Device code: {interaction.userCode}

} {interaction.browser && } {prompt &&
{ event.preventDefault(); if (bridge) void bridge.respond(dialog, interaction.attemptId, prompt.id, promptValue).catch(() => setError(CONTROL_ERROR)); }}> {prompt.type === "select" ? : setPromptValue(event.target.value)} className="w-full rounded-sm border border-border bg-background px-2 py-1.5" />}
}
} } {(error || status.error) &&

{error || status.error}

} setConfirm(true)}>Disconnect}> {!confirm && management && status.connectionId && status.available && !status.connected && !!status.error && !status.pending && <> {status.error && } } {confirm ? : view === "notice" ? : view === "connection" && }
}
; } export function NativeSubscriptionConnection({ providerId }: { providerId: NativeSubscriptionProviderId }) { const ui = useContext(Context), descriptor = NATIVE_SUBSCRIPTIONS.find((p) => p.providerId === providerId)!; const status = ui.statuses.find((s) => s.providerId === providerId) ?? emptySubscriptionStatus(providerId); return
s.pending)} onAction={() => ui.open(providerId)} /> {status.error &&

{status.error}

}
; } export function SubscriptionConnections({ setup = false }: { setup?: boolean }) { return
{NATIVE_SUBSCRIPTIONS.map((p) => )}
; } export function NativeSubscriptionBackgroundAccess({ provider }: { provider: string | undefined }) { const ui = useContext(Context), descriptor = NATIVE_SUBSCRIPTIONS.find((p) => p.logicalId === provider); const status = ui.statuses.find((s) => s.providerId === descriptor?.providerId); if (!descriptor || !status?.connected || status.allowBackgroundUse) return null; return ; }