diff --git a/src/agents/model-auth.profiles.test.ts b/src/agents/model-auth.profiles.test.ts index ca509f632d4..d149ca663e7 100644 --- a/src/agents/model-auth.profiles.test.ts +++ b/src/agents/model-auth.profiles.test.ts @@ -467,4 +467,17 @@ describe("getApiKeyForModel", () => { }, ); }); + + it("resolveEnvApiKey('gigachat') ignores password-only basic-auth envs", async () => { + await withEnvAsync( + { + GIGACHAT_CREDENTIALS: undefined, + GIGACHAT_PASSWORD: "gigachat-basic-password", + }, + async () => { + const resolved = resolveEnvApiKey("gigachat"); + expect(resolved).toBeUndefined(); + }, + ); + }); }); diff --git a/src/agents/pi-embedded-runner/run/attempt.test.ts b/src/agents/pi-embedded-runner/run/attempt.test.ts index 1953099cf7b..be8eafaccec 100644 --- a/src/agents/pi-embedded-runner/run/attempt.test.ts +++ b/src/agents/pi-embedded-runner/run/attempt.test.ts @@ -6,6 +6,7 @@ import { composeSystemPromptWithHookContext, isOllamaCompatProvider, prependSystemPromptAddition, + resolveGigachatAuthProfileMetadata, resolveAttemptFsWorkspaceOnly, resolveOllamaCompatNumCtxEnabled, resolvePromptBuildHookResult, @@ -106,6 +107,69 @@ describe("resolvePromptBuildHookResult", () => { }); }); +describe("resolveGigachatAuthProfileMetadata", () => { + it("prefers the active GigaChat auth profile metadata over the default profile", () => { + expect( + resolveGigachatAuthProfileMetadata( + { + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { scope: "GIGACHAT_API_PERS", insecureTls: "false" }, + }, + "gigachat:business": { + type: "api_key", + provider: "gigachat", + metadata: { scope: "GIGACHAT_API_B2B", insecureTls: "true" }, + }, + }, + }, + "gigachat:business", + ), + ).toEqual({ scope: "GIGACHAT_API_B2B", insecureTls: "true" }); + }); + + it("falls back to the default GigaChat profile metadata when the active profile is absent", () => { + expect( + resolveGigachatAuthProfileMetadata( + { + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { scope: "GIGACHAT_API_PERS", insecureTls: "false" }, + }, + }, + }, + "gigachat:business", + ), + ).toEqual({ scope: "GIGACHAT_API_PERS", insecureTls: "false" }); + }); + + it("ignores non-GigaChat active profiles when resolving metadata", () => { + expect( + resolveGigachatAuthProfileMetadata( + { + profiles: { + "gigachat:default": { + type: "api_key", + provider: "gigachat", + metadata: { scope: "GIGACHAT_API_PERS" }, + }, + "openai:p1": { + type: "api_key", + provider: "openai", + metadata: { scope: "not-gigachat" }, + }, + }, + }, + "openai:p1", + ), + ).toEqual({ scope: "GIGACHAT_API_PERS" }); + }); +}); + describe("composeSystemPromptWithHookContext", () => { it("returns undefined when no hook system context is provided", () => { expect(composeSystemPromptWithHookContext({ baseSystemPrompt: "base" })).toBeUndefined(); diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 9c153058ac6..752eab3e7a8 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -35,6 +35,7 @@ import { resolveOpenClawAgentDir } from "../../agent-paths.js"; import { resolveSessionAgentIds } from "../../agent-scope.js"; import { createAnthropicPayloadLogger } from "../../anthropic-payload-log.js"; import { ensureAuthProfileStore } from "../../auth-profiles.js"; +import type { ApiKeyCredential, AuthProfileStore } from "../../auth-profiles.js"; import { analyzeBootstrapBudget, buildBootstrapPromptWarning, @@ -218,6 +219,25 @@ function createYieldAbortedResponse(model: { api?: string; provider?: string; id }; } +export function resolveGigachatAuthProfileMetadata( + store: Pick, + authProfileId?: string, +): Record | undefined { + const profileIds = [authProfileId?.trim(), "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 as ApiKeyCredential).provider === "gigachat" + ) { + return credential.metadata; + } + } + return undefined; +} + // Queue a hidden steering message so pi-agent-core skips any remaining tool calls. function queueSessionsYieldInterruptMessage(activeSession: { agent: { steer: (message: AgentMessage) => void }; @@ -1913,8 +1933,10 @@ export async function runEmbeddedAttempt( // Read GigaChat-specific config from auth profile credential metadata. const gigachatStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false }); - const gigachatCred = gigachatStore.profiles["gigachat:default"]; - const gigachatMeta = gigachatCred?.type === "api_key" ? gigachatCred.metadata : undefined; + const gigachatMeta = resolveGigachatAuthProfileMetadata( + gigachatStore, + params.attempt.authProfileId, + ); const gigachatStreamFn = createGigachatStreamFn({ baseUrl, diff --git a/src/secrets/provider-env-vars.test.ts b/src/secrets/provider-env-vars.test.ts index a5c29c3d707..6270376579b 100644 --- a/src/secrets/provider-env-vars.test.ts +++ b/src/secrets/provider-env-vars.test.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from "vitest"; import { + PROVIDER_AUTH_ENV_VAR_CANDIDATES, + PROVIDER_ENV_VARS, listKnownProviderAuthEnvVarNames, listKnownSecretEnvVarNames, omitEnvKeysCaseInsensitive, @@ -42,4 +44,9 @@ describe("provider env vars", () => { expect.arrayContaining(["GIGACHAT_CREDENTIALS", "GIGACHAT_PASSWORD"]), ); }); + + it("does not treat GigaChat password-only env vars as API-key candidates", () => { + expect(PROVIDER_AUTH_ENV_VAR_CANDIDATES.gigachat).toEqual(["GIGACHAT_CREDENTIALS"]); + expect(PROVIDER_ENV_VARS.gigachat).toEqual(["GIGACHAT_CREDENTIALS", "GIGACHAT_PASSWORD"]); + }); }); diff --git a/src/secrets/provider-env-vars.ts b/src/secrets/provider-env-vars.ts index 8cff2ec7da9..aff3c220a9c 100644 --- a/src/secrets/provider-env-vars.ts +++ b/src/secrets/provider-env-vars.ts @@ -15,7 +15,7 @@ const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = { qianfan: ["QIANFAN_API_KEY"], xai: ["XAI_API_KEY"], mistral: ["MISTRAL_API_KEY"], - gigachat: ["GIGACHAT_CREDENTIALS", "GIGACHAT_PASSWORD"], + gigachat: ["GIGACHAT_CREDENTIALS"], kilocode: ["KILOCODE_API_KEY"], modelstudio: ["MODELSTUDIO_API_KEY"], volcengine: ["VOLCANO_ENGINE_API_KEY"], @@ -25,6 +25,7 @@ const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = { const CORE_PROVIDER_SETUP_ENV_VAR_OVERRIDES = { anthropic: ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN"], chutes: ["CHUTES_API_KEY", "CHUTES_OAUTH_TOKEN"], + gigachat: ["GIGACHAT_CREDENTIALS", "GIGACHAT_PASSWORD"], "minimax-cn": ["MINIMAX_API_KEY"], } as const;