





















@@ -19,9 +19,15 @@ import { resolveDefaultAgentWorkspaceDir } from "./workspace.js";
1919// discovery and external-CLI probing on the hot path.
20202121let currentProviderAuthState: ReadonlyMap<string, boolean> | null = null;
22+let currentProviderAuthStateWorkspaceDir: string | undefined;
23+// Generation counter guards against an in-flight warm publishing stale
24+// state after a subsequent clear/reload has invalidated it.
25+let currentProviderAuthStateGeneration = 0;
22262327export function clearCurrentProviderAuthState(): void {
2428currentProviderAuthState = null;
29+currentProviderAuthStateWorkspaceDir = undefined;
30+currentProviderAuthStateGeneration += 1;
2531}
26322733export function hasAuthForModelProvider(params: {
@@ -36,17 +42,20 @@ export function hasAuthForModelProvider(params: {
3642}): boolean {
3743const provider = normalizeProviderId(params.provider);
3844// The prepared map is built by warmCurrentProviderAuthState with broad
39-// auth discovery (external CLI + plugin synthetic auth enabled, no
40-// caller-supplied agentDir/env/store). Only consult it when the caller's
41-// scope matches; otherwise fall through to compute so callers that
42-// explicitly narrow the auth scope — e.g. gateway `models.list` with
43-// `runtimeAuthDiscovery: false` — get the answer they asked for.
45+// auth discovery (external CLI + plugin synthetic auth enabled) and the
46+// default-agent workspace dir. Only consult it when the caller's full
47+// auth context matches; otherwise fall through to compute so callers
48+// that narrow the scope — e.g. gateway `models.list` with
49+// `runtimeAuthDiscovery: false`, or per-agent picker calls that pass a
50+// non-default workspaceDir — get the answer they asked for.
4451const matchesWarmedScope =
4552params.discoverExternalCliAuth !== false &&
4653params.allowPluginSyntheticAuth !== false &&
4754params.agentDir === undefined &&
4855params.env === undefined &&
49-params.store === undefined;
56+params.store === undefined &&
57+(params.workspaceDir === undefined ||
58+params.workspaceDir === currentProviderAuthStateWorkspaceDir);
5059if (matchesWarmedScope) {
5160const preparedAnswer = currentProviderAuthState?.get(provider);
5261if (preparedAnswer !== undefined) {
@@ -109,6 +118,10 @@ export function createProviderAuthChecker(params: {
109118}
110119111120export async function warmCurrentProviderAuthState(cfg: OpenClawConfig): Promise<void> {
121+// Claim a fresh generation; any concurrent warm or clear bumps this and
122+// turns our published state stale.
123+currentProviderAuthStateGeneration += 1;
124+const ownGeneration = currentProviderAuthStateGeneration;
112125const catalog = await loadModelCatalog({ config: cfg });
113126const providers = new Set<string>();
114127for (const entry of catalog) {
@@ -134,5 +147,11 @@ export async function warmCurrentProviderAuthState(cfg: OpenClawConfig): Promise
134147});
135148state.set(provider, value);
136149}
150+if (ownGeneration !== currentProviderAuthStateGeneration) {
151+// A newer warm or clear ran while we were building; skip publication so
152+// the newer answer wins.
153+return;
154+}
137155currentProviderAuthState = state;
156+currentProviderAuthStateWorkspaceDir = workspaceDir;
138157}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。