






















@@ -59,6 +59,38 @@ vi.mock("../../agents/auth-profiles.runtime.js", () => ({
5959ensureAuthProfileStore: authProfileStoreMock.ensureAuthProfileStore,
6060}));
616162+// Alias-aware stub: mirrors the real isStoredCredentialCompatibleWithAuthProvider
63+// but inlines the claude-cli->anthropic alias so tests don't need live plugin metadata.
64+vi.mock("../../agents/auth-profiles/order.js", () => ({
65+isStoredCredentialCompatibleWithAuthProvider: ({
66+ provider,
67+ credential,
68+}: {
69+provider: string;
70+credential: { type: string; provider: string };
71+}) => {
72+const normalize = (v: string) => v.toLowerCase().replace(/[^a-z0-9]+/g, "");
73+const resolveAuthKey = (v: string) => {
74+const n = normalize(v);
75+// claude-cli is a deprecated choice id that resolves to the anthropic auth key
76+if (n === "claudecli") {
77+return "anthropic";
78+}
79+return n;
80+};
81+const providerKey = resolveAuthKey(provider);
82+const credentialKey = resolveAuthKey(credential.provider);
83+if (credentialKey === providerKey) {
84+return true;
85+}
86+// OpenAI Codex compat: openai api_key credential works for openai-codex provider
87+if (providerKey === "openaiapicodex" || providerKey === "openaicodex") {
88+return credentialKey === "openai" && credential.type === "api_key";
89+}
90+return false;
91+},
92+}));
93+6294afterEach(() => {
6395MODEL_CONTEXT_TOKEN_CACHE.clear();
6496vi.mocked(loadManifestModelCatalog).mockReset();
@@ -1677,6 +1709,50 @@ describe("createModelSelectionState auto-failover overrides", () => {
16771709});
16781710});
167917111712+describe("createModelSelectionState auth-profile override flapping regression", () => {
1713+const sessionKey = "agent:main:telegram:direct:1";
1714+1715+it("keeps alias-compatible authProfileOverride when stored credential provider is 'anthropic' for a claude-cli session", async () => {
1716+// Regression: the old code compared profile.provider directly to acceptedAuthProviders,
1717+// which cleared an 'anthropic' credential when the session ran under the 'claude-cli'
1718+// provider. The alias (claude-cli -> anthropic) must be respected so the override is kept.
1719+authProfileStoreMock.store = {
1720+version: 1,
1721+profiles: {
1722+"anthropic:claude-cli": {
1723+type: "api_key",
1724+provider: "anthropic",
1725+key: "test-cli-oauth-token",
1726+},
1727+},
1728+};
1729+const sessionEntry: SessionEntry = {
1730+sessionId: "s-cli",
1731+updatedAt: 1,
1732+authProfileOverride: "anthropic:claude-cli",
1733+};
1734+const sessionStore = { [sessionKey]: sessionEntry };
1735+1736+await createModelSelectionState({
1737+cfg: {} as OpenClawConfig,
1738+agentCfg: undefined,
1739+ sessionEntry,
1740+ sessionStore,
1741+ sessionKey,
1742+defaultProvider: "claude-cli",
1743+defaultModel: "claude-opus-4-7",
1744+provider: "claude-cli",
1745+model: "claude-opus-4-7",
1746+hasModelDirective: false,
1747+});
1748+1749+// The override must NOT have been cleared — the anthropic credential is
1750+// alias-compatible with the claude-cli provider.
1751+expect(sessionStore[sessionKey]?.authProfileOverride).toBe("anthropic:claude-cli");
1752+expect(sessionEntry.authProfileOverride).toBe("anthropic:claude-cli");
1753+});
1754+});
1755+16801756describe("createModelSelectionState resolveDefaultReasoningLevel", () => {
16811757it("uses manifest metadata before hydrating the runtime reasoning catalog", async () => {
16821758vi.mocked(loadModelCatalogLocal).mockClear();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。