Files
openclaw/src/plugins/provider-runtime.ts
Peter Steinberger bb46b79d3c refactor: internalize OpenClaw agent runtime (#85341)
* refactor: extract agent core package

Introduce packages/agent-core as the OpenClaw-owned home for reusable agent loop, harness, session, prompt, and runtime dependency contracts.

* refactor: extract shared llm runtime

Move provider model registries, stream wrappers, OAuth helpers, and LLM utilities into src/llm with plugin-sdk barrels instead of depending on the old embedded runtime layout.

* refactor: remove pi runtime internals

Rename remaining Pi-shaped agent surfaces to OpenClaw agent runtime names, delete obsolete Pi docs and package graph checks, and add the third-party notice for incorporated code.

* refactor: tighten agent session runtime

Make agent-core/runtime dependencies explicit, consolidate compaction and session transcript helpers, and move model/session helpers behind OpenClaw-owned contracts.

* refactor: remove static model and pi auth paths

Drop static model catalogs and Pi auth bridges, move model/provider facts to manifest-owned runtime contracts, and harden internal embedded-agent utilities.

* refactor: remove legacy provider compat paths

* docs: remove agent parity notes

* fix: skip provider wildcard metadata parsing

* refactor: share session extension sdk loading

* refactor: inline acpx proxy error formatter

* refactor: fold edit recovery into edit tool

* fix: accept extension batch separator

* test: align startup provider plugin expectations

* fix: restore provider-scoped release discovery

* test: align static asset packaging expectations

* fix: run static provider catalogs during scoped discovery

* fix: add provider entry catalogs for scoped live discovery

* fix: load lightweight provider catalog entries

* fix: refresh provider-scoped plugin metadata

* fix: keep provider catalog entries on release live path

* fix: keep static manifest models in release live checks

* fix: harden release model discovery

* fix: reduce OpenAI live cache probe reasoning

* fix: disable OpenAI cache probe reasoning

* ci: extend OpenAI gateway live timeout

* fix: extend live gateway model budget

* fix: stabilize release validation regressions

* fix: honor provider aliases in model rows

* fix: stabilize release validation lanes

* fix: stabilize release memory qa

* ci: stabilize release validation lanes

* ci: prefer ipv4 for live docker node calls

* fix: restore shared tool-call stream wrapper

* ci: remove legacy pi test shard alias

* fix: clean up embedded agent test drift

* fix: stabilize runtime alias status

* fix: clean up embedded agent ci drift

* fix: restore release ci invariants

* fix: clean up post-rebase runtime drift

* fix: restore release ci checks

* fix: restore release ci after rebase

* fix: remove stale pi runtime path

* test: align compaction runtime expectations

* test: update plugin prerelease expectations

* fix: handle claude live tool approvals

* fix: stabilize release validation gates

* fix: finish agent runtime import

* test: finish post-rebase agent runtime mocks

* fix: keep codex compaction native

* fix: stabilize codex app-server hook tests

* test: isolate codex diagnostic active run

* test: remove codex diagnostic completion race

# Conflicts:
#	extensions/codex/src/app-server/run-attempt.test.ts

* ci: fix full release manifest performance run id

* refactor: narrow llm plugin sdk boundary

* chore: drop generated google boundary stamps

* fix: repair rebase fallout

* fix: clean up rebased runtime references

* fix: decode codex jwt payloads as base64url

* fix: preserve shipped pi runtime alias

* fix: add scoped sdk virtual modules

* fix: decode llm codex oauth jwt as base64url

* fix: avoid stale vertex adc negative cache

* fix: harden tool arg decoding and codeql path

* fix: keep vertex adc negative checks live

* refactor: consolidate codex jwt and edit helpers

* fix: await codex oauth node runtime imports

* fix: preserve sdk tool and notice contracts

* fix: preserve shipped compat config boundaries

* fix: align codex oauth callback host

* fix: terminate agent-core loop streams on failure

* fix: keep codex oauth callback alive during fallback

* ci: include session tools in critical codeql scans

* fix: keep Cloudflare Anthropic provider auth header

* docs: redirect legacy pi runtime pages

* fix: honor bundled web provider compat discovery

* fix: protect session output spill files

* fix: keep legacy agent dir env blocked

* fix: contain auto-discovered skill symlinks

* fix: harden agent core sdk proxy surfaces

* fix: restore approval reaction sdk compat

* fix: keep live docker runs bounded

* fix: keep codex oauth redirect host aligned

* fix: resolve post-rebase agent runtime drift

* fix: redact anthropic oauth parse failures

* fix: preserve responses strict tool shaping

* fix: repair agent runtime rebase cleanup

* docs: redirect retired parity pages

* fix: bound auto-discovered resources to roots

* fix: repair post-rebase agent test drift

* fix: preserve bundled provider allowlist migration

* fix: preserve manifest-owned provider aliases

* fix: declare photon image dependency

* fix: keep provider headers out of proxy body

* fix: preserve shipped env aliases

* fix: refresh control ui i18n generated state

* fix: quote read fallback paths

* fix: preview edits through configured backend

* test: satisfy core test typecheck

* fix: preserve ZAI usage auth fallback

* test: repair codex diagnostic test

* fix: repair agent runtime rebase drift

* test: finish embedded runner import rename

* fix: repair agent runtime rebase integrations

* test: align compaction oauth fallback expectations

* fix: allow sdk-auth session models

* fix: update doctor tool schema import

* fix: preserve bedrock plugin region

* fix: stream harmony-like prose immediately

* ci: include session runtime in codeql shards

* fix: repair latest rebase integrations

* fix: honor explicit codex websocket transport

* fix: keep openai-compatible credentials provider-scoped

* fix: refresh sdk api baseline after rebase

* fix: route cli runtime aliases through openclaw harness

* test: rename stale harness mock expectation

* test: rename embedded agent overflow calls

* test: clean embedded auth test wording

* test: use openclaw stream types in deepinfra cache test

* fix: refresh sdk api baseline on latest main

* fix: honor bundled discovery compat allowlists

* fix: refresh sdk api baseline after latest rebase

* fix: remove stale rebase imports

* test: rename stale model catalog mock

* test: mock renamed doctor runtime modules

* fix: map canonical kimi env auth

* fix: use internal model registry in bench script

* fix: migrate deepinfra provider catalog entry

* fix: enforce builtin tool suppression

* fix: route compaction auth and proxy payloads safely

* refactor: prune unused llm registry leftovers

* test: update codex hooks session import

* test: fix model picker ci coverage

* test: align model picker auth mock types
2026-05-27 19:24:04 +01:00

966 lines
31 KiB
TypeScript

import type { AuthProfileCredential, OAuthCredential } from "../agents/auth-profiles/types.js";
import { resolveGpt5SystemPromptContribution } from "../agents/gpt5-prompt-overlay.js";
import {
applyPluginTextReplacements,
mergePluginTextTransforms,
} from "../agents/plugin-text-transforms.js";
import { normalizeProviderId } from "../agents/provider-id.js";
import type { ProviderSystemPromptContribution } from "../agents/system-prompt-contribution.js";
import type { ModelProviderConfig } from "../config/types.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { sortUniqueStrings, uniqueStrings } from "../shared/string-normalization.js";
import { sanitizeForLog } from "../terminal/ansi.js";
import { normalizeProviderModelIdWithManifest } from "./manifest-model-id-normalization.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import { resolvePluginDiscoveryProvidersRuntime } from "./provider-discovery.runtime.js";
import {
clearProviderRuntimePluginCacheForTest,
prepareProviderExtraParams,
resolveProviderAuthProfileId,
resolveProviderExtraParamsForTransport,
resolveProviderFollowupFallbackRoute,
ensureProviderRuntimePluginHandle,
resolveLoadedProviderRuntimePlugin,
resolveProviderHookPlugin,
resolveProviderPluginsForHooks,
resolveProviderRuntimePlugin,
type ProviderRuntimePluginHandle,
wrapProviderStreamFn,
} from "./provider-hook-runtime.js";
import { resolveBundledProviderPolicySurface } from "./provider-public-artifacts.js";
import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js";
import type { ProviderThinkingProfile } from "./provider-thinking.types.js";
import {
resolveCatalogHookProviderPluginIds,
resolveExternalAuthProfileCompatFallbackPluginIds,
resolveExternalAuthProfileProviderPluginIds,
resolveOwningPluginIdsForProvider,
resolveOwningPluginIdsForProviderRef,
} from "./providers.js";
import { getActivePluginRegistryWorkspaceDirFromState } from "./runtime-state.js";
import { resolveRuntimeTextTransforms } from "./text-transforms.runtime.js";
import type {
ProviderAuthDoctorHintContext,
ProviderAugmentModelCatalogContext,
ProviderExternalAuthProfile,
ProviderBuildMissingAuthMessageContext,
ProviderBuildUnknownModelHintContext,
ProviderCacheTtlEligibilityContext,
ProviderCreateEmbeddingProviderContext,
ProviderDeferSyntheticProfileAuthContext,
ProviderResolveSyntheticAuthContext,
ProviderCreateStreamFnContext,
ProviderDefaultThinkingPolicyContext,
ProviderFetchUsageSnapshotContext,
ProviderFailoverErrorContext,
ProviderNormalizeToolSchemasContext,
ProviderNormalizeConfigContext,
ProviderNormalizeModelIdContext,
ProviderReasoningOutputMode,
ProviderReasoningOutputModeContext,
ProviderReplayPolicy,
ProviderReplayPolicyContext,
ProviderNormalizeResolvedModelContext,
ProviderNormalizeTransportContext,
ProviderModernModelPolicyContext,
ProviderPrepareDynamicModelContext,
ProviderPreferRuntimeResolvedModelContext,
ProviderPlugin,
ProviderResolveExternalAuthProfilesContext,
ProviderResolveExternalOAuthProfilesContext,
ProviderPrepareRuntimeAuthContext,
ProviderApplyConfigDefaultsContext,
ProviderResolveConfigApiKeyContext,
ProviderSanitizeReplayHistoryContext,
ProviderResolveUsageAuthContext,
ProviderResolveDynamicModelContext,
ProviderResolveTransportTurnStateContext,
ProviderResolveWebSocketSessionPolicyContext,
ProviderSystemPromptContributionContext,
ProviderTransformSystemPromptContext,
ProviderThinkingPolicyContext,
ProviderTransportTurnState,
ProviderValidateReplayTurnsContext,
ProviderWebSocketSessionPolicy,
PluginTextTransforms,
} from "./types.js";
const log = createSubsystemLogger("plugins/provider-runtime");
const warnedExternalAuthFallbackPluginIds = new Set<string>();
function matchesProviderPluginRef(provider: ProviderPlugin, providerId: string): boolean {
const normalized = normalizeProviderId(providerId);
if (!normalized) {
return false;
}
if (normalizeProviderId(provider.id) === normalized) {
return true;
}
return [...(provider.aliases ?? []), ...(provider.hookAliases ?? [])].some(
(alias) => normalizeProviderId(alias) === normalized,
);
}
function resolveProviderHookRefs(
provider: string,
providerConfig?: ModelProviderConfig,
modelApi?: string,
): string[] {
const refs = [provider];
const apiRef = normalizeOptionalString(modelApi ?? providerConfig?.api);
if (apiRef && normalizeProviderId(apiRef) !== normalizeProviderId(provider)) {
refs.push(apiRef);
}
return uniqueStrings(refs);
}
function matchesAnyProviderPluginRef(provider: ProviderPlugin, providerRefs: readonly string[]) {
return providerRefs.some((providerRef) => matchesProviderPluginRef(provider, providerRef));
}
function hasExplicitProviderRuntimePluginActivation(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): boolean {
if (!params.config) {
return true;
}
const ownerPluginIds =
resolveOwningPluginIdsForProvider({
provider: params.provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
}) ?? [];
if (ownerPluginIds.length === 0) {
return false;
}
const allow = new Set(params.config.plugins?.allow ?? []);
const entries = params.config.plugins?.entries ?? {};
return ownerPluginIds.some((pluginId) => allow.has(pluginId) || entries[pluginId] !== undefined);
}
export {
prepareProviderExtraParams,
resolveProviderAuthProfileId,
resolveProviderExtraParamsForTransport,
resolveProviderFollowupFallbackRoute,
resolveProviderRuntimePlugin,
wrapProviderStreamFn,
};
function resetExternalAuthFallbackWarningCacheForTest(): void {
warnedExternalAuthFallbackPluginIds.clear();
}
export const testing = {
clearProviderRuntimePluginCacheForTest,
resetExternalAuthFallbackWarningCacheForTest,
} as const;
function resolveProviderPluginsForCatalogHooks(params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): ProviderPlugin[] {
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
const env = params.env ?? process.env;
const onlyPluginIds = resolveCatalogHookProviderPluginIds({
config: params.config,
workspaceDir,
env,
});
if (onlyPluginIds.length === 0) {
return [];
}
return resolveProviderPluginsForHooks({
...params,
workspaceDir,
env,
onlyPluginIds,
});
}
export function runProviderDynamicModel(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveDynamicModelContext;
}): ProviderRuntimeModel | undefined {
return resolveProviderRuntimePlugin(params)?.resolveDynamicModel?.(params.context) ?? undefined;
}
export function resolveProviderSystemPromptContribution(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
runtimeHandle?: ProviderRuntimePluginHandle;
context: ProviderSystemPromptContributionContext;
}): ProviderSystemPromptContribution | undefined {
const plugin = ensureProviderRuntimePluginHandle(params).plugin;
const baseOverlay = resolveGpt5SystemPromptContribution({
config: params.context.config ?? params.config,
providerId: params.context.provider ?? params.provider,
modelId: params.context.modelId,
trigger: params.context.trigger,
});
const providerOverlay =
plugin?.resolvePromptOverlay?.({
...params.context,
baseOverlay,
}) ?? undefined;
return mergeProviderSystemPromptContributions(
mergeProviderSystemPromptContributions(baseOverlay, providerOverlay),
plugin?.resolveSystemPromptContribution?.(params.context) ?? undefined,
);
}
function mergeProviderSystemPromptContributions(
base?: ProviderSystemPromptContribution,
override?: ProviderSystemPromptContribution,
): ProviderSystemPromptContribution | undefined {
if (!base) {
return override;
}
if (!override) {
return base;
}
const stablePrefix = mergeUniquePromptSections(base.stablePrefix, override.stablePrefix);
const dynamicSuffix = mergeUniquePromptSections(base.dynamicSuffix, override.dynamicSuffix);
return {
...(stablePrefix ? { stablePrefix } : {}),
...(dynamicSuffix ? { dynamicSuffix } : {}),
sectionOverrides: {
...base.sectionOverrides,
...override.sectionOverrides,
},
};
}
function mergeUniquePromptSections(...sections: Array<string | undefined>): string | undefined {
const uniqueSections = uniqueStrings(
sections.filter((section): section is string => Boolean(section?.trim())),
);
return uniqueSections.length > 0 ? uniqueSections.join("\n\n") : undefined;
}
export function transformProviderSystemPrompt(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
runtimeHandle?: ProviderRuntimePluginHandle;
context: ProviderTransformSystemPromptContext;
}): string {
const plugin = ensureProviderRuntimePluginHandle(params).plugin;
const textTransforms = mergePluginTextTransforms(
resolveRuntimeTextTransforms(),
plugin?.textTransforms,
);
const transformed =
plugin?.transformSystemPrompt?.(params.context) ?? params.context.systemPrompt;
return applyPluginTextReplacements(transformed, textTransforms?.input);
}
export function resolveProviderTextTransforms(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
runtimeHandle?: ProviderRuntimePluginHandle;
}): PluginTextTransforms | undefined {
return mergePluginTextTransforms(
resolveRuntimeTextTransforms(),
ensureProviderRuntimePluginHandle(params).plugin?.textTransforms,
);
}
export async function prepareProviderDynamicModel(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderPrepareDynamicModelContext;
}): Promise<void> {
await resolveProviderRuntimePlugin(params)?.prepareDynamicModel?.(params.context);
}
export function shouldPreferProviderRuntimeResolvedModel(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderPreferRuntimeResolvedModelContext;
}): boolean {
return (
resolveProviderRuntimePlugin(params)?.preferRuntimeResolvedModel?.(params.context) ?? false
);
}
export function normalizeProviderResolvedModelWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: {
config?: OpenClawConfig;
agentDir?: string;
workspaceDir?: string;
provider: string;
modelId: string;
model: ProviderRuntimeModel;
};
}): ProviderRuntimeModel | undefined {
return (
resolveProviderRuntimePlugin(params)?.normalizeResolvedModel?.(params.context) ?? undefined
);
}
export function applyProviderResolvedTransportWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeResolvedModelContext;
}): ProviderRuntimeModel | undefined {
const normalized = normalizeProviderTransportWithPlugin({
provider: params.provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
provider: params.context.provider,
api: params.context.model.api,
baseUrl: params.context.model.baseUrl,
},
});
if (!normalized) {
return undefined;
}
const nextApi = normalized.api ?? params.context.model.api;
const nextBaseUrl = normalized.baseUrl ?? params.context.model.baseUrl;
if (nextApi === params.context.model.api && nextBaseUrl === params.context.model.baseUrl) {
return undefined;
}
return {
...params.context.model,
api: nextApi as ProviderRuntimeModel["api"],
baseUrl: nextBaseUrl,
};
}
export function normalizeProviderModelIdWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeModelIdContext;
}): string | undefined {
const plugin = resolveProviderHookPlugin(params);
return (
normalizeOptionalString(plugin?.normalizeModelId?.(params.context)) ??
normalizeProviderModelIdWithManifest(params)
);
}
export function normalizeProviderTransportWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeTransportContext;
}): { api?: string | null; baseUrl?: string } | undefined {
const hasTransportChange = (normalized: { api?: string | null; baseUrl?: string }) =>
(normalized.api ?? params.context.api) !== params.context.api ||
(normalized.baseUrl ?? params.context.baseUrl) !== params.context.baseUrl;
const matchedPlugin = resolveProviderHookPlugin(params);
const normalizedMatched = matchedPlugin?.normalizeTransport?.(params.context);
if (normalizedMatched && hasTransportChange(normalizedMatched)) {
return normalizedMatched;
}
for (const candidate of resolveProviderPluginsForHooks(params)) {
if (!candidate.normalizeTransport || candidate === matchedPlugin) {
continue;
}
const normalized = candidate.normalizeTransport(params.context);
if (normalized && hasTransportChange(normalized)) {
return normalized;
}
}
return undefined;
}
export function normalizeProviderConfigWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeConfigContext;
allowRuntimePluginLoad?: boolean;
}): ModelProviderConfig | undefined {
const hasConfigChange = (normalized: ModelProviderConfig) =>
normalized !== params.context.providerConfig;
const bundledSurface = resolveBundledProviderPolicySurface(params.provider);
if (bundledSurface?.normalizeConfig) {
const normalized = bundledSurface.normalizeConfig(params.context);
return normalized && hasConfigChange(normalized) ? normalized : undefined;
}
if (!hasExplicitProviderRuntimePluginActivation(params)) {
return undefined;
}
if (params.allowRuntimePluginLoad === false) {
return undefined;
}
const matchedPlugin = resolveProviderRuntimePlugin(params);
const normalizedMatched = matchedPlugin?.normalizeConfig?.(params.context);
return normalizedMatched && hasConfigChange(normalizedMatched) ? normalizedMatched : undefined;
}
export function applyProviderNativeStreamingUsageCompatWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderNormalizeConfigContext;
allowRuntimePluginLoad?: boolean;
}): ModelProviderConfig | undefined {
if (params.allowRuntimePluginLoad === false) {
return undefined;
}
return (
resolveProviderRuntimePlugin(params)?.applyNativeStreamingUsageCompat?.(params.context) ??
undefined
);
}
export function resolveProviderConfigApiKeyWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveConfigApiKeyContext;
allowRuntimePluginLoad?: boolean;
}): string | undefined {
const bundledSurface = resolveBundledProviderPolicySurface(params.provider);
if (bundledSurface?.resolveConfigApiKey) {
return normalizeOptionalString(bundledSurface.resolveConfigApiKey(params.context));
}
if (params.allowRuntimePluginLoad === false) {
return undefined;
}
return normalizeOptionalString(
resolveProviderRuntimePlugin(params)?.resolveConfigApiKey?.(params.context),
);
}
export function resolveProviderReplayPolicyWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderReplayPolicyContext;
}): ProviderReplayPolicy | undefined {
return resolveProviderRuntimePlugin(params)?.buildReplayPolicy?.(params.context) ?? undefined;
}
export async function sanitizeProviderReplayHistoryWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderSanitizeReplayHistoryContext;
}) {
return await resolveProviderRuntimePlugin(params)?.sanitizeReplayHistory?.(params.context);
}
export async function validateProviderReplayTurnsWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderValidateReplayTurnsContext;
}) {
return await resolveProviderRuntimePlugin(params)?.validateReplayTurns?.(params.context);
}
export function normalizeProviderToolSchemasWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
runtimeHandle?: ProviderRuntimePluginHandle;
context: ProviderNormalizeToolSchemasContext;
}) {
return (
ensureProviderRuntimePluginHandle(params).plugin?.normalizeToolSchemas?.(params.context) ??
undefined
);
}
export function inspectProviderToolSchemasWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
runtimeHandle?: ProviderRuntimePluginHandle;
context: ProviderNormalizeToolSchemasContext;
}) {
return (
ensureProviderRuntimePluginHandle(params).plugin?.inspectToolSchemas?.(params.context) ??
undefined
);
}
export function resolveProviderReasoningOutputModeWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderReasoningOutputModeContext;
}): ProviderReasoningOutputMode | undefined {
const mode = resolveProviderRuntimePlugin(params)?.resolveReasoningOutputMode?.(params.context);
return mode === "native" || mode === "tagged" ? mode : undefined;
}
export function resolveProviderStreamFn(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
allowRuntimePluginLoad?: boolean;
context: ProviderCreateStreamFnContext;
}) {
const plugin =
params.allowRuntimePluginLoad === false
? resolveLoadedProviderRuntimePlugin(params)
: resolveProviderRuntimePlugin(params);
return plugin?.createStreamFn?.(params.context) ?? undefined;
}
export function resolveProviderTransportTurnStateWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveTransportTurnStateContext;
}): ProviderTransportTurnState | undefined {
return (
resolveProviderRuntimePlugin(params)?.resolveTransportTurnState?.(params.context) ?? undefined
);
}
export function resolveProviderWebSocketSessionPolicyWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveWebSocketSessionPolicyContext;
}): ProviderWebSocketSessionPolicy | undefined {
return (
resolveProviderRuntimePlugin(params)?.resolveWebSocketSessionPolicy?.(params.context) ??
undefined
);
}
export async function createProviderEmbeddingProvider(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderCreateEmbeddingProviderContext;
}) {
return await resolveProviderRuntimePlugin(params)?.createEmbeddingProvider?.(params.context);
}
export async function prepareProviderRuntimeAuth(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderPrepareRuntimeAuthContext;
}) {
return await resolveProviderRuntimePlugin(params)?.prepareRuntimeAuth?.(params.context);
}
export async function resolveProviderUsageAuthWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveUsageAuthContext;
}) {
return await resolveProviderRuntimePlugin(params)?.resolveUsageAuth?.(params.context);
}
export async function resolveProviderUsageSnapshotWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderFetchUsageSnapshotContext;
}) {
return await resolveProviderRuntimePlugin(params)?.fetchUsageSnapshot?.(params.context);
}
export function matchesProviderContextOverflowWithPlugin(params: {
provider?: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderFailoverErrorContext;
}): boolean {
const plugins = params.provider
? [resolveProviderHookPlugin({ ...params, provider: params.provider })].filter(
(plugin): plugin is ProviderPlugin => Boolean(plugin),
)
: resolveProviderPluginsForHooks(params);
for (const plugin of plugins) {
if (plugin.matchesContextOverflowError?.(params.context)) {
return true;
}
}
return false;
}
export function classifyProviderFailoverReasonWithPlugin(params: {
provider?: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderFailoverErrorContext;
}) {
const plugins = params.provider
? [resolveProviderHookPlugin({ ...params, provider: params.provider })].filter(
(plugin): plugin is ProviderPlugin => Boolean(plugin),
)
: resolveProviderPluginsForHooks(params);
for (const plugin of plugins) {
const reason = plugin.classifyFailoverReason?.(params.context);
if (reason) {
return reason;
}
}
return undefined;
}
export function formatProviderAuthProfileApiKeyWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: AuthProfileCredential;
}) {
return resolveProviderRuntimePlugin(params)?.formatApiKey?.(params.context);
}
export async function refreshProviderOAuthCredentialWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: OAuthCredential;
}) {
return await resolveProviderRuntimePlugin(params)?.refreshOAuth?.(params.context);
}
export async function buildProviderAuthDoctorHintWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderAuthDoctorHintContext;
}) {
return await resolveProviderRuntimePlugin(params)?.buildAuthDoctorHint?.(params.context);
}
export function resolveProviderCacheTtlEligibility(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderCacheTtlEligibilityContext;
}) {
return resolveProviderRuntimePlugin(params)?.isCacheTtlEligible?.(params.context);
}
export function resolveProviderBinaryThinking(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderThinkingPolicyContext;
}) {
return resolveProviderRuntimePlugin(params)?.isBinaryThinking?.(params.context);
}
export function resolveProviderXHighThinking(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderThinkingPolicyContext;
}) {
return resolveProviderRuntimePlugin(params)?.supportsXHighThinking?.(params.context);
}
export function resolveProviderThinkingProfile(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderDefaultThinkingPolicyContext;
}): ProviderThinkingProfile | null | undefined {
const bundledSurface = resolveBundledProviderPolicySurface(params.provider);
if (bundledSurface?.resolveThinkingProfile) {
return bundledSurface.resolveThinkingProfile(params.context) ?? undefined;
}
return resolveProviderRuntimePlugin(params)?.resolveThinkingProfile?.(params.context);
}
export function resolveProviderDefaultThinkingLevel(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderDefaultThinkingPolicyContext;
}) {
return resolveProviderRuntimePlugin(params)?.resolveDefaultThinkingLevel?.(params.context);
}
export function applyProviderConfigDefaultsWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderApplyConfigDefaultsContext;
}) {
const bundledSurface = resolveBundledProviderPolicySurface(params.provider);
if (bundledSurface?.applyConfigDefaults) {
return bundledSurface.applyConfigDefaults(params.context) ?? undefined;
}
return resolveProviderRuntimePlugin(params)?.applyConfigDefaults?.(params.context) ?? undefined;
}
export function resolveProviderModernModelRef(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderModernModelPolicyContext;
}) {
return resolveProviderRuntimePlugin(params)?.isModernModelRef?.(params.context);
}
export function buildProviderMissingAuthMessageWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderBuildMissingAuthMessageContext;
}) {
return (
resolveProviderRuntimePlugin(params)?.buildMissingAuthMessage?.(params.context) ?? undefined
);
}
export function buildProviderUnknownModelHintWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderBuildUnknownModelHintContext;
}) {
return resolveProviderRuntimePlugin(params)?.buildUnknownModelHint?.(params.context) ?? undefined;
}
export function resolveProviderSyntheticAuthWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveSyntheticAuthContext;
modelApi?: string;
}) {
const providerRefs = resolveProviderHookRefs(
params.provider,
params.context.providerConfig,
params.modelApi,
);
const discoveryPluginIds = [
...new Set(
providerRefs.flatMap(
(provider) =>
resolveOwningPluginIdsForProviderRef({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
}) ?? [],
),
),
];
const discoveryProvider = (
discoveryPluginIds.length > 0
? resolvePluginDiscoveryProvidersRuntime({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
onlyPluginIds: discoveryPluginIds,
discoveryEntriesOnly: true,
})
: []
).find((provider) => matchesAnyProviderPluginRef(provider, providerRefs));
if (typeof discoveryProvider?.resolveSyntheticAuth === "function") {
return discoveryProvider.resolveSyntheticAuth(params.context) ?? undefined;
}
const runtimeResolved = resolveProviderRuntimePlugin({
...params,
applyAutoEnable: false,
bundledProviderVitestCompat: false,
})?.resolveSyntheticAuth?.(params.context);
if (runtimeResolved) {
return runtimeResolved;
}
for (const providerRef of providerRefs) {
if (normalizeProviderId(providerRef) === normalizeProviderId(params.provider)) {
continue;
}
const runtimeProviderResolved = resolveProviderRuntimePlugin({
...params,
provider: providerRef,
applyAutoEnable: false,
bundledProviderVitestCompat: false,
})?.resolveSyntheticAuth?.(params.context);
if (runtimeProviderResolved) {
return runtimeProviderResolved;
}
}
if (providerRefs.length === 1) {
return resolvePluginDiscoveryProvidersRuntime({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
})
.find((provider) => matchesAnyProviderPluginRef(provider, providerRefs))
?.resolveSyntheticAuth?.(params.context);
}
return undefined;
}
export function resolveExternalAuthProfilesWithPlugins(params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveExternalAuthProfilesContext;
}): ProviderExternalAuthProfile[] {
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
const env = params.env ?? process.env;
const { manifestRegistry } = loadPluginMetadataSnapshot({
config: params.config ?? {},
workspaceDir,
env,
});
const externalAuthPluginIds = resolveExternalAuthProfileProviderPluginIds({
config: params.config,
workspaceDir,
env,
manifestRegistry,
});
const declaredPluginIds = new Set(externalAuthPluginIds);
const fallbackPluginIds = resolveExternalAuthProfileCompatFallbackPluginIds({
config: params.config,
workspaceDir,
env,
declaredPluginIds,
manifestRegistry,
});
const pluginIds = sortUniqueStrings([...externalAuthPluginIds, ...fallbackPluginIds]);
if (pluginIds.length === 0) {
return [];
}
const matches: ProviderExternalAuthProfile[] = [];
for (const plugin of resolveProviderPluginsForHooks({
...params,
workspaceDir,
env,
onlyPluginIds: pluginIds,
})) {
const profiles =
plugin.resolveExternalAuthProfiles?.(params.context) ??
plugin.resolveExternalOAuthProfiles?.(params.context);
if (!profiles || profiles.length === 0) {
continue;
}
const pluginId = plugin.pluginId ?? plugin.id;
if (!declaredPluginIds.has(pluginId) && !warnedExternalAuthFallbackPluginIds.has(pluginId)) {
warnedExternalAuthFallbackPluginIds.add(pluginId);
log.warn(
`Provider plugin "${sanitizeForLog(pluginId)}" uses external auth hooks without declaring contracts.externalAuthProviders. This compatibility fallback is deprecated and will be removed in a future release.`,
);
}
matches.push(...profiles);
}
return matches;
}
export function resolveExternalOAuthProfilesWithPlugins(params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderResolveExternalOAuthProfilesContext;
}): ProviderExternalAuthProfile[] {
return resolveExternalAuthProfilesWithPlugins(params);
}
export function shouldDeferProviderSyntheticProfileAuthWithPlugin(params: {
provider: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderDeferSyntheticProfileAuthContext;
modelApi?: string;
}) {
const providerRefs = resolveProviderHookRefs(
params.provider,
params.context.providerConfig,
params.modelApi,
);
for (const providerRef of providerRefs) {
const resolved = resolveProviderRuntimePlugin({
...params,
provider: providerRef,
})?.shouldDeferSyntheticProfileAuth?.(params.context);
if (resolved !== undefined) {
return resolved;
}
}
return undefined;
}
export async function augmentModelCatalogWithProviderPlugins(params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
context: ProviderAugmentModelCatalogContext;
}) {
const supplemental = [] as ProviderAugmentModelCatalogContext["entries"];
for (const plugin of resolveProviderPluginsForCatalogHooks(params)) {
const next = await plugin.augmentModelCatalog?.(params.context);
if (!next || next.length === 0) {
continue;
}
supplemental.push(...next);
}
return supplemental;
}