diff --git a/src/agents/gigachat-auth.ts b/src/agents/gigachat-auth.ts index 8378062bf41..b4ffce7526a 100644 --- a/src/agents/gigachat-auth.ts +++ b/src/agents/gigachat-auth.ts @@ -5,10 +5,14 @@ export type GigachatAuthMetadata = Record | undefined; export function resolveGigachatAuthProfileMetadata( store: Pick, authProfileId?: string, + options?: { + allowDefaultProfileFallback?: boolean; + }, ): GigachatAuthMetadata { - const profileIds = [authProfileId?.trim(), "gigachat:default"].filter( - (profileId): profileId is string => Boolean(profileId), - ); + const profileIds = [ + authProfileId?.trim(), + options?.allowDefaultProfileFallback === false ? undefined : "gigachat:default", + ].filter((profileId): profileId is string => Boolean(profileId)); for (const profileId of profileIds) { const credential = store.profiles[profileId]; if (credential?.type === "api_key" && credential.provider === "gigachat") { @@ -39,7 +43,7 @@ export function resolveGigachatAuthMode(params: { return metadataAuthMode; } - if (!params.authProfileId?.trim() && looksLikeGigachatBasicCredentials(params.apiKey)) { + if (looksLikeGigachatBasicCredentials(params.apiKey)) { return "basic"; } diff --git a/src/agents/models-config.providers.gigachat.test.ts b/src/agents/models-config.providers.gigachat.test.ts index 9d4814fbf4f..6dc3b9f78cc 100644 --- a/src/agents/models-config.providers.gigachat.test.ts +++ b/src/agents/models-config.providers.gigachat.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { GIGACHAT_BASE_URL, GIGACHAT_BASIC_BASE_URL } from "../commands/onboard-auth.models.js"; +import { resolveGigachatAuthProfileMetadata } from "./gigachat-auth.js"; import { resolveImplicitGigachatBaseUrl } from "./models-config.providers.js"; describe("GigaChat implicit provider", () => { @@ -29,4 +30,30 @@ describe("GigaChat implicit provider", () => { }), ).toBe("https://preview.gigachat.example/api/v1"); }); + + it("does not inherit stale default-profile metadata for auth-profile-less credentials", async () => { + const metadata = resolveGigachatAuthProfileMetadata( + { + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { + authMode: "basic", + scope: "GIGACHAT_API_B2B", + }, + }, + }, + }, + undefined, + { allowDefaultProfileFallback: false }, + ); + + expect( + resolveImplicitGigachatBaseUrl({ + metadata, + apiKey: "oauth:credential:with:colon", + }), + ).toBe(GIGACHAT_BASE_URL); + }); }); diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 2d84f7f1b04..06e742d29f7 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -737,7 +737,11 @@ function resolveImplicitGigachatProvider(ctx: ImplicitProviderContext): Provider if (!auth.apiKey) { return null; } - const metadata = resolveGigachatAuthProfileMetadata(ctx.authStore, auth.profileId); + const metadata = resolveGigachatAuthProfileMetadata(ctx.authStore, auth.profileId, { + // Env-backed GIGACHAT_CREDENTIALS has no profile id, so do not inherit + // stale auth-mode metadata from a stored default profile. + allowDefaultProfileFallback: Boolean(auth.profileId?.trim()), + }); return buildGigachatProvider({ apiKey: auth.apiKey, diff --git a/src/agents/pi-embedded-runner/compact.hooks.test.ts b/src/agents/pi-embedded-runner/compact.hooks.test.ts index 0ebe414ed66..0d855004ac3 100644 --- a/src/agents/pi-embedded-runner/compact.hooks.test.ts +++ b/src/agents/pi-embedded-runner/compact.hooks.test.ts @@ -1064,6 +1064,66 @@ describe("compactEmbeddedPiSessionDirect hooks", () => { expect(result.ok, result.reason).toBe(true); }); + + it("does not inherit stale GigaChat metadata for env-backed OAuth credentials", async () => { + resolveModelMock.mockReturnValue({ + model: { + provider: "gigachat", + api: "openai-completions", + id: "GigaChat-2-Max", + input: ["text"], + baseUrl: "https://gigachat.devices.sberbank.ru/api/v1", + }, + error: null, + authStorage: { setRuntimeApiKey: vi.fn() }, + modelRegistry: {}, + } as never); + vi.mocked(getApiKeyForModel).mockResolvedValueOnce({ + apiKey: "oauth:credential:with:colon", + mode: "api-key", + source: "env: GIGACHAT_CREDENTIALS", + }); + ensureAuthProfileStoreMock.mockReturnValue({ + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { + authMode: "basic", + insecureTls: "true", + scope: "GIGACHAT_API_B2B", + }, + }, + }, + }); + sessionCompactImpl.mockImplementation(async () => { + expect(createGigachatStreamFnMock).toHaveBeenCalledWith({ + baseUrl: "https://gigachat.devices.sberbank.ru/api/v1", + authMode: "oauth", + insecureTls: false, + scope: undefined, + }); + return { + summary: "summary", + firstKeptEntryId: "entry-1", + tokensBefore: 120, + details: { ok: true }, + }; + }); + + const result = await compactEmbeddedPiSessionDirect({ + sessionId: "session-1", + sessionKey: "agent:main:session-1", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp", + config: gigachatTestConfig(), + provider: "gigachat", + model: "GigaChat-2-Max", + customInstructions: "focus on decisions", + }); + + expect(result.ok, result.reason).toBe(true); + }); }); describe("compactEmbeddedPiSession hooks (ownsCompaction engine)", () => { diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 5bb92b16bde..e5c802b64d9 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -856,6 +856,9 @@ export async function compactEmbeddedPiSessionDirect( const gigachatMeta = resolveGigachatAuthProfileMetadata( gigachatStore, resolvedGigachatProfileId, + { + allowDefaultProfileFallback: Boolean(resolvedGigachatProfileId), + }, ); session.agent.streamFn = createGigachatStreamFn({ diff --git a/src/agents/pi-embedded-runner/run/attempt.test.ts b/src/agents/pi-embedded-runner/run/attempt.test.ts index a78f42e9920..99b81736b10 100644 --- a/src/agents/pi-embedded-runner/run/attempt.test.ts +++ b/src/agents/pi-embedded-runner/run/attempt.test.ts @@ -204,6 +204,24 @@ describe("resolveGigachatAuthProfileMetadata", () => { ), ).toEqual({ scope: "GIGACHAT_API_PERS" }); }); + + it("does not inherit the default GigaChat profile when fallback is disabled", () => { + expect( + resolveGigachatAuthProfileMetadata( + { + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { scope: "GIGACHAT_API_B2B", insecureTls: "true" }, + }, + }, + }, + undefined, + { allowDefaultProfileFallback: false }, + ), + ).toBeUndefined(); + }); }); describe("resolveGigachatAuthMode", () => { @@ -223,6 +241,15 @@ describe("resolveGigachatAuthMode", () => { }), ).toBe("oauth"); }); + + it("infers basic auth for single-separator stored profile credentials without metadata", () => { + expect( + resolveGigachatAuthMode({ + apiKey: "user:password", + authProfileId: "gigachat:default", + }), + ).toBe("basic"); + }); }); describe("composeSystemPromptWithHookContext", () => { diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index e15b05d8844..1be3df5903f 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1982,6 +1982,9 @@ export async function runEmbeddedAttempt( const gigachatMeta = resolveGigachatAuthProfileMetadata( gigachatStore, params.authProfileId, + { + allowDefaultProfileFallback: Boolean(params.authProfileId?.trim()), + }, ); const gigachatApiKey = await params.authStorage.getApiKey(params.provider);