openclaw / src /agents /runtime-plan /prepare-auth.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
e249c6d verified
Raw History Blame Contribute Delete
26.3 kB
/**
* Prepares route-aware auth forwarding for auxiliary agent-runtime calls.
* Callers supply an already loaded credential snapshot; this module never
* resolves secrets or loads a provider runtime.
*/
import { resolveMergedModelProviderConfig } from "../../config/model-provider-config.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type {
ProviderResolveModelRoutesContext,
ProviderRouteOverridePresence,
} from "../../plugin-sdk/provider-model-types.js";
import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js";
import { isPendingOAuthRefreshFence } from "../auth-profiles/oauth-refresh-marker.js";
import {
prependAuthProfilePin,
resolveAuthProfileEligibility,
resolveAuthProfileOrderWithMetadata,
} from "../auth-profiles/order.js";
import { resolveStoredCredentialReadOnlyAvailability } from "../auth-profiles/read-only-availability.js";
import { createSelectedAuthProfileUnavailableError } from "../auth-profiles/selection-error.js";
import type { AuthProfileStore } from "../auth-profiles/types.js";
import { isProfileInCooldown } from "../auth-profiles/usage-state.js";
import { resolveProviderDirectAuthPlanningEvidence } from "../model-auth-env.js";
import {
hasUsableCustomProviderApiKey,
resolveProviderConfigSecretInput,
resolveProviderEntryApiKeyProfileReference,
shouldPreferExplicitConfigApiKeyAuth,
} from "../model-auth-provider-config.js";
import { resolveModelProviderAuthConfig } from "../model-auth-provider-route.js";
import { resolveDefaultModelForAgent } from "../model-selection-config.js";
import { resolveOpenAIModelRoutes, selectOpenAIModelRouteAuth } from "../openai-model-routes.js";
import {
buildProviderModelAuthDirectSource,
buildProviderModelAuthSourcePlan,
classifyProviderModelAuthSource,
type ProviderModelAuthDirectSource,
type ProviderModelAuthProfileSource,
} from "../provider-model-auth-source-plan.js";
import {
selectProviderModelAuthSources,
resolveProviderModelRouteAuthRequirement,
} from "../provider-model-route-auth.js";
import { buildAgentRuntimeAuthPlan } from "./auth.js";
import type { AgentRuntimeAuthPlan } from "./types.js";
type PrepareAgentRuntimeAuthPlanParams = {
provider: string;
modelId: string;
modelApi?: string | null;
modelBaseUrl?: unknown;
requestTransportOverrides?: ProviderRouteOverridePresence;
config?: OpenClawConfig;
agentId?: string;
routeIntent?: ProviderResolveModelRoutesContext["routeIntent"];
env?: NodeJS.ProcessEnv;
agentDir?: string;
workspaceDir?: string;
metadataSnapshot?: PluginMetadataSnapshot;
authProfileStore?: AuthProfileStore;
sessionAuthProfileId?: string;
sessionAuthProfileSource?: "auto" | "user" | "user-link";
allowAuthProfileFallback?: boolean;
harnessId?: string;
harnessRuntime?: string;
harnessAuthBootstrap?: "harness";
allowHarnessAuthProfileForwarding?: boolean;
allowTransientCooldownProbe?: boolean;
resolveProviderPreferredProfileId?(context: {
config?: OpenClawConfig;
agentDir?: string;
workspaceDir?: string;
provider: string;
modelId: string;
preferredProfileId?: string;
lockedProfileId?: string;
profileOrder: string[];
authStore: AuthProfileStore;
}): string | undefined;
};
export type PreparedAgentRuntimeAuthAttempt =
| {
kind: "profile";
plan: AgentRuntimeAuthPlan;
profileId: string;
allowAuthProfileFallback?: never;
requiresPriorProfileAttempt?: never;
}
| {
kind: "direct";
plan: AgentRuntimeAuthPlan;
profileId?: never;
/** Direct lookup cannot re-enter automatic profile discovery. */
allowAuthProfileFallback: false;
/** Fail closed when every prepared profile became cooldown-blocked before dispatch. */
requiresPriorProfileAttempt: boolean;
}
| {
kind: "implicit";
plan: AgentRuntimeAuthPlan;
profileId?: never;
allowAuthProfileFallback?: never;
requiresPriorProfileAttempt?: never;
};
export type PreparedAgentRuntimeAuth = {
plan: AgentRuntimeAuthPlan;
/** Ordered physical attempts; every route/profile tuple was selected by this planner. */
attempts: readonly PreparedAgentRuntimeAuthAttempt[];
};
/** Prevents a direct fallback from bypassing a prepared profile tier. */
export function canRunPreparedAgentRuntimeAuthAttempt(params: {
attempt: PreparedAgentRuntimeAuthAttempt;
priorProfileAttempted: boolean;
}): boolean {
return (
params.attempt.kind !== "direct" ||
!params.attempt.requiresPriorProfileAttempt ||
params.priorProfileAttempted
);
}
/** Rechecks automatic cooldowns immediately before a prepared profile attempt. */
export function preparedAgentRuntimeProfileAttemptHasCandidate(params: {
attempt: PreparedAgentRuntimeAuthAttempt;
store: AuthProfileStore;
modelId: string;
}): boolean {
if (params.attempt.kind !== "profile") {
return false;
}
const profileIds = params.attempt.plan.forwardedAuthProfileCandidateIds ?? [
params.attempt.profileId,
];
return profileIds.some(
(profileId) => !isProfileInCooldown(params.store, profileId, undefined, params.modelId),
);
}
/** True when a prepared auth tuple can be reused for this exact compaction target. */
export function agentRuntimeAuthPlanMatchesTarget(
plan: AgentRuntimeAuthPlan,
target: { provider: string; modelId: string },
): boolean {
const route = plan.modelRoute;
const provider = route?.provider ?? plan.providerForAuth;
const modelId = route?.modelId ?? plan.modelId;
return (
modelId !== undefined &&
provider.trim().toLowerCase() === target.provider.trim().toLowerCase() &&
modelId === target.modelId
);
}
function resolveProfile(
params: PrepareAgentRuntimeAuthPlanParams,
profileId: string,
options: { ignoreCooldown?: boolean } = {},
): ProviderModelAuthProfileSource {
const credential = params.authProfileStore?.profiles[profileId];
const configured = params.config?.auth?.profiles?.[profileId];
const availability = credential
? resolveStoredCredentialReadOnlyAvailability({
credential,
cfg: params.config ?? {},
env: params.env ?? process.env,
})
: undefined;
const pendingOAuthRefresh =
credential?.type === "oauth" && isPendingOAuthRefreshFence(credential);
return {
kind: "profile",
profileId,
provider: credential?.provider ?? configured?.provider,
mode: credential?.type ?? configured?.mode,
// Runtime materialization owns secret readiness; only proven-invalid facts are terminal here.
readiness: availability === false && !pendingOAuthRefresh ? "unavailable" : "unknown",
cooldown:
!options.ignoreCooldown &&
params.authProfileStore &&
isProfileInCooldown(params.authProfileStore, profileId, undefined, params.modelId)
? "active"
: "clear",
};
}
/** Applies terminal provider-entry credential policy before route selection. */
function resolvePreparedProviderEntryApiKeyProfileReference(
params: PrepareAgentRuntimeAuthPlanParams & { store: AuthProfileStore },
) {
const reference = resolveProviderEntryApiKeyProfileReference({
cfg: params.config,
authAliasLookupParams: params,
provider: params.provider,
store: params.store,
});
if (reference.kind !== "profile") {
return reference;
}
const eligibility = resolveAuthProfileEligibility({
cfg: params.config,
authAliasLookupParams: params,
store: params.store,
provider: params.provider,
profileId: reference.profileId,
});
if (!eligibility.eligible) {
throw new Error(
`Per-entry apiKey profile "${reference.profileId}" has no usable credentials for ${params.provider}.`,
);
}
if (isProfileInCooldown(params.store, reference.profileId, undefined, params.modelId)) {
throw new Error(
`Auth profile "${reference.profileId}" is temporarily unavailable for ${params.provider}/${params.modelId}.`,
);
}
return reference;
}
/** Selects concrete provider routes and ordered credentials as one immutable preparation. */
export function prepareAgentRuntimeAuth(
input: PrepareAgentRuntimeAuthPlanParams,
): PreparedAgentRuntimeAuth {
const params = { ...input, config: resolveModelProviderAuthConfig(input) };
const requestedProfileId = params.sessionAuthProfileId?.trim() || undefined;
const userPinnedProfileId =
params.sessionAuthProfileSource === "user" || params.sessionAuthProfileSource === "user-link"
? requestedProfileId
: undefined;
const harnessOwnsOpenAIAuth =
params.harnessId?.trim().toLowerCase() === "codex" ||
params.harnessRuntime?.trim().toLowerCase() === "codex";
const harnessAuthOwnerId = params.harnessId?.trim() || params.harnessRuntime?.trim();
const runtimeAuthOwner =
harnessOwnsOpenAIAuth && params.harnessAuthBootstrap === "harness" && harnessAuthOwnerId
? { id: harnessAuthOwnerId }
: undefined;
const harnessAllowsAuthProfileForwarding = params.allowHarnessAuthProfileForwarding !== false;
if (userPinnedProfileId && !harnessAllowsAuthProfileForwarding) {
throw new Error(
`Auth profile "${userPinnedProfileId}" cannot be forwarded to the selected agent harness. Configure that harness's native account instead.`,
);
}
const store = params.authProfileStore;
const authProfileSelectionProvider = harnessOwnsOpenAIAuth ? "openai" : params.provider;
if (userPinnedProfileId) {
const eligibility = store
? resolveAuthProfileEligibility({
cfg: params.config,
authAliasLookupParams: params,
store,
provider: authProfileSelectionProvider,
profileId: userPinnedProfileId,
includePendingOAuthRefresh: true,
})
: { eligible: false };
if (!eligibility.eligible) {
if (
!store?.profiles[userPinnedProfileId] &&
params.config?.auth?.profiles?.[userPinnedProfileId]?.mode !== "aws-sdk"
) {
throw createSelectedAuthProfileUnavailableError({
profileId: userPinnedProfileId,
provider: authProfileSelectionProvider,
modelId: params.modelId,
});
}
throw new Error(
`Auth profile "${userPinnedProfileId}" is not configured for ${authProfileSelectionProvider}.`,
);
}
}
const configuredProvider = resolveMergedModelProviderConfig(params.config, params.provider);
const configuredAuthMode =
userPinnedProfileId || !harnessAllowsAuthProfileForwarding
? undefined
: configuredProvider?.auth;
const configuredAwsSdkAuth = configuredAuthMode === "aws-sdk";
const providerApiKeySecretRef = harnessAllowsAuthProfileForwarding
? resolveProviderConfigSecretInput(params.config, params.provider).ref
: undefined;
const providerHasApiKeySecretRef = Boolean(providerApiKeySecretRef);
const providerBinding =
harnessAllowsAuthProfileForwarding && !userPinnedProfileId && store && !configuredAwsSdkAuth
? resolvePreparedProviderEntryApiKeyProfileReference({
...params,
store,
})
: { kind: "none" as const };
if (providerBinding.kind === "profile-incompatible") {
throw new Error(
`Per-entry apiKey "${providerBinding.profileId}" is not a compatible bearer profile for ${params.provider}.`,
);
}
const boundProfileId = providerBinding.kind === "profile" ? providerBinding.profileId : undefined;
const providerHasUsableMarker =
providerBinding.kind === "marker" &&
hasUsableCustomProviderApiKey(params.config, params.provider, params.env);
const providerHasDirectMaterial =
!configuredAwsSdkAuth &&
(providerBinding.kind === "literal" || providerHasUsableMarker || providerHasApiKeySecretRef);
const explicitConfigApiKeyAuth = shouldPreferExplicitConfigApiKeyAuth(
params.config,
params.provider,
);
const providerBindingSuppressesProfiles =
(providerBinding.kind === "literal" && explicitConfigApiKeyAuth) ||
providerHasUsableMarker ||
providerHasApiKeySecretRef;
const providerBindingNeedsNonProfileFallback =
providerHasDirectMaterial && !providerBindingSuppressesProfiles;
// Explicit auth owns the physical route; apiKey is only its bearer material.
const selectedConfiguredAuthMode =
configuredAuthMode ?? (providerHasDirectMaterial ? "api-key" : undefined);
const selectedProfileId =
boundProfileId ?? (params.allowAuthProfileFallback === false ? userPinnedProfileId : undefined);
const resolvedAutomaticOrder =
!harnessAllowsAuthProfileForwarding ||
selectedProfileId ||
providerBindingSuppressesProfiles ||
configuredAwsSdkAuth ||
!store
? {
profileIds: selectedProfileId ? [selectedProfileId] : [],
hasExplicitOrder: false,
}
: resolveAuthProfileOrderWithMetadata({
cfg: params.config,
authAliasLookupParams: params,
store,
provider: authProfileSelectionProvider,
preferredProfile: requestedProfileId,
forModel: params.modelId,
readinessMode: "read-only",
includePendingOAuthRefresh: true,
});
const automaticOrderResolution = prependAuthProfilePin(
resolvedAutomaticOrder,
userPinnedProfileId,
);
const providerPreferredProfileId =
harnessAllowsAuthProfileForwarding &&
!selectedProfileId &&
!userPinnedProfileId &&
!providerBindingSuppressesProfiles &&
!configuredAwsSdkAuth &&
store
? params.resolveProviderPreferredProfileId?.({
config: params.config,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
provider: params.provider,
modelId: params.modelId,
preferredProfileId: requestedProfileId,
lockedProfileId: undefined,
profileOrder: automaticOrderResolution.profileIds,
authStore: store,
})
: undefined;
const resolvedOrderedProfileIds =
providerPreferredProfileId &&
automaticOrderResolution.profileIds.includes(providerPreferredProfileId)
? [
providerPreferredProfileId,
...automaticOrderResolution.profileIds.filter(
(profileId) => profileId !== providerPreferredProfileId,
),
]
: automaticOrderResolution.profileIds;
const directSource = (
mode: string | undefined,
evidence: ProviderModelAuthDirectSource["evidence"] = providerBinding.kind === "marker" &&
providerHasUsableMarker
? providerBinding.evidence
: providerApiKeySecretRef?.source === "env"
? "environment"
: "provider-config",
availability?: boolean,
authorization: ProviderModelAuthDirectSource["authorization"] = "declared",
) => buildProviderModelAuthDirectSource({ mode, evidence, availability, authorization });
const directPlanningCandidate = harnessAllowsAuthProfileForwarding
? resolveProviderDirectAuthPlanningEvidence(
authProfileSelectionProvider,
params.env ?? process.env,
{
config: params.config,
workspaceDir: params.workspaceDir,
metadataSnapshot: params.metadataSnapshot,
},
)
: null;
// OpenAI native account discovery is harness-owned synthetic auth, not a
// bearer credential for an OpenClaw request route.
const directPlanningEvidence =
directPlanningCandidate?.kind === "setup-provider" &&
authProfileSelectionProvider.trim().toLowerCase() === "openai"
? null
: directPlanningCandidate;
const directPlanningMode = directPlanningEvidence
? (configuredAuthMode ?? directPlanningEvidence.mode)
: undefined;
// Provenance ("where was it found") is not authorization ("may it be used
// here"). A credential found in the environment is still *declared* when the
// provider entry points at it — a literal apiKey, a `${VAR}` marker, or a
// SecretRef naming a canonical variable. Only a credential that nothing in
// config references is ambient, and only ambient credentials are restricted.
const fallbackIsAmbientCredential =
directPlanningEvidence?.kind === "environment" && !providerHasDirectMaterial;
const fallbackDirectSource = directPlanningMode
? directSource(
directPlanningMode,
directPlanningEvidence?.kind === "environment" ? "environment" : "runtime",
directPlanningEvidence?.kind === "environment" ? true : undefined,
fallbackIsAmbientCredential ? "ambient" : "declared",
)
: providerBindingNeedsNonProfileFallback
? directSource(selectedConfiguredAuthMode)
: undefined;
const automaticRouteAuthMode =
fallbackDirectSource && !providerBindingSuppressesProfiles && !configuredAuthMode
? undefined
: selectedConfiguredAuthMode;
const ownership = selectedProfileId
? {
reason:
selectedProfileId === userPinnedProfileId
? ("runtime-binding" as const)
: ("provider-binding" as const),
source: resolveProfile(params, selectedProfileId, { ignoreCooldown: true }),
}
: configuredAwsSdkAuth
? {
reason: "configured-auth" as const,
source: directSource("aws-sdk", "aws-sdk"),
}
: providerBindingSuppressesProfiles
? {
reason: "configured-auth" as const,
source: directSource(selectedConfiguredAuthMode),
}
: undefined;
const sourcePlan = buildProviderModelAuthSourcePlan({
...(ownership ? { ownership } : {}),
profiles: resolvedOrderedProfileIds.map((profileId) => resolveProfile(params, profileId)),
...(userPinnedProfileId || providerPreferredProfileId
? { preferredProfileId: userPinnedProfileId ?? providerPreferredProfileId }
: {}),
explicitOrder: automaticOrderResolution.hasExplicitOrder,
preserveProfilePriority: Boolean(userPinnedProfileId),
...(fallbackDirectSource ? { fallback: fallbackDirectSource } : {}),
allowCooldown: params.allowTransientCooldownProbe,
});
const resolution = resolveOpenAIModelRoutes({
provider: params.provider,
modelId: params.modelId,
api: params.modelApi,
baseUrl: params.modelBaseUrl,
config: params.config,
agentId: params.agentId,
primaryModel:
!params.routeIntent && params.config
? resolveDefaultModelForAgent({
cfg: params.config,
agentId: params.agentId,
allowManifestNormalization: false,
allowPluginNormalization: false,
})
: undefined,
resolveProfileAuthMode: (profileId) => params.authProfileStore?.profiles[profileId]?.type,
routeIntent: params.routeIntent,
pinnedAuthRequirement: resolveProviderModelRouteAuthRequirement(
sourcePlan.kind === "required"
? sourcePlan.source.mode
: (sourcePlan.orderedProfiles.find((source) => source.profileId === userPinnedProfileId)
?.mode ?? configuredAuthMode),
),
env: params.env,
requestTransportOverrides: params.requestTransportOverrides,
});
if (!resolution || resolution.kind === "indeterminate") {
const sourceDecision = selectProviderModelAuthSources({
provider: authProfileSelectionProvider,
plan: sourcePlan,
});
if (sourceDecision.kind === "rejected") {
if (sourceDecision.reason === "all-cooldown" && sourceDecision.source) {
throw new Error(
`Auth profile "${sourceDecision.source.profileId}" is temporarily unavailable for ${params.provider}/${params.modelId}.`,
);
}
throw new Error(sourceDecision.message);
}
const buildGenericPlan = (
attempt: (typeof sourceDecision.attempts)[number] | undefined,
candidateIndex: number,
) => {
const profile = attempt?.kind === "profile" ? attempt.source : undefined;
const candidateIds = sourceDecision.attempts
.slice(candidateIndex)
.flatMap((candidate) => (candidate.kind === "profile" ? [candidate.source.profileId] : []));
return buildAgentRuntimeAuthPlan({
provider: params.provider,
modelId: params.modelId,
authProfileProvider: profile?.provider,
authProfileMode:
profile?.mode ??
(attempt?.kind === "direct" ? attempt.source.mode : selectedConfiguredAuthMode),
sessionAuthProfileId: profile?.profileId,
sessionAuthProfileSource: profile
? profile.profileId === userPinnedProfileId
? "user"
: "auto"
: undefined,
sessionAuthProfileCandidateIds: candidateIds.length > 0 ? candidateIds : undefined,
credentialSource: attempt
? classifyProviderModelAuthSource(attempt.source)
: { kind: "none" },
config: params.config,
env: params.env,
workspaceDir: params.workspaceDir,
metadataSnapshot: params.metadataSnapshot,
harnessId: params.harnessId,
harnessRuntime: params.harnessRuntime,
allowHarnessAuthProfileForwarding: harnessAllowsAuthProfileForwarding,
});
};
const attempts: PreparedAgentRuntimeAuthAttempt[] = sourceDecision.attempts.map(
(attempt, index) => {
const plan = buildGenericPlan(attempt, index);
return attempt.kind === "profile"
? { kind: "profile", plan, profileId: attempt.source.profileId }
: {
kind: "direct",
plan,
allowAuthProfileFallback: attempt.allowAuthProfileFallback,
requiresPriorProfileAttempt: sourceDecision.attempts
.slice(0, index)
.some((candidate) => candidate.kind === "profile"),
};
},
);
const plan = attempts[0]?.plan ?? buildGenericPlan(undefined, 0);
if (
selectedProfileId &&
harnessOwnsOpenAIAuth &&
plan.forwardedAuthProfileId !== selectedProfileId
) {
throw new Error(
`Auth profile "${selectedProfileId}" cannot be forwarded to the codex runtime.`,
);
}
return {
plan,
attempts: attempts.length > 0 ? attempts : [{ kind: "implicit", plan }],
};
}
if (resolution.kind === "incompatible") {
throw new Error(resolution.message);
}
const toPreparedRoute = (route: (typeof resolution.routes)[number]) => ({
provider: params.provider,
modelId: params.modelId,
api: route.api,
baseUrl: route.baseUrl,
authRequirement: route.authRequirement,
requestTransportOverrides: route.requestTransportOverrides,
runtimePolicy: route.runtimePolicy,
});
const routeAuthDecision = selectOpenAIModelRouteAuth({
resolution,
sourcePlan,
configuredAuthMode: automaticRouteAuthMode,
...(runtimeAuthOwner ? { runtimeAuthOwner } : {}),
...(runtimeAuthOwner && configuredProvider === undefined
? { allowNativeAuthOnSingleRoute: true }
: {}),
});
if (routeAuthDecision.kind === "deferred") {
const plan = buildAgentRuntimeAuthPlan({
provider: params.provider,
modelId: params.modelId,
config: params.config,
env: params.env,
workspaceDir: params.workspaceDir,
metadataSnapshot: params.metadataSnapshot,
harnessId: params.harnessId,
harnessRuntime: params.harnessRuntime,
allowHarnessAuthProfileForwarding: harnessAllowsAuthProfileForwarding,
deferredRouteSupport: routeAuthDecision.routeSupport,
});
return { plan, attempts: [{ kind: "implicit", plan }] };
}
if (routeAuthDecision.kind !== "selected") {
if (
routeAuthDecision.kind === "rejected" &&
routeAuthDecision.reason === "all-cooldown" &&
routeAuthDecision.source
) {
throw new Error(
`Auth profile "${routeAuthDecision.source.profileId}" is temporarily unavailable for ${params.provider}/${params.modelId}.`,
);
}
throw new Error(routeAuthDecision.message);
}
const buildRoutedPlan = (attempt: (typeof routeAuthDecision.attempts)[number] | undefined) => {
const profile = attempt?.kind === "profile" ? attempt.source : undefined;
const route = attempt?.route ?? routeAuthDecision.selection.route;
return buildAgentRuntimeAuthPlan({
provider: params.provider,
modelId: params.modelId,
authProfileProvider: profile?.provider,
authProfileMode:
profile?.mode ??
(attempt?.kind === "direct" ? attempt.source.mode : selectedConfiguredAuthMode),
sessionAuthProfileId: profile?.profileId,
sessionAuthProfileSource: profile
? profile.profileId === userPinnedProfileId
? "user"
: "auto"
: undefined,
sessionAuthProfileCandidateIds:
attempt?.kind === "profile" ? [...attempt.sameRouteProfileIds] : undefined,
credentialSource: attempt
? classifyProviderModelAuthSource(attempt.source)
: { kind: "none" },
modelRoute: toPreparedRoute(route),
config: params.config,
env: params.env,
workspaceDir: params.workspaceDir,
metadataSnapshot: params.metadataSnapshot,
harnessId: params.harnessId,
harnessRuntime: params.harnessRuntime,
allowHarnessAuthProfileForwarding: harnessAllowsAuthProfileForwarding,
});
};
const attempts: PreparedAgentRuntimeAuthAttempt[] = routeAuthDecision.attempts.map(
(attempt, index) => {
const plan = buildRoutedPlan(attempt);
return attempt.kind === "profile"
? { kind: "profile", plan, profileId: attempt.source.profileId }
: {
kind: "direct",
plan,
allowAuthProfileFallback: attempt.allowAuthProfileFallback,
requiresPriorProfileAttempt: routeAuthDecision.attempts
.slice(0, index)
.some((candidate) => candidate.kind === "profile"),
};
},
);
const plan = attempts[0]?.plan ?? buildRoutedPlan(undefined);
for (const attempt of attempts) {
if (
attempt.profileId &&
harnessOwnsOpenAIAuth &&
attempt.plan.forwardedAuthProfileId !== attempt.profileId
) {
throw new Error(
`Auth profile "${attempt.profileId}" cannot be forwarded to the codex runtime.`,
);
}
}
return {
plan,
attempts: attempts.length > 0 ? attempts : [{ kind: "implicit", plan }],
};
}