Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/adapters/opencode-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof fetch> })

Expand Down
11 changes: 11 additions & 0 deletions src/main/agent-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 9 additions & 0 deletions src/main/enterprise-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
return this.fetchAndStoreAiGatewayVirtualKey()
}

private async fetchAndStoreAiGatewayVirtualKey(): Promise<void> {
const result = await this.apiRequest('GET', '/api/20x/ai-gateway/virtual-key') as EnterpriseAiGatewayVirtualKeyResponse

Expand Down
Loading