import type { AuthProfileCredential, OAuthCredential } from "../agents/auth-profiles/types.js"; import type { FailoverReason } from "../agents/embedded-agent-helpers/types.js"; import type { ModelCatalogEntry } from "../agents/model-catalog.types.js"; import type { AgentMessage, StreamFn } from "../agents/runtime/index.js"; import type { ProviderSystemPromptContribution } from "../agents/system-prompt-contribution.js"; import type { AnyAgentTool } from "../agents/tools/common.js"; import type { ModelProviderConfig } from "../config/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { ProviderUsageSnapshot } from "../infra/provider-usage.types.js"; import type { PluginTextTransforms } from "./cli-backend.types.js"; import type { ProviderAuthMethod, ProviderDeferSyntheticProfileAuthContext, ProviderModelSelectedContext, ProviderOAuthProfileIdRepair, ProviderPluginWizard, ProviderSystemPromptContributionContext, ProviderTransformSystemPromptContext, } from "./provider-authentication.types.js"; import type { ProviderPluginCatalog, ProviderBuiltInModelSuppressionContext, ProviderBuiltInModelSuppressionResult, ProviderModernModelPolicyContext, ProviderAugmentModelCatalogContext, } from "./provider-catalog.types.js"; import type { ProviderApplyConfigDefaultsContext, ProviderNormalizeConfigContext, ProviderResolveConfigApiKeyContext, } from "./provider-config-context.types.js"; import type { ProviderExternalAuthProfile, ProviderResolveExternalAuthProfilesContext, ProviderResolveSyntheticAuthContext, ProviderSyntheticAuthResult, } from "./provider-external-auth.types.js"; import type { ProviderReasoningOutputMode, ProviderReplayPolicy, ProviderReplayPolicyContext, ProviderSanitizeReplayHistoryContext, ProviderValidateReplayTurnsContext, ProviderNormalizeToolSchemasContext, ProviderToolSchemaDiagnostic, ProviderReasoningOutputModeContext, } from "./provider-replay.types.js"; import type { ProviderRuntimeModel } from "./provider-runtime-model.types.js"; import type { ProviderResolveDynamicModelContext, ProviderPrepareDynamicModelContext, ProviderPreferRuntimeResolvedModelContext, ProviderNormalizeResolvedModelContext, ProviderNormalizeModelIdContext, ProviderNormalizeTransportContext, ProviderPrepareRuntimeAuthContext, ProviderPreparedRuntimeAuth, ProviderResolveUsageAuthContext, ProviderResolvedUsageAuth, ProviderFetchUsageSnapshotContext, ProviderAuthDoctorHintContext, ProviderPrepareExtraParamsContext, ProviderExtraParamsForTransportContext, ProviderExtraParamsForTransportResult, ProviderResolvePromptOverlayContext, ProviderFollowupFallbackRouteContext, ProviderFollowupFallbackRouteResult, ProviderResolveAuthProfileIdContext, } from "./provider-runtime.types.js"; import type { ProviderDefaultThinkingPolicyContext, ProviderThinkingProfile, } from "./provider-thinking.types.js"; import type { ProviderCreateStreamFnContext, ProviderWrapStreamFnContext, ProviderTransportTurnState, ProviderResolveTransportTurnStateContext, ProviderWebSocketSessionPolicy, ProviderResolveWebSocketSessionPolicyContext, ProviderFailoverErrorContext, PluginEmbeddingProvider, ProviderCreateEmbeddingProviderContext, ProviderCacheTtlEligibilityContext, ProviderBuildMissingAuthMessageContext, ProviderBuildUnknownModelHintContext, } from "./provider-transport.types.js"; export type ProviderPlugin = { id: string; pluginId?: string; label: string; docsPath?: string; aliases?: string[]; /** * Internal-only aliases used for runtime/config hook lookup. * * Unlike `aliases`, these values are not treated as user-facing provider ids * for auth/setup surfaces. Use them for legacy config keys or compat-only * hook routing. */ hookAliases?: string[]; /** * Provider-related env vars shown in setup/search/help surfaces. * * Keep entries in preferred display order. This can include direct auth env * vars or setup inputs such as OAuth client id/secret vars. */ envVars?: string[]; auth: ProviderAuthMethod[]; /** * Legacy text-provider catalog hook. * * @deprecated New catalog/control-plane surfaces should use * `api.registerModelCatalogProvider`. This hook remains the text runtime * source until the unified loader fully replaces it. * Returns provider config/model definitions that merge into models.providers. */ catalog?: ProviderPluginCatalog; /** * Legacy offline text-provider catalog hook for display-only surfaces. * * @deprecated New static rows should be registered with * `api.registerModelCatalogProvider`. * * Unlike `catalog`, this hook must not perform network I/O or require real * credentials. Use it for bundled/static rows that can be shown before auth is * configured. */ staticCatalog?: ProviderPluginCatalog; /** * Show catalog row labels as the literal `/` * composition instead of the canonical (deduped) key. * * `modelKey` strips a duplicate `/` prefix so storage and * lookups stay stable. This flag only changes the picker label — the * option value and persisted config remain canonical. * * Set when the leading `/` segment in the native model id is * a meaningful vendor namespace (e.g. NVIDIA's `nvidia/nemotron-...` * alongside `moonshotai/kimi-k2.5`). */ preserveLiteralProviderPrefix?: boolean; /** * Sync runtime fallback for model ids not present in the local catalog. * * Hook order: * 1. discovered/static model lookup * 2. plugin `resolveDynamicModel` * 3. core fallback heuristics * 4. generic provider-config fallback * * Keep this hook cheap and deterministic. If you need network I/O first, use * `prepareDynamicModel` to prime state for the async retry path. */ resolveDynamicModel?: ( ctx: ProviderResolveDynamicModelContext, ) => ProviderRuntimeModel | null | undefined; /** * Optional async prefetch for dynamic model resolution. * * OpenClaw calls this only from async model resolution paths. After it * completes, `resolveDynamicModel` is called again. */ prepareDynamicModel?: (ctx: ProviderPrepareDynamicModelContext) => Promise; /** * Lets a provider plugin opt exact configured models into a runtime * metadata comparison pass before the embedded runner returns the explicit * entry unchanged. */ preferRuntimeResolvedModel?: (ctx: ProviderPreferRuntimeResolvedModelContext) => boolean; /** * Provider-owned transport normalization. * * Use this to rewrite a resolved model without forking the generic runner: * swap API ids, update base URLs, or adjust compat flags for a provider's * transport quirks. */ normalizeResolvedModel?: ( ctx: ProviderNormalizeResolvedModelContext, ) => ProviderRuntimeModel | null | undefined; /** * Provider-owned model-id normalization. * * Runs before model lookup/canonicalization. Use this for alias cleanup such * as provider-owned preview/legacy model ids. */ normalizeModelId?: (ctx: ProviderNormalizeModelIdContext) => string | null | undefined; /** * Provider-owned transport-family normalization before generic model * assembly. * * Use this for API/baseUrl cleanup that may apply to custom provider ids * which still target the provider's transport family. */ normalizeTransport?: ( ctx: ProviderNormalizeTransportContext, ) => { api?: string | null; baseUrl?: string } | null | undefined; /** * Provider-owned config normalization for `models.providers.`. * * Use this for provider-specific baseUrl/model-id cleanup that should stay * with the plugin rather than in core config-policy tables. */ normalizeConfig?: (ctx: ProviderNormalizeConfigContext) => ModelProviderConfig | null | undefined; /** * Provider-owned final native-streaming compat pass for config providers. * * Use this when a provider opts specific native base URLs into * `supportsUsageInStreaming` or similar transport compatibility flags. */ applyNativeStreamingUsageCompat?: ( ctx: ProviderNormalizeConfigContext, ) => ModelProviderConfig | null | undefined; /** * Provider-owned config apiKey/env marker resolution. * * Use this when a provider resolves auth from env vars such as AWS/GCP * markers rather than a normal API-key env var. */ resolveConfigApiKey?: (ctx: ProviderResolveConfigApiKeyContext) => string | null | undefined; /** * Provider-owned replay/compaction policy override. * * Use this when transcript replay or compaction should follow provider-owned * rules that are more expressive than the static `capabilities` bag. */ buildReplayPolicy?: (ctx: ProviderReplayPolicyContext) => ProviderReplayPolicy | null | undefined; /** * Provider-owned replay-history sanitization. * * Runs after OpenClaw performs generic transcript cleanup. Use this for * provider-specific replay rewrites that should stay with the provider * plugin rather than in shared core compaction helpers. */ sanitizeReplayHistory?: ( ctx: ProviderSanitizeReplayHistoryContext, ) => Promise | AgentMessage[] | null | undefined; /** * Provider-owned final replay-turn validation. * * Use this when provider transports need stricter replay-time validation or * turn reshaping after generic sanitation. Returning a non-null value * replaces the built-in replay validators rather than composing with them. */ validateReplayTurns?: ( ctx: ProviderValidateReplayTurnsContext, ) => Promise | AgentMessage[] | null | undefined; /** * Provider-owned tool-schema normalization. * * Use this for transport-family schema cleanup before OpenClaw registers * tools with the embedded runner. */ normalizeToolSchemas?: ( ctx: ProviderNormalizeToolSchemasContext, ) => AnyAgentTool[] | null | undefined; /** * Provider-owned tool-schema diagnostics after normalization. * * Use this when a provider wants to surface transport-specific schema * warnings without teaching core about provider-specific keyword rules. */ inspectToolSchemas?: ( ctx: ProviderNormalizeToolSchemasContext, ) => ProviderToolSchemaDiagnostic[] | null | undefined; /** * Provider-owned reasoning output mode. * * Use this when a provider requires tagged reasoning/final output instead of * native structured reasoning fields. */ resolveReasoningOutputMode?: ( ctx: ProviderReasoningOutputModeContext, ) => ProviderReasoningOutputMode | null | undefined; /** * Provider-owned extra-param normalization before generic stream option * wrapping. * * Typical uses: set provider-default `transport`, map provider-specific * config aliases, or inject extra request metadata sourced from * `agents.defaults.models./.params`. */ prepareExtraParams?: ( ctx: ProviderPrepareExtraParamsContext, ) => Record | null | undefined; /** * Provider-owned request params after transport/model resolution. * * Use this for transport-family request knobs that should be keyed by the * resolved model API/transport rather than a hardcoded core allowlist. */ extraParamsForTransport?: ( ctx: ProviderExtraParamsForTransportContext, ) => ProviderExtraParamsForTransportResult | null | undefined; /** * Provider-owned transport factory. * * Use this when the provider needs a fully custom StreamFn instead of a * wrapper around the normal `streamSimple` path. */ createStreamFn?: (ctx: ProviderCreateStreamFnContext) => StreamFn | null | undefined; /** * Provider-owned stream wrapper applied after generic OpenClaw wrappers. * * Typical uses: provider attribution headers, request-body rewrites, or * provider-specific compat payload patches that do not justify a separate * transport implementation. */ wrapStreamFn?: (ctx: ProviderWrapStreamFnContext) => StreamFn | null | undefined; /** * Provider-owned wrapper for direct `completeSimple` callers. * * Opt in only when the provider must enforce the same wire contract outside * the embedded agent runtime. */ wrapSimpleCompletionStreamFn?: (ctx: ProviderWrapStreamFnContext) => StreamFn | null | undefined; /** * Provider-owned native transport turn identity. * * Use this when a provider wants generic transports to attach provider-native * request headers or metadata on each turn without hardcoding vendor logic in * core. */ resolveTransportTurnState?: ( ctx: ProviderResolveTransportTurnStateContext, ) => ProviderTransportTurnState | null | undefined; /** * Provider-owned WebSocket session policy. * * Use this when a provider wants generic WebSocket transports to attach * native session headers or tune the session-scoped cool-down before HTTP * fallback. */ resolveWebSocketSessionPolicy?: ( ctx: ProviderResolveWebSocketSessionPolicyContext, ) => ProviderWebSocketSessionPolicy | null | undefined; /** * Provider-owned embedding provider factory. * * Use this when memory embedding behavior belongs with the provider plugin * rather than the core embedding switchboard. */ createEmbeddingProvider?: ( ctx: ProviderCreateEmbeddingProviderContext, ) => | Promise | PluginEmbeddingProvider | null | undefined; /** * Runtime auth exchange hook. * * Called after OpenClaw resolves the raw configured credential but before the * runner stores it in runtime auth storage. This lets plugins exchange a * source credential (for example a GitHub token) into a short-lived runtime * token plus optional base URL override. */ prepareRuntimeAuth?: ( ctx: ProviderPrepareRuntimeAuthContext, ) => Promise; /** * Usage/billing auth resolution hook. * * Called by provider-usage surfaces (`/usage`, status snapshots, reporting). * Use this when a provider's usage endpoint needs provider-owned token * extraction, blob parsing, or alias handling. */ resolveUsageAuth?: ( ctx: ProviderResolveUsageAuthContext, ) => | Promise | ProviderResolvedUsageAuth | null | undefined; /** * Usage/quota snapshot fetch hook. * * Called after `resolveUsageAuth` by `/usage` and related reporting surfaces. * Use this when the provider's usage endpoint or payload shape is * provider-specific and you want that logic to live with the provider plugin * instead of the core switchboard. */ fetchUsageSnapshot?: ( ctx: ProviderFetchUsageSnapshotContext, ) => Promise | ProviderUsageSnapshot | null | undefined; /** * Provider-owned failover context-overflow matcher. * * Return true when the provider recognizes the raw error as a context-window * overflow shape that generic heuristics would miss. */ matchesContextOverflowError?: (ctx: ProviderFailoverErrorContext) => boolean | undefined; /** * Provider-owned failover error classification. * * Return a failover reason when the provider recognizes a provider-specific * raw error shape. Return undefined to fall back to generic classification. */ classifyFailoverReason?: (ctx: ProviderFailoverErrorContext) => FailoverReason | null | undefined; /** * Provider-owned cache TTL eligibility. * * Use this when a proxy provider supports Anthropic-style prompt caching for * only a subset of upstream models. */ isCacheTtlEligible?: (ctx: ProviderCacheTtlEligibilityContext) => boolean | undefined; /** * Provider-owned missing-auth message override. * * Return a custom message when the provider wants a more specific recovery * hint than OpenClaw's generic auth-store guidance. */ buildMissingAuthMessage?: ( ctx: ProviderBuildMissingAuthMessageContext, ) => string | null | undefined; /** * Provider-owned unknown-model hint override. * * Return a suffix when the provider wants a more specific recovery hint than * OpenClaw's generic `Unknown model` error after catalog/runtime lookup * fails. */ buildUnknownModelHint?: (ctx: ProviderBuildUnknownModelHintContext) => string | null | undefined; /** * Provider-owned built-in model suppression. * * Return `{ suppress: true }` to hide a stale upstream row. Include * `errorMessage` when OpenClaw should surface a provider-specific hint for * direct model resolution failures. * * @deprecated Use manifest `modelCatalog.suppressions`. Runtime suppression * hooks are no longer called by model resolution. */ suppressBuiltInModel?: ( ctx: ProviderBuiltInModelSuppressionContext, ) => ProviderBuiltInModelSuppressionResult | null | undefined; /** * Provider-owned final catalog augmentation. * * @deprecated Use `api.registerModelCatalogProvider` for supplemental catalog * rows. This hook is kept only for existing text-provider runtime * compatibility during the migration window. * * Return extra rows to append to the final catalog after discovery/config * merging. OpenClaw deduplicates by `provider/id`, so plugins only need to * describe the desired supplemental rows. */ augmentModelCatalog?: ( ctx: ProviderAugmentModelCatalogContext, ) => | Array | ReadonlyArray | Promise | ReadonlyArray | null | undefined> | null | undefined; /** * Provider-owned thinking level profile. * * Prefer this over the individual thinking capability hooks when a provider * or model exposes a custom set of thinking levels. OpenClaw stores the * canonical `id`, shows `label` when provided, and downgrades stale stored * values by profile rank. */ resolveThinkingProfile?: ( ctx: ProviderDefaultThinkingPolicyContext, ) => ProviderThinkingProfile | null | undefined; /** * Provider-owned system-prompt contribution. * * Use this when a provider/model family needs cache-aware prompt tuning * without replacing the full OpenClaw-owned system prompt. */ resolveSystemPromptContribution?: ( ctx: ProviderSystemPromptContributionContext, ) => ProviderSystemPromptContribution | null | undefined; /** * Provider-owned GPT/model prompt overlay seam. * * Runs after OpenClaw's built-in overlay is resolved and before the * provider's regular system-prompt contribution is merged. */ resolvePromptOverlay?: ( ctx: ProviderResolvePromptOverlayContext, ) => ProviderSystemPromptContribution | null | undefined; /** * Provider-owned fallback route override for model/profile failure handling. * * Return undefined/null to keep OpenClaw's default fallback policy. */ followupFallbackRoute?: ( ctx: ProviderFollowupFallbackRouteContext, ) => ProviderFollowupFallbackRouteResult | null | undefined; /** * Provider-owned auth profile resolver. * * Return a profile id from the supplied order to prefer it for this attempt; * invalid or missing ids are ignored by core. */ resolveAuthProfileId?: (ctx: ProviderResolveAuthProfileIdContext) => string | null | undefined; /** * Provider-owned final system-prompt transform. * * Use this sparingly when a provider transport needs small compatibility * rewrites after OpenClaw has assembled the complete prompt. Return * `undefined`/`null` to leave the prompt unchanged. */ transformSystemPrompt?: (ctx: ProviderTransformSystemPromptContext) => string | null | undefined; /** * Provider-owned bidirectional text replacements. * * `input` applies to system prompts and text message content before transport. * `output` applies to assistant text deltas/final text before OpenClaw handles * its own control markers or channel delivery. */ textTransforms?: PluginTextTransforms; /** * Provider-owned global config defaults. * * Use this when config materialization needs provider-specific defaults that * depend on auth mode, env, or provider model-family semantics. */ applyConfigDefaults?: ( ctx: ProviderApplyConfigDefaultsContext, ) => OpenClawConfig | null | undefined; /** * Provider-owned "modern model" matcher used by live profile/smoke filters. * * Return true when the given provider/model ref should be treated as a * preferred modern model candidate. */ isModernModelRef?: (ctx: ProviderModernModelPolicyContext) => boolean | undefined; wizard?: ProviderPluginWizard; /** * Provider-owned auth-profile API-key formatter. * * OpenClaw uses this when a stored auth profile is already valid and needs to * be converted into the runtime `apiKey` string expected by the provider. Use * this for providers whose auth profile stores extra metadata alongside the * bearer token (for example Gemini CLI's `{ token, projectId }` payload). */ formatApiKey?: (cred: AuthProfileCredential) => string; /** * Legacy auth-profile ids that should be retired by `openclaw doctor`. * * Use this when a provider plugin replaces an older core-managed profile id * and wants cleanup/migration messaging to live with the provider instead of * in hardcoded doctor tables. */ deprecatedProfileIds?: string[]; /** * Legacy OAuth profile-id migrations that `openclaw doctor` should offer. * * Use this when a provider moved from a legacy default OAuth profile id to a * newer identity-based id and wants doctor to own the config rewrite without * another core-specific migration branch. */ oauthProfileIdRepairs?: ProviderOAuthProfileIdRepair[]; /** * Provider-owned OAuth refresh. * * OpenClaw calls this before falling back to the shared `shared model runtime` OAuth * refreshers. Use it when the provider has a custom refresh endpoint, or when * the provider needs custom refresh-failure behavior that should stay out of * core auth-profile code. */ refreshOAuth?: (cred: OAuthCredential) => Promise; /** * Provider-owned auth-doctor hint. * * Return a multiline repair hint when OAuth refresh fails and the provider * wants to steer users toward a specific auth-profile migration or recovery * path. Return nothing to keep OpenClaw's generic error text. */ buildAuthDoctorHint?: ( ctx: ProviderAuthDoctorHintContext, ) => string | Promise | null | undefined; /** * Provider-owned config-backed auth resolution. * * Providers own any provider-specific fallback secret rules here so core * auth/discovery code can stay generic and avoid parsing provider-private * config layouts. * * The returned `apiKey` may be: * - a real credential from the active runtime snapshot, suitable for runtime use * - a non-secret marker (for example a managed SecretRef marker), suitable only * for discovery/bootstrap callers * * Runtime callers must not treat non-secret markers as runnable credentials; * they should retry against the active runtime snapshot when available. * * This hook is the canonical seam for provider-specific fallback auth * derived from plugin/private config. It may return: * - a runnable literal credential for runtime callers * - a non-secret marker for managed-secret source config, which is still useful * for discovery/bootstrap callers * * Runtime callers must not treat non-secret markers as runnable credentials; * they should retry against the active runtime snapshot when available. * * Use this when the provider can operate without a real secret for certain * configured local/self-hosted cases and wants auth resolution to treat that * config as available. */ resolveSyntheticAuth?: ( ctx: ProviderResolveSyntheticAuthContext, ) => ProviderSyntheticAuthResult | null | undefined; /** * Provider-owned external auth profile discovery. * * Use this when credentials are managed by an external tool and should be visible * to runtime auth resolution without being written back into `auth-profiles.json` * by core. */ resolveExternalAuthProfiles?: ( ctx: ProviderResolveExternalAuthProfilesContext, ) => | Array | ReadonlyArray | null | undefined; /** * Provider-owned precedence rule for stored synthetic auth profiles. * * Return true when a stored profile API key is only a provider-owned * synthetic placeholder and should yield to env/config-backed auth before * OpenClaw falls back to that stored profile. */ shouldDeferSyntheticProfileAuth?: ( ctx: ProviderDeferSyntheticProfileAuthContext, ) => boolean | undefined; onModelSelected?: (ctx: ProviderModelSelectedContext) => Promise; };