From 253885d9478a14b37be8ddb6d157dab82f0b41e2 Mon Sep 17 00:00:00 2001 From: dimavedenyapin Date: Mon, 27 Apr 2026 14:19:28 +0700 Subject: [PATCH 1/3] fix: expose public method to refresh AI gateway virtual key on demand Add refreshAiGatewayVirtualKey() to EnterpriseAuth so callers (IPC handlers, adapter error recovery) can trigger a key refresh when LiteLLM returns "Invalid proxy server token" during a session. Co-Authored-By: Claude Opus 4.6 --- src/main/enterprise-auth.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/enterprise-auth.ts b/src/main/enterprise-auth.ts index da33085..4de93c4 100644 --- a/src/main/enterprise-auth.ts +++ b/src/main/enterprise-auth.ts @@ -654,6 +654,15 @@ export class EnterpriseAuth { return jwt } + /** + * Re-fetch and store the AI gateway virtual key from the backend. + * Exposed publicly so callers (e.g. IPC handlers, adapter error recovery) + * can trigger a key refresh when LiteLLM returns "Invalid proxy server token". + */ + async refreshAiGatewayVirtualKey(): Promise { + return this.fetchAndStoreAiGatewayVirtualKey() + } + private async fetchAndStoreAiGatewayVirtualKey(): Promise { const result = await this.apiRequest('GET', '/api/20x/ai-gateway/virtual-key') as EnterpriseAiGatewayVirtualKeyResponse From c74e0d781df79e727b387573e73d34a07757f28f Mon Sep 17 00:00:00 2001 From: dimavedenyapin Date: Mon, 27 Apr 2026 15:08:05 +0700 Subject: [PATCH 2/3] fix: refresh AI gateway virtual key before each agent session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an admin rotates the user's virtual key (via plan removal + re- assignment), the desktop's local SQLite cache still holds the old key. Now the agent-manager calls refreshAiGatewayVirtualKey() before each session start so the adapter always builds its provider config with the latest key from the backend. Best-effort — falls back to the cached key if the refresh fails (offline, server down). Co-Authored-By: Claude Opus 4.6 --- src/main/agent-manager.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/agent-manager.ts b/src/main/agent-manager.ts index 462cbac..d93f3e5 100644 --- a/src/main/agent-manager.ts +++ b/src/main/agent-manager.ts @@ -1073,6 +1073,17 @@ export class AgentManager extends EventEmitter { } await yieldEL() + // Refresh the AI gateway virtual key before building the provider config + // so the adapter gets the latest key from the backend (handles key rotation, + // admin plan changes, etc.). Best-effort — fall back to the cached key. + if (this.enterpriseAuth) { + try { + await this.enterpriseAuth.refreshAiGatewayVirtualKey() + } catch (err) { + console.warn('[AgentManager] AI gateway key refresh failed (will use cached key):', err) + } + } + // Initialize adapter console.log(`[AgentManager] startAdapterSession: agent=${agent.name}, coding_agent=${agent.config?.coding_agent || 'opencode'}, model=${agent.config?.model}, adapter=${adapter.constructor.name}`) await adapter.initialize() From ed368baf01c991ee8a523df399c8394587f7ff16 Mon Sep 17 00:00:00 2001 From: dimavedenyapin Date: Mon, 27 Apr 2026 15:32:52 +0700 Subject: [PATCH 3/3] fix: push refreshed AI gateway config to already-running OpenCode server ensureServerRunning() early-returns when the server is already running at the target URL, which skips the initial config push. This means rotated provider credentials (e.g. a new LiteLLM virtual key after plan re- assignment) never reach the running server. Now createSession() always calls pushMergedConfigToClient() after ensureServerRunning(), so the OpenCode provider config reflects the latest key fetched by refreshAiGatewayVirtualKey() in agent-manager. Co-Authored-By: Claude Opus 4.6 --- src/main/adapters/opencode-adapter.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/adapters/opencode-adapter.ts b/src/main/adapters/opencode-adapter.ts index c6cdfe3..57763a1 100644 --- a/src/main/adapters/opencode-adapter.ts +++ b/src/main/adapters/opencode-adapter.ts @@ -444,6 +444,18 @@ export class OpencodeAdapter implements CodingAgentAdapter { await this.ensureServerRunning(config.serverUrl || DEFAULT_SERVER_URL) + // Always push the latest merged config (incl. refreshed AI gateway key) + // to the running server. ensureServerRunning skips the config push when + // the server is already running at the same URL, but provider credentials + // may have been rotated since the server was started. + if (this.sharedClient) { + try { + await this.pushMergedConfigToClient(this.sharedClient) + } catch { + // pushMergedConfigToClient already logs details; proceed with cached config + } + } + const baseUrl = this.serverUrl || config.serverUrl || DEFAULT_SERVER_URL const ocClient = OpenCodeSDK!.createOpencodeClient({ baseUrl, fetch: noTimeoutFetch as unknown as (request: Request) => ReturnType })