fix(googlechat): isolate auth transports

This commit is contained in:
Vincent Koc
2026-05-04 00:48:16 -07:00
parent e622223bcd
commit b31c001a2b
3 changed files with 19 additions and 18 deletions

View File

@@ -61,6 +61,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Control UI/Talk: make failed Talk startup errors dismissable and clear the stale Talk error state when dismissed, so missing realtime voice provider configuration does not leave a permanent chat banner. Fixes #77071. Thanks @ijoshdavis.
- Google Chat: create an isolated Google auth transport per auth client, so google-auth-library interceptor mutations do not accumulate across webhook verification and access-token clients. Thanks @vincentkoc.
- Control UI/performance: cap long-task and long-animation-frame diagnostics in the shared event log, so slow-render telemetry does not evict gateway/plugin events from the Debug and Overview views. Thanks @vincentkoc.
- Web fetch: late-bind `web_fetch` config and provider fallback metadata from the active runtime snapshot, matching `web_search` so long-lived tools do not use stale fetch provider settings. Thanks @vincentkoc.
- Discord: clear stale startup probe bot/application status when the async bot probe throws, not just when it returns a degraded probe result. Thanks @vincentkoc.

View File

@@ -359,6 +359,18 @@ describe("googlechat google auth runtime", () => {
}
});
it("keeps auth transports isolated from google-auth interceptor mutations", async () => {
const first = await getGoogleAuthTransport();
const second = await getGoogleAuthTransport();
expect(first).not.toBe(second);
expect(mocks.gaxiosCtor).toHaveBeenCalledTimes(2);
expect(first.interceptors.request.add).toHaveBeenCalledOnce();
expect(first.interceptors.response.add).toHaveBeenCalledOnce();
expect(second.interceptors.request.add).toHaveBeenCalledOnce();
expect(second.interceptors.response.add).toHaveBeenCalledOnce();
});
it("normalizes Google auth request headers before upstream interceptors run", async () => {
const config = {
headers: { "x-test": "1" },

View File

@@ -71,7 +71,6 @@ const MAX_GOOGLE_AUTH_RESPONSE_BYTES = 1024 * 1024;
const MAX_GOOGLE_CHAT_SERVICE_ACCOUNT_FILE_BYTES = 64 * 1024;
let googleAuthRuntimePromise: Promise<GoogleAuthRuntime> | null = null;
let googleAuthTransportPromise: Promise<GoogleAuthTransport> | null = null;
function normalizeGoogleAuthPreparedRequestHeaders<T extends GoogleAuthRequestWithUnknownHeaders>(
config: T,
@@ -536,22 +535,12 @@ export async function loadGoogleAuthRuntime(): Promise<GoogleAuthRuntime> {
}
export async function getGoogleAuthTransport(): Promise<GoogleAuthTransport> {
if (!googleAuthTransportPromise) {
googleAuthTransportPromise = (async () => {
try {
const { Gaxios } = await loadGoogleAuthRuntime();
return installGoogleAuthHeaderCompatibilityInterceptor(
new Gaxios({
fetchImplementation: createGoogleAuthFetch(),
}),
);
} catch (error) {
googleAuthTransportPromise = null;
throw error;
}
})();
}
return await googleAuthTransportPromise;
const { Gaxios } = await loadGoogleAuthRuntime();
return installGoogleAuthHeaderCompatibilityInterceptor(
new Gaxios({
fetchImplementation: createGoogleAuthFetch(),
}),
);
}
export async function resolveValidatedGoogleChatCredentials(
@@ -570,7 +559,6 @@ export async function resolveValidatedGoogleChatCredentials(
export const __testing = {
resetGoogleAuthRuntimeForTests(): void {
googleAuthRuntimePromise = null;
googleAuthTransportPromise = null;
},
normalizeGoogleAuthPreparedRequestHeaders,
normalizeGoogleAuthResponseHeaders,