Unify ChatGPT background use and account management

This commit is contained in:
amosblomqvist
2026-10-24 15:28:26 +03:00
parent 8691470d03
commit 42abeb65ae
19 changed files with 687 additions and 416 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ interface BridgeState {
pending: Map<string, Pending>; controllers: Map<AbortController, boolean>; listeners: Set<() => void>; counter: number; catalogRequest: number;
}
const key = Symbol.for("lh.chatgpt.bridge");
const unavailable = (): ChatGPTStatus => ({ available: false, pending: false, activeId: null, accounts: [], error: null });
const unavailable = (): ChatGPTStatus => ({ available: false, pending: false, allowBackgroundUse: true, activeId: null, accounts: [], error: null });
const validStamp = (s: Stamp) => s && [s.epoch, s.authorityRevision, s.revision].every((v) => Number.isSafeInteger(v) && v >= 0);
function bridge(): BridgeState {
const global = globalThis as typeof globalThis & { [key]?: BridgeState };
@@ -69,7 +69,7 @@ export function chatGPTRequest<T>(op: "status" | "models" | "lease", fields: Rec
}
export function chatGPTStatus(): ChatGPTStatus { return bridge().status; }
export function chatGPTConnected(): boolean { const s = chatGPTStatus(); return s.available && s.accounts.some((a) => a.id === s.activeId && a.connected && a.planEnabled === true); }
export function chatGPTBackgroundAllowed(): boolean { const s = chatGPTStatus(); return chatGPTConnected() && s.accounts.some((a) => a.id === s.activeId && a.background); }
export function chatGPTBackgroundAllowed(): boolean { const s = chatGPTStatus(); return chatGPTConnected() && s.allowBackgroundUse === true; }
export function onChatGPTChange(listener: () => void): () => void { bridge().listeners.add(listener); return () => { bridge().listeners.delete(listener); }; }
export function chatGPTModels(): Model<"openai-responses">[] { return bridge().models; }
export function chatGPTModel(id: string): Model<"openai-responses"> | undefined { return chatGPTModels().find((m) => m.id === id); }
+4 -4
View File
@@ -8,12 +8,12 @@ import type { AdmissionScope, ChatGPTDispatchLifecycle } from "./chatgpt-request
const USAGE = "https://chatgpt.com/settings/usage";
export function chatGPTStreamError(message: string): string {
if (/subscription_sharing_usage_limit_exceeded|quota|429/.test(message)) return `ChatGPT plan usage limit reached. Manage usage at ${USAGE}. No API-key fallback was used.`;
if (/scope|authorization_context|permission/.test(message)) return "ChatGPT plan permission is missing. Reconnect in Connections and authorize plan use. No API-key fallback was used.";
if (/expired|invalid_grant|401|invalid_user/.test(message)) return "Your ChatGPT session expired. Reconnect in Connections. No API-key fallback was used.";
if (/background|Background/.test(message)) return "Background use is off for this ChatGPT account. Enable Allow background use in Connections to run this work. No API-key fallback was used.";
if (/scope|authorization_context|permission/.test(message)) return "ChatGPT plan permission is missing. Reconnect in Providers and authorize plan use. No API-key fallback was used.";
if (/expired|invalid_grant|401|invalid_user/.test(message)) return "Your ChatGPT session expired. Reconnect in Providers. No API-key fallback was used.";
if (/background|Background/.test(message)) return "ChatGPT background use is off in this app. Enable Allow background use for all ChatGPT accounts in Providers to run this work. No API-key fallback was used.";
if (/eligible|403/.test(message)) return "This ChatGPT account or workspace does not permit this request. Check ChatGPT settings; reconnecting may not change eligibility.";
if (/cancel|abort|closed|disconnect/i.test(message)) return "ChatGPT request cancelled or connection closed. No API-key fallback was used.";
if (/catalog|model/.test(message)) return "The selected model is unavailable for this ChatGPT account. Refresh Connections and explicitly choose an available model. No API-key fallback was used.";
if (/catalog|model/.test(message)) return "The selected model is unavailable for this ChatGPT account. Refresh Providers and explicitly choose an available model. No API-key fallback was used.";
if (/incomplete|terminal|interrupted|stop reason/.test(message)) return "ChatGPT returned an incomplete response. Retry when ready; no API-key fallback was used.";
return "ChatGPT request failed. Check your connection and ChatGPT usage, then retry. No API-key fallback was used.";
}
+6 -2
View File
@@ -2,8 +2,10 @@
export interface ChatGPTStatus {
available: boolean;
pending: boolean;
/** App-wide ChatGPT background permission, independent of account selection. */
allowBackgroundUse: boolean;
activeId: string | null;
accounts: { id: string; email: string; registration: string; connected: boolean; background: boolean; planEnabled?: boolean }[];
accounts: { id: string; email: string; registration: string; connected: boolean; planEnabled?: boolean }[];
error: string | null;
catalogError?: string | null;
}
@@ -12,7 +14,9 @@ export interface ChatGPTControlBridge {
login(id?: string): Promise<ChatGPTStatus>;
cancel(): Promise<ChatGPTStatus>;
select(id: string): Promise<ChatGPTStatus>;
/** Explicitly forget every local registration/credential for this verified identity. */
disconnect(id: string): Promise<ChatGPTStatus>;
logout(id: string): Promise<ChatGPTStatus>;
consent(id: string, enabled: boolean): Promise<ChatGPTStatus>;
consent(enabled: boolean): Promise<ChatGPTStatus>;
onChange(callback: () => void): () => void;
}
+1 -1
View File
@@ -209,7 +209,7 @@ export async function handleChatInput(
if (conversation.provider === "chatgpt") {
await refreshConnectionCatalog();
if (input.type === "steers" && !chatGPTBackgroundAllowed()) {
return Response.json({ error: "Background use is off for this ChatGPT account. Automatic follow-up remains deferred in its queue." }, { status: 403 });
return Response.json({ error: "ChatGPT background use is off in this app. Automatic follow-up remains deferred in its queue." }, { status: 403 });
}
}
if (!providerKeyPresent(conversation.provider)) {